system函数的原理和调用方法

 

原文地址: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;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值