Linux下system () 函数详解简介
2016-12-21 10:14
351 查看
(执行shell命令)
相关函数fork,execve,waitpid,popen
表头文件
#include<stdlib.h>
定义函数
intsystem(constchar*string);
函数说明
system()会调用fork()产生子进程,由子进程来调用/bin/sh-cstring来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD信号会被暂时搁置,SIGINT和SIGQUIT信号则会被忽略。
返回值
如果fork()失败返回-1:出现错误
如果exec()失败,表示不能执行Shell,返回值相当于Shell执行了exit(127)
如果执行成功则返回子Shell的终止状态
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值>。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno来确认执行成功。
附加说明
在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
System与exec的区别
1、system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程2、system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程
system()函数功能强大,很多人用却对它的原理知之甚少先看linux版system函数的源码:
#include<sys/types.h> #include<sys/wait.h> #include<errno.h> #include<unistd.h> intsystem(constchar*cmdstring) { pid_tpid; intstatus; if(cmdstring==NULL){ return(1); } if((pid=fork())<0){ status=-1; }elseif(pid=0){ execl("/bin/sh","sh","-c",cmdstring,(char*)0); -exit(127);//子进程正常执行则不会执行此语句 }else{ while(waitpid(pid,&status,0)<0){ if(errno!=EINTER){ status=-1; break; } } } returnstatus; }
先分析一下原理,然后再看上面的代码大家估计就能看懂了:
当system接受的命令为NULL时直接返回,否则fork出一个子进程,因为fork在两个进程:父进程和子进程中都返回,这里要检查返回的pid,fork在子进程中返回0,在父进程中返回子进程的pid,父进程使用waitpid等待子进程结束,子进程则是调用execl来启动一个程序代替自己,execl("/bin/sh","sh","-c",cmdstring,(char*)0)是调用shell,这个shell的路径是/bin/sh,后面的字符串都是参数,然后子进程就变成了一个shell进程,这个shell的参数
是cmdstring,就是system接受的参数。在windows中的shell是command,想必大家很熟悉shell接受命令之后做的事了。
如果上面的你没有看懂,那我再解释下fork的原理:当一个进程A调用fork时,系统内核创建一个新的进程B,并将A的内存映像复制到B的进程空间中,因为A和B是一样的,那么他们怎么知道自己是父进程还是子进程呢,看fork的返回值就知道,上面也说了fork在子进程中返回0,在父进程中返回子进程的pid。
windows中的情况也类似,就是execl换了个又臭又长的名字,参数名也换的看了让人发晕的,我在MSDN中找到了原型,给大家看看:
HINSTANCEShellExecute( HWNDhwnd, LPCTSTRlpVerb, LPCTSTRlpFile, LPCTSTRlpParameters, LPCTSTRlpDirectory, INTnShowCmd );
用法见下:
ShellExecute(NULL,"open","c:\\a.reg",NULL,NULL,SW_SHOWNORMAL);
你也许会奇怪ShellExecute中有个用来传递父进程环境变量的参数lpDirectory,linux中的execl却没有,这是因为execl是编译器的函数(在一定程度上隐藏具体系统实现),在linux中它会接着产生一个linux系统的调用execve,原型见下:
intexecve(constchar*file,constchar**argv,constchar**envp);
看到这里你就会明白为什么system()会接受父进程的环境变量,但是用system改变环境变量后,system一返回主函数还是没变。原因从system的实现可以看到,它是通过产生新进程实现的,从我的分析中可以看到父进程和子进程间没有进程通信,子进程自然改变不了父进程的环境变量。希望小菜们不要拿tc或使用tc库的其他编译器中的system的调用结果来反驳我,这不是一个概念,DOS早死翘翘了,玩linux吧。就说到这里了。
c语言调用Linux的命令system("");popen
exec系列
execl等等类似的函数都可以执行任何shell下的命令。
#include<stdlib.h>
intexecl(constchar*path,constchar*arg,...);
intexeclp(constchar*file,constchar*arg,...);
intexecle(constchar*path,constchar*arg,...,char*constenvp[]);
intexecv(constchar*path,char*constargv[]);
intexecvp(constchar*file,char*constargv[]);
也可以,不同之处是前者可以执行完毕而不退出,后者执行完毕,正确会推出,错误则不退出,继续下面的语句。
【C/C++】Linux下使用system()函数一定要谨慎
曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值、它所执行命令的返回值以及命令执行失败原因如何定位,这才是重点。当初因为这个函数风险较多,故抛弃不用,改用其他的方法。这里先不说我用了什么方法,这里必须要搞懂system()函数,因为还是有很多人用了system()函数,有时你不得不面对它。
先来看一下system()函数的简单介绍:
view
source
1 | #include <stdlib.h> |
2 | int system ( const char *command); |
beblocked,andSIGINTandSIGQUITwillbeignored.
system()函数调用/bin/sh来执行参数指定的命令,/bin/sh一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;
在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说;
在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。
再来看一下system()函数返回值:
Thevaluereturnedis-1onerror(e.g.fork(2)failed),andthereturnstatusofthecommandotherwise.Thislatterreturnstatusisinthe
formatspecifiedinwait(2).Thus,theexitcodeofthecommandwillbeWEXITSTATUS(status).Incase/bin/shcouldnotbeexecuted,theexitstatuswillbethatofacommandthatdoesexit(127).
IfthevalueofcommandisNULL,system()returnsnonzeroiftheshellisavailable,andzeroifnot.
为了更好的理解system()函数返回值,需要了解其执行过程,实际上system()函数执行了三步操作:
1.fork一个子进程;
2.在子进程中调用exec函数去执行command;
3.在父进程中调用wait去等待子进程结束。
对于fork失败,system()函数返回-1。
如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。
(注意,command顺利执行不代表执行成功,比如command:"rmdebuglog.txt",不管文件存不存在该command都顺利执行了)
如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.
如果command为NULL,则system()函数返回非0值,一般为1.
看一下system()函数的源码
看完这些,我想肯定有人对system()函数返回值还是不清楚,看源码最清楚,下面给出一个system()函数的实现:
view
source
01 | int system ( const char * cmdstring) |
02 | { |
03 | pid_t pid; |
04 | int status; |
05 |
06 | if (cmdstring |
07 | { |
08 | return (1); //如果cmdstring为空,返回非零值,一般为1 |
09 | } |
10 |
11 | if ((pid |
12 | { |
13 | status //fork失败,返回-1 |
14 | } |
15 | else if (pid |
16 | { |
17 | execl( "/bin/sh" , "sh" , "-c" , char *)0); |
18 | _exit(127); // |
19 | } |
20 | else //父进程 |
21 | { |
22 | while (waitpid(pid, |
23 | { |
24 | if ( errno != |
25 | { |
26 | status //如果waitpid被信号中断,则返回-1 |
27 | break ; |
28 | } |
29 | } |
30 | } |
31 |
32 | return status; //如果waitpid成功,则返回子进程的返回状态 |
33 | } |
看一下该怎么监控system()函数执行状态
这里给我出的做法:
view
source
01 | int status; |
02 | if (NULL //如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针 |
03 | { |
04 | return XXX; |
05 | } |
06 | status system (cmdstring); |
07 | if (status |
08 | { |
09 | printf ( "cmd: , strerror ( errno )); // |
10 | return XXX; |
11 | } |
12 |
13 | if (WIFEXITED(status)) |
14 | { |
15 | printf ( "normal , //取得cmdstring执行结果 |
16 | } |
17 | else if (WIFSIGNALED(status)) |
18 | { |
19 | printf ( "abnormal , //如果cmdstring被信号中断,取得信号值 |
20 | } |
21 | else if (WIFSTOPPED(status)) |
22 | { |
23 | printf ( "process , //如果cmdstring被信号暂停执行,取得信号值 |
24 | } |
system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。
popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。
这篇文章只涉及了system()函数的简单使用,还没有谈及SIGCHLD、SIGINT和SIGQUIT对system()函数的影响,事实上,之所以今天写这篇文章,是因为项目中因有人使用了system()函数而造成了很严重的事故。现像是system()函数执行时会产生一个错误:“Nochildprocesses”。
相关文章推荐
- Linux下system () 函数详解简介
- Discuz!教程之帖子摘要(简介)截取函数messagecutstr()详解
- Linux下system () 函数详解简介
- MySQL简介、安装、函数接口和Linux C\C++操作MySQL实例详解
- MySQL简介、安装、函数接口和Linux C\C++操作MySQL实例详解
- XML系列函数详解
- MFC程序中消息以及函数的处理顺序简介!
- XML系列函数详解
- Oracle 中DECODE 函数的 使用简介
- Boost String Algorithms Library 函数详解一 (case conversion和trimmming)
- windows API函数简介
- Oracle函数详解
- netfilter各个HOOK的关系简介(笔记中已经总结到函数调用流程中)
- Oracle中PL/SQL单行函数和组函数详解
- Oracle中PL/SQL单行函数和组函数详解
- PL/SQL单行函数和组函数详解
- MFC程序中消息以及函数的处理顺序简介!
- 详解Variant 的相关函数
- PL/SQL单行函数和组函数详解
- ASP的函数详解