• 进程管理之system


    system定义

    #include<stdlib.h>
    int system(const char *command);
    

    首先要知道,system函数是c库中的函数,而不是系统调用。其实system函数使用起来并不复杂,难就难在对其返回值的理解。这个问题,下文会详细分析。参数的话,很简单,就是终端的命令即可。这是因为system函数的实现中调用了shell的缘故。

    system优缺点

    • 优点:可以让c程序猿很方便地调用其他语言编写的程序,当然调用c程序自然也没问题;
    • 缺点:第一:效率低,第二:返回值难理解;
      效率低是因为system函数的实现过程至少要创建两个进程,一个是shell进程,还有一个或着多个shell命令运行的进程。所以在对效率高有要求的程序中,直接用fork和exec函数族比较合适;返回值的问题下文会讲;

    system函数返回值

    先写个简化版的system函数的实现过程。简化是没有考虑处理信号的问题。代码如下:

    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    
    int system(char * command)
    {
    	int status;
    	pid_t child;
    	
    	swicth(child = fork())
    	{
    		case -1:
    			return -1;
    		case 0:
    			execl("/bin/sh","sh","-c",command,NULL);
    			_exit(127);
    		default:
    			while(waitpid(child,&status,0)<0)
    			{
    				if(errno != EINTR)
    				{
    					status = -1;
    					break;
    				}
    			}
    			return status;
    	}
    }
    

    1)返回值为 “0” 或 “1”
    这中情况一般不会出现,只有当写成system(NULL)时,才会出现这种结果。目的是为了检测系统的shell是否可用。返回1表示shell可用,返回0表示shell不可用;这种情况在上诉代码中看不出来,通过glibc库中的源码可以看出:

    glibc-2.17/sysdeps/posix/system.c
    int __libc_system(const char *line)
    {
    	if(line == NULL)
    	return do_system("exit 0") == 0;
    	......
    }
    

    line指针指向的就是command命令行参数,system函数调用do_system系统调用,当执行“exit 0”(表示结束当前shell),执行成功do_system返回0,说明shell可用。反之,shell不可用。

    2)返回值为 -1
    有两个原因造成这样的结果。第一,因为fork创建子进程失败导致的。即“case -1”的情况,这中情况比较少见;第二,是不能正常处理子进程的“墓志铭”导致的,说白了,就是子进程的进程表在子进程结束时,没有经过父进程处理,而自己销毁了。这样的效果是因为父进程中安插的处理SIGCHLD信号的处理函数是SIG_IGN,或者用户设置了SA_NOCLDWAIT标志位导致,waitpid函数返回值为-1且全局变量errno的值为ECHLD;
    例如:

    signal(SIGCHLD,SIG_IGN); //出错的根源
    
    if( (status = system(command)) < 0 )
    {
    	fprintf(stderr,"system return %d (%s)
    ",status,strerror(errno));
    	return -2;
    }
    

    所以在使用system函数时,一定要判断SIGCHLD是否被设置为SIG_IGN。

    3)返回值为 _exit(127)的返回值
    这种情况是因为程序运行到“case 0”中,execl函数执行失败后,执行_exit函数导致的。说明子进程无法执行shell该shell命令。

    4)返回值为shell执行的进程的返回值
    shell的终止状态是其执行最后一条命令的退出状态。这种情况下和获取子进程的退出状态一样。通过如下宏获取其退出状态:

    • WIFEXITED(status)
    • WEXITSATUS(status)
    • WIFSIGNALED(status)
    • WTERMSIG(status)
    • WCOREDUMP(satus)

    综上所述,可以给出一个system函数返回值判断的例程:

    if( (status == system(command))==-1 )
    {
    	fprintf(stderr,"system() function return -1 (%s)
    ",strerror(errno));
    }
    else if(WIFEXITED(status)&&WEXITSTATUS(status) == 127)
    {
    	fprintf(stderr,"cannot invoke shell to exec command (%s)
    ",strerror(enrrno));
    }
    else
    	print_wait_exit(status);
    

    print_wait_exit函数就是上文中提到的获取shell退出状态的宏,根据需要去实现即可。

    system函数和信号

    影响system函数执行的信号有三个:SIGCHLD、SIGINT和SIGQUIT信号。

    SIGCHLD信号:上文已经提过,它会影响waitpid函数,产生竞争现象的出现。调用system函数的进程可能还有其他的子进程,当然同样也会有wait或waitpid函数。当有SIGCHLD信号到来时,system函数中的waitpid函数和其他的wait或waitpid函数产生竞争状态,从而使得system执行异常。所以system执行期间会暂时屏蔽SIGCHLD信号;

    SIGINT信号和SIGQUIT信号:调用system函数的进程会屏蔽这两个信号。system函数创建的进程会恢复这两个信号的默认处理状态。调用system函数的进程其实已经放弃了控制权,所以不能够去终止进程。反之,system函数创建的进程当然就有了控制权,所以要恢复控制权。

  • 相关阅读:
    判断&数学&生活
    Tomcat7源码环境搭建
    CentOS 7 下使用虚拟环境Virtualenv安装Tensorflow cpu版记录
    Quartz学习笔记1:Quartz概述
    Docker学习笔记2: Docker 概述
    大数据基础知识问答----spark篇,大数据生态圈
    [MSSQL] [EntityFramework(.Net Core)] 自增长id字段,无法插入数据
    [json-server] RESTful API 中,取主数据时,同时获取多个关联子表的数据
    前后端分离开发之前端自己的API(DB)---- (2)
    前后端分离开发之前端自己的API(DB)---- (1)
  • 原文地址:https://www.cnblogs.com/cjaaron/p/7476706.html
Copyright © 2020-2023  润新知