原文地址:http://blog.sina.com.cn/s/blog_7708265a01010luz.html
在C/C++中我们有时需要用到system系统调用来完成一些操作系统“更加擅长”的工作,本篇对system调用原理,调用方法,返回值封装宏,system简单源代码等内容进行了描述。
1. 应用例子
#include <stdlib.h>
int system(const char *command);
返回值:
通常:shell命令执行成功返回0,失败返回非0.
1. 若参数string为空指针(NULL),则返回非零值
2. 若system()在调用/bin/sh时失败则返回127
返回127的例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
const char* cmd = "ll /home/wipan";
int status = system(cmd);
printf("Returned code: %d\n", WEXITSTATUS(status));
}
输入结果为:
[wipan@bjlinc1229 ~/src]$ ./system
sh: ll: command not found
Returned code: 127
3. 若fork不成功返回-1.
4. 若system()调用成功则最后会返回执行shell命令后的返回值(此值也可能是127,若shell命令执行的最后有返回值,则需注意其返回值的判断方法)
例子:
while ( !HDDready )
{
status = system("/usr/dhafw/InstallHDD/isHddMounted.sh > /tmp/isHddMounted");
if (status == 0) //调用成功
{
// Now check result of command
fp = fopen("/tmp/isHddMounted", "r");
……
}
}
else //调用失败
{
sleep(1);
}
}
2. system的调用过程原理
原理:当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接受命令之后做的事了。
3. 判断system返回是否成功更加详细的方法(实践中我们用上一种即可)
3.1 背景
exit的名字就能看出,这个系统调用是用来终止一个进程的。无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩下的所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。
exit系统调用带有一个整数类型的参数status,我们可以利用这个参数传递进程结束时的状态,比如说,该进程是正常结束的,还是出现某种意外而结束的,一般来说,0表示没有意外的正常结束;其他的数值表示出现了错误,进程非正常结束。我们在实际编程时,可以用wait系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理。关于wait的详细情况,我们将在以后的篇幅中进行介绍。
在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构。在Linux进程的5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集。
先来了解一下僵尸进程的来由,我们知道,Linux和UNIX总有着剪不断理还乱的亲缘关系,僵尸进程的概念也是从UNIX上继承来的,而UNIX的先驱们设计这个东西并非是因为闲来无聊想烦烦其他的程序员。僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,首先,这个进程是怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程强迫退出的?其次,这个进程占用的总系统CPU时间和总用户CPU时间分别是多少?发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中,试想如果没有僵尸进程,进程一退出,所有与之相关的信息都立刻归于无形,而此时程序员或系统管理员需要用到,就只好干瞪眼了。
waitpid调用和wait调用。这两者的作用都是收集僵尸进程留下的信息,同时使这个进程彻底消失。进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
3.2 判断返回值的宏
如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的,或被哪一个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,人们就设计了一套专门的宏(macro)来完成这项工作,下面我们来学习一下其中最常用的两个:
1) WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。
2) WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。
例子如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
main()
{
int status;
pid_t pc,pr;
pc=fork();
if(pc<0)
printf("error ocurred!\n");
else if(pc==0){
printf("This is child process with pid of %d.\n",getpid());
exit(3);
}
else{
pr=wait(&status);
if(WIFEXITED(status)){
printf("the child process %d exit normally.\n",pr);
printf("the return code is %d.\n",WEXITSTATUS(status));
}else
printf("the child process %d exit abnormally.\n",pr);
}
}
结果如下:
$ cc wait2.c -o wait2
$ ./wait2
This is child process with pid of 1538.
the child process 1538 exit normally.
the return code is 3.
3.3 判断system调用的返回值
#inlucde <wait.h>
...
sprintf(szCommand, "compress -fc %s > %s.Z", szFilePath, szFilePath);
iStatus = system(szCommand);
if (WIFEXITED(iStatus) && WEXITSTATUS(iStatus) == 0)
{
strcat(pstMsgHeader->acFileName, ".Z";
}
else
{
printf("Compress file <%s> fail with exit status %d.",
szFilePath, WEXITSTATUS(iStatus)) ;
return -1;
}
4. system源码
system()函数功能强大,很多人用却对它的原理知之甚少。看下简单的linux版system函数的源码:
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
int system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}
else if(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;
}
}
}
return status;
}