如何configuration一个bash shell脚本慢启动?

我的bashshell需要3-4秒才能启动,而如果我用--norc启动,它会立即运行。

我通过手动插入return语句并寻求速度改进来开始“分析” /etc/bash.bashrc~/.bashrc ,但这不是一个定量过程,效率也不高。

我怎样才能分析我的bash脚本,看看哪些命令最需要启动?

如果你有GNU date (或者可以输出纳秒的另一个版本),可以在/etc/bash.bashrc (或者任何想在任何Bash脚本中开始跟踪的地方)的开头执行:

 PS4='+ $(date "+%s.%N")\011 ' exec 3>&2 2>/tmp/bashstart.$$.log set -x 

 set +x exec 2>&3 3>&- 

~/.bashrc (或者你想跟踪停止的任何Bash脚本部分的末尾)。

您应该在/tmp/bashstart.PID.log中获取跟踪日志,其中显示了每个执行的命令的seconds.nanoseconds时间戳。 从一次到下一次的差异是干预步骤花费的时间量。

在缩小范围时,可以稍后移动set -xset +x (或者select性地包含几个感兴趣的部分)。

分析bash (4答案)

编辑:2016年3月添加script方法

阅读这个,因为分析是一个重要的步骤,我已经做了一些testing和研究这个整个SO问题,并已发布答案。

有4个以上的答案:

  • 第一个是基于@ DennisWilliamson的想法,但是资源消耗less得多
  • 第二个是我自己的(在此之前;)
  • 第三是基于@fgm的答案,但更准确。
  • 最后一个使用scriptscripreplay计时文件

  • 最后,还有一点比较。

使用set -xdate但有限的叉子

拿@ DennisWilliamson的想法来看,但是用下面的语法,将会有一个初始分支到3个命令:

 exec 3>&2 2> >(tee /tmp/sample-time.$$.log | sed -u 's/^.*$/now/' | date -f - +%s.%N >/tmp/sample-time.$$.tim) set -x 

这样做只会运行一次。 有一个快速演示/testing,以显示它是如何工作的:

 for i in {1..4};do echo now;sleep .05;done| date -f - +%N 

示例脚本:

 #!/bin/bash exec 3>&2 2> >( tee /tmp/sample-$$.log | sed -u 's/^.*$/now/' | date -f - +%s.%N >/tmp/sample-$$.tim) set -x for ((i=3;i--;));do sleep .1;done for ((i=2;i--;)) do tar -cf /tmp/test.tar -C / bin gzip /tmp/test.tar rm /tmp/test.tar.gz done set +x exec 2>&3 3>&- 

通过运行这个脚本,你创build了两个文件: /tmp/sample-XXXX.log/tmp/sample-XXXX.tim (其中XXXX是正在运行的脚本的进程ID)。

你可以通过使用paste来显示它们:

 paste tmp/sample-XXXX.{tim,log} 

