system函数

system函数用方便在一个进程中执行命令行(一行shell命令)。
用法如下:
#include <stdio.h>
#include <stdlib.h> int main()
{
printf("Hello\n");
system("sleep 5");
return 0;
}

在程序中通过system调用了命令行 sleep 5。(这里知识举一个例子,当然可以执行一个类似“ bash test.sh”之类的脚本

在这个小程序的运行时,可以通过ps -aux 看到新增加了三个进程。
一个是我们程序本身a.out
一个是shell进程:sh -c ***
一个是我们执行的命令行进程:sleep进程


system函数的实现:

《APUE》中给出了system的一中实现:

#include        <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h> int
system(const char *cmdstring) /* with appropriate signal handling */
{
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask; if (cmdstring == NULL)
return(1); /* always a command processor with UNIX */ ignore.sa_handler = SIG_IGN; /* ignore SIGINT and SIGQUIT */
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if (sigaction(SIGINT, &ignore, &saveintr) < 0)
return(-1);
if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
return(-1);
sigemptyset(&chldmask); /* now block SIGCHLD */
sigaddset(&chldmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
return(-1); if ((pid = fork()) < 0) {
status = -1; /* probably out of processes */
} else if (pid == 0) { /* child */
/* restore previous signal actions & reset signal mask */
sigaction(SIGINT, &saveintr, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL); execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); /* exec error */
} else { /* parent */
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR) {
status = -1; /* error other than EINTR from waitpid() */
break;
}
} /* restore previous signal actions & reset signal mask */
if (sigaction(SIGINT, &saveintr, NULL) < 0)
return(-1);
if (sigaction(SIGQUIT, &savequit, NULL) < 0)
return(-1);
if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
return(-1); return(status);
}
很多人对这个实现有不少的疑问。疑问点主要在下面几个方面:
首先:为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
其次:为什么要锁住子进程结束信号SIGCHLD
另外:为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。


1. 为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
前面已经介绍,./a.out执行时,产生了三个进程:a.out、(shell进程)sh -c、system调用的命令行产生的进程(如sleep 5, 在apue中是ed进程)。
如果不忽略SIGINT和SIGQUIT会出现上面情况呢,《APUE》中通过自己设计的不屏蔽SIGINT和SIGQUIT信号的实现进行了测试,发现SIGINT和SIGQUIT信号会向./a.out产生的三个进程发送,即会发送给a.out、sh -c、ed。(其中sh -c默认忽略此信号),这样会捕获信号的有a.out、ed。这样在实际运行中会出现上面情况呢:在我们要通过Ctl+c关闭ed(这种交互式程序经常会用ctl+c来关闭)时,信号也被发送给了a.out,也就是a.out和ed都被关闭了,这样可能a.out还有其他工作也不能完成了。
出于以上的原因,我们要求system屏蔽SIGINT和SIGQUIT。

2.为什么要锁住子进程结束信号SIGCHLD
首先明白,如果不锁住SIGCHLD,那么在system执行的命令行创建的子进程(如ed)结束时,会向./a.out也发送SIGCHLD信号。这会产生什么问题呢?
加入我们在调用system之前,./a.out创建了一个子进程(子进程A),并且是通过wait函数等待他结束。但是我们的ed进程结束时就像a.out发送了SIGCHLD,导致wait直接返回,由此a.out就无法等到子进程A结束了。

3.为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。
这一部分我还没找到确切的证据,但描述的也都是一些合理的推断:
我认为wait和waitpid并不是通过捕获SIGCHLD来返回子进程结束状态的。理由如下:
a.Linux系统对SIGCHLD的默认处理方式就是ignore
b.经常看到一些code里,捕获SIGCHLD,然后再SIGCHLD的信号处理函数中调用wait,来使得父进程不阻塞。
c.waitpid中的option选项为WNOHANG时,waitpid可以立即返回。也就是可以循环调用waitpid来判断子进程是否退出。
基于以上三点,基本上可以退出waitpid并不是通过捕获SIGCHLD来判断子进程状态的。而应该是通过阻塞读取内核某个状态值








