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. MVC中 关于退出按钮的写法

    public ActionResult Logoff() { Session.Abandon(); Session.Clear(); FormsAuthentication.SignOut(); re ...

  2. crontab + rsyncd同步方案

    目的主机: rsync --daemon [root@iZ23ohdbxmrZ ~]# vim /etc/rsyncd.conf #global settingsport = 873pid file= ...

  3. GET 对比 POST

    HTTP 方法:GET 对比 POST HTTP 消息 标签列表(字母排序) 两种最常用的 HTTP 方法是:GET 和 POST. 什么是 HTTP? 超文本传输协议(HTTP)的设计目的是保证客户 ...

  4. LCD1602

    一.关于LCD1602: 在编写LCD1602程序前,我们必须了解其手册上一些非常重要的信息,如果这些信息不能理解透彻,编程可能会遇到或多或少的问题,在此先大致归纳几点. 1.管脚: 1602共16个 ...

  5. LeetCode(476): Number Complement

    Given a positive integer, output its complement number. The complement strategy is to flip the bits ...

  6. [翻译]纠正PostCSS的4大认识误区

    市面上已经有很多的前端工具,再来引入新的前端工具,价值大不大?这主要取决于,它是否给开发人员提供了新的功能,是否值得花时间和精力去学习和使用? PostCSS出现时有一个很有趣的现象.像sass和le ...

  7. [CF1042F]Leaf Sets

    题意:给定一棵$n$个点的树,将叶子节点分为数个集合使集合里点对最长距离不超过$k$,求最少集合数.($n\le1000000$) 首先我们可以想到,这道题并不是让你构造最优方案,因为只要把所有叶子节 ...

  8. 好的Mysql 查询语句

    select swr.id,swr.name,swr.sort as type,count(swl.id) as nums,ifnull(sum(swl.package_num),0) package ...

  9. hdu 1241 搬寝室 水dp

    搬寝室 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Desc ...

  10. Gtk基础学习总结(一)

    第一个GTK程序例子: #include <stdio.h> #include <gtk/gtk.h> int main(int argc, char *argv[]) { g ...