或者你甚至可以计算diff时间:

 paste <( while read tim ;do crt=000000000$((${tim//.}-10#0$last)) printf "%12.9f\n" ${crt:0:${#crt}-9}.${crt:${#crt}-9} last=${tim//.} done < sample-time.24804.tim ) sample-time.24804.log 1388487534.391309713 + (( i=3 )) 0.000080807 + (( i-- )) 0.000008312 + sleep .1 0.101304843 + (( 1 )) 0.000032616 + (( i-- )) 0.000007124 + sleep .1 0.101251684 + (( 1 )) 0.000033036 + (( i-- )) 0.000007054 + sleep .1 0.104013813 + (( 1 )) 0.000026959 + (( i-- )) 0.000006915 + (( i=2 )) 0.000006635 + (( i-- )) 0.000006844 + tar -cf /tmp/test.tar -C / bin 0.022655107 + gzip /tmp/test.tar 0.637042668 + rm /tmp/test.tar.gz 0.000823649 + (( 1 )) 0.000011314 + (( i-- )) 0.000006915 + tar -cf /tmp/test.tar -C / bin 0.016084482 + gzip /tmp/test.tar 0.627798263 + rm /tmp/test.tar.gz 0.001294946 + (( 1 )) 0.000023187 + (( i-- )) 0.000006845 + set +x 

或两列:

 paste <( while read tim ;do [ -z "$last" ] && last=${tim//.} && first=${tim//.} crt=000000000$((${tim//.}-10#0$last)) ctot=000000000$((${tim//.}-10#0$first)) printf "%12.9f %12.9f\n" ${crt:0:${#crt}-9}.${crt:${#crt}-9} \ ${ctot:0:${#ctot}-9}.${ctot:${#ctot}-9} last=${tim//.} done < sample-time.24804.tim ) sample-time.24804.log 

可能呈现:

  0.000000000 0.000000000 + (( i=3 )) 0.000080807 0.000080807 + (( i-- )) 0.000008312 0.000089119 + sleep .1 0.101304843 0.101393962 + (( 1 )) 0.000032616 0.101426578 + (( i-- )) 0.000007124 0.101433702 + sleep .1 0.101251684 0.202685386 + (( 1 )) 0.000033036 0.202718422 + (( i-- )) 0.000007054 0.202725476 + sleep .1 0.104013813 0.306739289 + (( 1 )) 0.000026959 0.306766248 + (( i-- )) 0.000006915 0.306773163 + (( i=2 )) 0.000006635 0.306779798 + (( i-- )) 0.000006844 0.306786642 + tar -cf /tmp/test.tar -C / bin 0.022655107 0.329441749 + gzip /tmp/test.tar 0.637042668 0.966484417 + rm /tmp/test.tar.gz 0.000823649 0.967308066 + (( 1 )) 0.000011314 0.967319380 + (( i-- )) 0.000006915 0.967326295 + tar -cf /tmp/test.tar -C / bin 0.016084482 0.983410777 + gzip /tmp/test.tar 0.627798263 1.611209040 + rm /tmp/test.tar.gz 0.001294946 1.612503986 + (( 1 )) 0.000023187 1.612527173 + (( i-- )) 0.000006845 1.612534018 + set +x 

最近的 GNU / Linux内核上使用trap debug/proc/timer_list没有 分叉

GNU / Linux最新的内核下,你可以find一个名为timer_list/proc文件:

 grep 'now at\|offset' /proc/timer_list now at 5461935212966259 nsecs .offset: 0 nsecs .offset: 1383718821564493249 nsecs .offset: 0 nsecs 

当前时间是总和5461935212966259 + 1383718821564493249 ,但以纳秒为单位。

所以为了计算经过的时间 ,不需要知道偏移量。

对于这样的工作,我写了elap.bash(V2) ,它来自以下语法:

 source elap.bash-v2 

要么

 . elap.bash-v2 init 

(请参阅完整语法的注释)

所以你可以简单地在脚本的顶部添加这一行:

 . elap.bash-v2 trap2 

小样本:

 #!/bin/bash . elap.bash-v2 trap for ((i=3;i--;));do sleep .1;done elapCalc2 elapShowTotal \\e[1mfirst total\\e[0m for ((i=2;i--;)) do tar -cf /tmp/test.tar -C / bin gzip /tmp/test.tar rm /tmp/test.tar.gz done trap -- debug elapTotal \\e[1mtotal time\\e[0m 

在我的主机上渲染:

  0.000947481 Starting 0.000796900 ((i=3)) 0.000696956 ((i--)) 0.101969242 sleep .1 0.000812478 ((1)) 0.000755067 ((i--)) 0.103693305 sleep .1 0.000730482 ((1)) 0.000660360 ((i--)) 0.103565001 sleep .1 0.000719516 ((1)) 0.000671325 ((i--)) 0.000754856 elapCalc2 0.316018113 first total 0.000754787 elapShowTotal \e[1mfirst total\e[0m 0.000711275 ((i=2)) 0.000683408 ((i--)) 0.075673816 tar -cf /tmp/test.tar -C / bin 0.596389329 gzip /tmp/test.tar 0.006565188 rm /tmp/test.tar.gz 0.000830217 ((1)) 0.000759466 ((i--)) 0.024783966 tar -cf /tmp/test.tar -C / bin 0.604119903 gzip /tmp/test.tar 0.005172940 rm /tmp/test.tar.gz 0.000952299 ((1)) 0.000827421 ((i--)) 1.635788924 total time 1.636657204 EXIT 

使用trap2而不是trap作为源命令的参数:

 #!/bin/bash . elap.bash-v2 trap2 ... 

将呈现两列最后的命令和总数

  0.000894541 0.000894541 Starting 0.001306122 0.002200663 ((i=3)) 0.001929397 0.004130060 ((i--)) 0.103035812 0.107165872 sleep .1 0.000875613 0.108041485 ((1)) 0.000813872 0.108855357 ((i--)) 0.104954517 0.213809874 sleep .1 0.000900617 0.214710491 ((1)) 0.000842159 0.215552650 ((i--)) 0.104846890 0.320399540 sleep .1 0.000899082 0.321298622 ((1)) 0.000811708 0.322110330 ((i--)) 0.000879455 0.322989785 elapCalc2 0.322989785 first total 0.000906692 0.323896477 elapShowTotal \e[1mfirst total\e[0m 0.000820089 0.324716566 ((i=2)) 0.000773782 0.325490348 ((i--)) 0.024752613 0.350242961 tar -cf /tmp/test.tar -C / bin 0.596199363 0.946442324 gzip /tmp/test.tar 0.003007128 0.949449452 rm /tmp/test.tar.gz 0.000791452 0.950240904 ((1)) 0.000779371 0.951020275 ((i--)) 0.030519702 0.981539977 tar -cf /tmp/test.tar -C / bin 0.584155405 1.565695382 gzip /tmp/test.tar 0.003058674 1.568754056 rm /tmp/test.tar.gz 0.000955093 1.569709149 ((1)) 0.000919964 1.570629113 ((i--)) 1.571516599 total time 0.001723708 1.572352821 EXIT 

使用strace

是的, strace可以完成这项工作:

 strace -q -f -s 10 -ttt sample-script 2>sample-script-strace.log 

但是可以做很多东西!

 wc sample-script-strace.log 6925 57637 586518 sample-script-strace.log 

使用更多受限命令:

 strace -f -s 10 -ttt -eopen,access,read,write ./sample-script 2>sample-script-strace.log 

将转储ligther日志:

  4519 36695 374453 sample-script-strace.log 

根据您要search的内容,您可能会受到更多的限制:

  strace -f -s 10 -ttt -eaccess,open ./sample-script 2>&1 | wc 189 1451 13682 

读它们会有点困难:

 { read -a first first=${first//.} last=$first while read tim line;do crt=000000000$((${tim//.}-last)) ctot=000000000$((${tim//.}-first)) printf "%9.6f %9.6f %s\n" ${crt:0:${#crt}-6}.${crt:${#crt}-6} \ ${ctot:0:${#ctot}-6}.${ctot:${#ctot}-6} "$line" last=${tim//.} done } < <( sed </tmp/sample-script.strace -e ' s/^ *//; s/^\[[^]]*\] *//; /^[0-9]\{4\}/!d ') 0.000110 0.000110 open("/lib/x86_64-linux-gnu/libtinfo.so.5", O_RDONLY) = 4 0.000132 0.000242 open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY) = 4 0.000121 0.000363 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 4 0.000462 0.000825 open("/dev/tty", O_RDWR|O_NONBLOCK) = 4 0.000147 0.000972 open("/usr/lib/locale/locale-archive", O_RDONLY) = 4 ... 0.000793 1.551331 open("/etc/ld.so.cache", O_RDONLY) = 4 0.000127 1.551458 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 4 0.000545 1.552003 open("/usr/lib/locale/locale-archive", O_RDONLY) = 4 0.000439 1.552442 --- SIGCHLD (Child exited) @ 0 (0) --- 

原来的bash脚本不太容易遵循这个…

使用scriptscriptreplay计时文件

作为BSD Utils的一部分, script (和scriptreplay )是一个非常古老的工具,可以用来分析bash,占用空间非常小。

 script -t script.log 2>script.tim -c 'bash -x -c " for ((i=3;i--;));do sleep .1;done for ((i=2;i--;)) ;do tar -cf /tmp/test.tar -C / bin gzip /tmp/test.tar rm /tmp/test.tar.gz done "' 

会产生:

 Script started on Fri Mar 25 08:29:37 2016 + (( i=3 )) + (( i-- )) + sleep .1 + (( 1 )) + (( i-- )) + sleep .1 + (( 1 )) + (( i-- )) + sleep .1 + (( 1 )) + (( i-- )) + (( i=2 )) + (( i-- )) + tar -cf /tmp/test.tar -C / bin + gzip /tmp/test.tar + rm /tmp/test.tar.gz + (( 1 )) + (( i-- )) + tar -cf /tmp/test.tar -C / bin + gzip /tmp/test.tar + rm /tmp/test.tar.gz + (( 1 )) + (( i-- )) Script done on Fri Mar 25 08:29:39 2016 

并生成两个文件:

 ls -l script.* -rw-r--r-- 1 user user 450 Mar 25 08:29 script.log -rw-r--r-- 1 user user 177 Mar 25 08:29 script.tim 

文件script.log包含所有的跟踪和script.tim时间文件

 head -n 4 script.* ==> script.log <== Script started on Fri Mar 25 08:29:37 2016 + (( i=3 )) + (( i-- )) + sleep .1 ==> script.tim <== 0.435331 11 0.000033 2 0.000024 11 0.000010 2 

您可以在日志文件的第一行和最后一行看到总时间执行情况,并且/或者通过计时文件中的累计时间:

 head -n1 script.log ;tail -n1 script.log Script started on Fri Mar 25 08:29:37 2016 Script done on Fri Mar 25 08:29:39 2016 sed < script.tim 's/ .*$//;H;${x;s/\n/+/g;s/^\+//;p};d' | bc -l 2.249755 

在计时文件中,第二个值是相应日志文件中的下一个字节数。 这让你以一个加速因子select性地重播日志文件的能力:

 scriptreplay script.{tim,log} 

要么

 scriptreplay script.{tim,log} 5 

要么

  scriptreplay script.{tim,log} .2 

同时显示时间和命令也有点复杂:

 exec 4<script.log read -u 4 line echo $line ;while read tim char;do read -u 4 -N $char -r -s line echo $tim $line done < script.tim && while read -u 4 line;do echo $line done;exec 4<&- Script started on Fri Mar 25 08:28:51 2016 0.558012 + (( i=3 )) 0.000053 0.000176 + (( i-- )) 0.000015 0.000059 + sleep .1 0.000015 + sleep .1) + (( 1 )) + sleep .1) + (( 1 )) + tar -cf /tmp/test.tar -C / bin 0.035024 + gzip /tmp/test.tar 0.793846 + rm /tmp/test.tar.gz + tar -cf /tmp/test.tar -C / bin 0.024971 + gzip /tmp/test.tar 0.729062 + rm /tmp/test.tar.gz + (( i-- )) + (( 1 )) Script done on Fri Mar 25 08:28:53 2016 

testing和结论

为了进行testing,我已经在bash复杂hello world下载了第二个示例,这个脚本大约需要0.72秒才能在我的主机上完成。

我已经添加了脚本的顶部之一:

  • 通过elap.bash函数

     #!/bin/bash source elap.bash-v2 trap2 eval "BUNCHS=(" $(perl <<EOF | gunzip ... 
  • 通过set -xPS4

     #!/bin/bash PS4='+ $(date "+%s.%N")\011 ' exec 3>&2 2>/tmp/bashstart.$$.log set -x eval "BUNCHS=(" $(perl <<EOF | gunzip ... 
  • 通过set -x初始叉到长执行命令

     #!/bin/bash exec 3>&2 2> >(tee /tmp/sample-time.$$.log | sed -u 's/^.*$/now/' | date -f - +%s.%N >/tmp/sample-time.$$.tim) set -x eval "BUNCHS=(" $(perl <<EOF | gunzip 
  • 通过script (并set +x

     script -t helloworld.log 2>helloworld.tim -c ' bash -x complex_helloworld-2.sh' >/dev/null 

并比较执行时间(在我的主机上):

  • 直接 0.72秒
  • elap.bash 13.18秒
  • 设置+date@ PS4 54.61秒
  • 设置+ 1叉 1.45秒
  • 脚本和计时文件 2.19秒
  • strace 4.47秒

输出

  • 通过elap.bash函数

      0.000950277 0.000950277 Starting 0.007618964 0.008569241 eval "BUNCHS=(" $(perl <<EOF | gunzi 0.005259953 0.013829194 BUNCHS=("2411 1115 -13 15 33 -3 15 1 0.010945070 0.024774264 MKey="V922/G/,2:" 0.001050990 0.025825254 export RotString="" 0.004724348 0.030549602 initRotString 0.001322184 0.031871786 for bunch in "${BUNCHS[@]}" 0.000768893 0.032640679 out="" 0.001008242 0.033648921 bunchArray=($bunch) 0.000741095 0.034390016 ((k=0)) 
  • 通过set -xPS4

     ++ 1388598366.536099290 perl ++ 1388598366.536169132 gunzip + 1388598366.552794757 eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3 15 1 ++ 1388598366.555001983 BUNCHS=("2411 1115 -13 15 33 -3 15 13111 -6 1 + 1388598366.557551018 MKey=V922/G/,2: + 1388598366.558316839 export RotString= + 1388598366.559083848 RotString= + 1388598366.560165147 initRotString + 1388598366.560942633 local _i _char + 1388598366.561706988 RotString= 
  • 通过set -x初始叉到长执行命令 (和我的第二个paste示例脚本)

      0.000000000 0.000000000 ++ perl 0.008141159 0.008141159 ++ gunzip 0.000007822 0.008148981 + eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3 0.000006216 0.008155197 ++ BUNCHS=("2411 1115 -13 15 33 -3 15 13111 0.000006216 0.008161413 + MKey=V922/G/,2: 0.000006076 0.008167489 + export RotString= 0.000006007 0.008173496 + RotString= 0.000006006 0.008179502 + initRotString 0.000005937 0.008185439 + local _i _char 0.000006006 0.008191445 + RotString= 
  • 通过strace

      0.000213 0.000213 brk(0) = 0x17b6000 0.000044 0.000257 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 0.000047 0.000304 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf1c0dc000 0.000040 0.000344 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) 0.000040 0.000384 open("/etc/ld.so.cache", O_RDONLY) = 4 ... 0.000024 4.425049 close(10) = 0 0.000042 4.425091 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 0.000028 4.425119 read(255, "", 4409) = 0 0.000058 4.425177 exit_group(0) = ? 
  • script

     Le script a débuté sur ven 25 mar 2016 09:18:35 CET 0.667160 ++ gunzip 0.000025 0.000948 ++ perl 0.000011 0.005338 + eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3 15 13111 -6 1 111 4 0.000044 1223 15 3311 121121 17 3311 121121 1223 3311 121121 17 3311 121 0.000175 ++ BUNCHS=("2411 1115 -13 15 33 -3 15 13111 -6 15 1114 15 12211 0.000029 1 1321 12211 412 21211 33 21211 -2 15 2311 11121 232 121111 122 0.000023 4 3311 121121 12221 3311 121121 12221 3311 121121 1313 -6 15 33 

结论

好! 如果我的纯粹bash每个命令的date都要更快,那么我纯粹的bash在每个命令中包含一些操作。

专门用于logging和存储的独立过程显然更有效率。

strace是一个有趣的方式,更详细,但很难阅读。

scriptscriptreplay和加速因子也是非常好的,不是基于控制台交换而是基于stream程执行的相同精度,但非常轻巧和高效(目标不一样,用法不一样)。

最后,我认为效率更高,在可读性和性能方面set + 1 fork ,这个答案的第一个,但是在很好的情况下,根据具体情况,我也使用了某些时间段和/或screen

跟踪系统调用通常是有帮助的

 strace -c -f ./script.sh 

从手册:

-c为每个系统调用计算时间,调用和错误,并在程序退出时报告摘要。

-f跟踪subprocess…

这不完全是你想要的,一个面向行的分析器会显示给你,但它通常有助于find热点。

您可能需要查看带有DEBUG条件的trap命令。 有一种方法可以设置命令与您的命令一起执行。 请参阅答案的注释。

Alan Hargreaves的这篇文章介绍了使用DTrace提供程序分析Bourne shell脚本的方法。 据我所知,这与Solaris和OpenSolaris(参见: / bin / sh DTrace提供程序 )一起使用。

所以给了以下dtrace脚本(基于原始的 GH的sh_flowtime.d ):

 #!/usr/sbin/dtrace -Zs #pragma D option quiet #pragma D option switchrate=10 dtrace:::BEGIN { depth = 0; printf("%s %-20s %-22s %s %s\n", "C", "TIME", "FILE", "DELTA(us)", "NAME"); } sh*:::function-entry { depth++; printf("%d %-20Y %-22s %*s-> %s\n", cpu, walltimestamp, basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1)); } sh*:::function-return { printf("%d %-20Y %-22s %*s<- %s\n", cpu, walltimestamp, basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1)); depth--; } sh*:::builtin-entry { printf("%d %-20Y %-22s %*s > %s\n", cpu, walltimestamp, basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1)); } sh*:::command-entry { printf("%d %-20Y %-22s %*s | %s\n", cpu, walltimestamp, basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1)); } 

您可以跟踪包括增量时间的functionstream程。

示例输出:

 # ./sh_flowtime.d C TIME FILE DELTA(us) -- NAME 0 2007 Aug 10 18:52:51 func_abc.sh 0 -> func_a 0 2007 Aug 10 18:52:51 func_abc.sh 54 > echo 0 2007 Aug 10 18:52:52 func_abc.sh 1022880 | sleep 0 2007 Aug 10 18:52:52 func_abc.sh 34 -> func_b 0 2007 Aug 10 18:52:52 func_abc.sh 44 > echo 0 2007 Aug 10 18:52:53 func_abc.sh 1029963 | sleep 0 2007 Aug 10 18:52:53 func_abc.sh 44 -> func_c 0 2007 Aug 10 18:52:53 func_abc.sh 43 > echo 0 2007 Aug 10 18:52:54 func_abc.sh 1029863 | sleep 0 2007 Aug 10 18:52:54 func_abc.sh 33 <- func_c 0 2007 Aug 10 18:52:54 func_abc.sh 14 <- func_b 0 2007 Aug 10 18:52:54 func_abc.sh 7 <- func_a 

然后使用sort -nrk7命令,可以对输出进行sorting以显示消耗最多的调用。

我不知道任何提供程序可以有效地探测其他shell,所以做一些研究(GitHubsearch?),或者如果你想投资一些时间,你可以写这样的基于现有的sh例子:(见: 如何激活sh DTrace提供者? )。

时间,xtrace,bash -x, set -xset+xhttp://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html )仍然是debugging脚本的正统方法。

为了扩大我们的视野,可以检查某些系统的debugging和分析可用于通常的Linux程序(这里是其中一个列表) ,例如它应该导致基于valgrind的有用的特别是debugging内存或sysprof到configuration文件整个系统:

对于sysprof:

使用sysprof,您可以configuration您机器上运行的所有应用程序,包括multithreading或多处理应用程序…

之后select你感兴趣的子stream程的分支。


对于Valgrind:
有了更多的健身房,看起来Valgrind 可以通过二进制(例如OpenOffice )安装一些程序。

从valgrind的FAQ中可以看出,如果明确要求Valgrind将会描述subprocess

…即使默认情况下它的configuration文件只跟踪顶层进程,所以如果你的程序是由shell脚本 ,Perl脚本或类似的东西启动的,Valgrind将跟踪shell或Perl解释器或等价物。 ..

这将启用此选项

  --trace-children=yes 

其他参考:

  • Valgrind 手册。
  • 关于接口KCachegrind和Callgrind的一些消息或者也在这里 ,仍然在使用从CERN的wiki报道
  • gdb 手册。 对于gdb ,可以导致对脚本调用的c,c ++程序有用。