APUE学习笔记——10.18 system函数 与waitpid的更多相关文章

  1. APUE学习笔记——10.9 信号发送函数kill、 raise、alarm、pause

    转载注明出处:Windeal学习笔记 kil和raise kill()用来向进程或进程组发送信号 raise()用来向自身进程发送信号. #include <signal.h> int k ...

  2. APUE学习笔记——10信号——信号接口函数 signal 和 sigaction

    signal函数     signal函数是早起Unix系统的信号接口,早期系统中提供不可靠的信号机制.在后来的分支中,部分系统使用原来的不可靠机制定义signal函数,如 Solaris 10 .而 ...

  3. APUE学习笔记——10.11~10.13 信号集、信号屏蔽字、未决信号

    如有转载,请注明出处:Windeal专栏 首先简述下几个概念的关系: 我们通过信号集建立信号屏蔽字,使得信号发生阻塞,被阻塞的信号即未决信号. 信号集: 信号集:其实就是一系列的信号.用sigset_ ...

  4. APUE学习笔记——10 信号

    信号的基本概念     信号是软件中断,信号提供了解决异步时间的方法.     每一中信号都有一个名字,信号名以SIG开头. 产生信号的几种方式     很多条件可以产生信号:     终端交互:用户 ...

  5. APUE学习笔记——8.1-8.4 进程基础

    进程ID 1 进程id是唯一的.(不会有进程id一样的两个进程) 2进程id是可复用的,一个进程销毁后,它的id号可以被新的进程使用.但是Unix采用了延迟复用的算法,也就是进程   销毁后它的id不 ...

  6. APUE学习笔记——10.15 sigsetjmp和siglongjmp

    转载自:sigsetjmp使用方法 如侵犯您的权益,请联系:windeal12@qq.com sigsetjmp使用方法 分类: c/c++ linux2012-02-03 12:33 1252人阅读 ...

  7. APUE学习笔记——10.可靠信号与不可靠信号

    首先说明:现在大部分Unix系系统如Linux都已经实现可靠信号. 1~31信号与SIGRTMIN-SIGRTMAX之间并不是可靠信号与不可靠信号的区别,在大多数系统下他们都是可靠信号. 只不过: 1 ...

  8. Hadoop源码学习笔记(2) ——进入main函数打印包信息

    Hadoop源码学习笔记(2) ——进入main函数打印包信息 找到了main函数,也建立了快速启动的方法,然后我们就进去看一看. 进入NameNode和DataNode的主函数后,发现形式差不多: ...

  9. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

随机推荐

  1. Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)返回非映射实体类的解决方法

    Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)易错使用 一直用ORM,今天用JdbcTemplate ...

  2. javascript中常用函数汇总

    js中函数很多,在实际项目开发中,函数的应用可以很大程度上简化我们的代码,所以在此记下开发中js中常用的函数,增强记忆. 1.isNaN(X):函数用于检查其参数是否是非数字值. 如果 x 是特殊的非 ...

  3. Let Her Go

    转自:https://www.zhihu.com/question/29255072/answer/43962611 [MV]Passenger-Let Her Go-高清MV在线播放 版本一 Let ...

  4. php项目代码 编码格式不对会大范围报错

    php项目代码 编码格式不对会大范围报错

  5. GIT使用—提交的查找与变更

    一.查找提交 (1)git bisect(二分搜索法) 基于任意搜索条件查找特定的错误提交.在排查某个提交版本导致的错误时非常有用. [root@localhost public_html]# git ...

  6. UVA 12657 Boxes in a Line(双向链表+小技巧)

    题意:对于一行按照顺序排列盒子数字与位置都为 1,2,3,4....n 执行四种操作 c = 1    x 放到 y 的左边 c =2     x 放到 y 的右边 c =3 交换 x, y c =4 ...

  7. webservice用cxf发布SOAP

    cxf的安装,就是把文件解压,然后配置环境变量 http://cxf.apache.org/download.html这是官网下载 解压到这里 环境变量 wsdl2java命令测试 1.新建java项 ...

  8. Glibc-2.3.4编译

    $tar xf Glibc2.3.4.tar.bz2 $mkdir build_glibc $cd build_glibc ../glibc-2.3.4/configure --prefix=/too ...

  9. Mysql uploader File

    前几天和哥们做一次渗透测试,内网情况.防护相当严格. 内网不允许访问DMZ,DMZ不允许访问内网,除了服务端口,比如80,3306. 经过长时间的分析,就发现本机连接了内网的一个MYQL,发现WEB开 ...

  10. C# 生成图片缩略图

    最近项目有部分需求,是关于图片操作部分的, 大致的功能就是图片的保存和展示.但是直接操作原图,程序运行效率太慢.而且如果传输数据量过大的话,可能直接导致调用WCF服务失败的问题. 为了解决这个问题,决 ...