一、函数wait和waitpid

今天我们继续通过昨天那个死爹死儿子的故事来讲(便于记忆),现在看看wait和waitpid函数。

#include<sys/wait.h>

pid_t wait(int *statloc);

pid_t waitpid(pid_t pid , int *statloc , int options);  若成功,返回进程ID,若出错,返回0或-1

wait系列函数的作用就是通知父亲儿子的死讯的,父进程一般接收到SIGCHLD信号而调用wait,(等待终止),可能有三种情况发生:

(1).子进程都在Running,则阻塞

(2).如果一个子进程终止,则父进程获取其终止状态(statloc指针指向的空间),然后立即返回

(3).如果它没有任何子进程,则立即出错返回

wait可以返回的四个终止状态的互斥的宏,分别为:

WIFEXITED(status)               正常终止

WIFSIGNALED(status)             异常终止

WIFSTOPPED(status)              暂停子进程

WIFCONTINUED(status)           暂停后已经继续的子进程

例子:

#include "apue.h"
#include <sys/wait.h> void pr_exit(int status)
{
if ( WIFEXITED(status) )
printf("normal termination,exit status = %d\n",
WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("abnormal termination,signal number = %d\n",
WTERMSIG(status));
else if (WIFSTOPPED(status))
printf("child stopped, signal number = %d\n",
WSTOPSIG(status));
}
int
main(void)
{
pid_t pid;
int status; if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0) /* child */
exit(7); if (wait(&status) != pid) /* wait for child */
err_sys("wait error");
pr_exit(status); /* and print its status */ if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0) /* child */
abort(); /* generates SIGABRT */ if (wait(&status) != pid) /* wait for child */
err_sys("wait error");
pr_exit(status); /* and print its status */ if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid == 0) /* child */
status /= 0; /* divide by 0 generates SIGFPE */ if (wait(&status) != pid) /* wait for child */
err_sys("wait error");
pr_exit(status); /* and print its status */ exit(0);
}

waitpid函数,顾名思义,其作用就在于可以等待特定的pid的子进程终止。此外,waitpid的options参数提供了WNOHANG(相当于不阻塞的wait)、WCONTINUED、WUNTRACED(支持作业控制)三个常量选项。

下面是一个非常经典的fork子、孙进程的示例:(两次fork,有效的避免僵死进程)

#include "apue.h"
#include <sys/wait.h>
int
main(void)
{
pid_t pid; if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* first child */
if ((pid = fork()) < 0)
err_sys("fork error");
else if (pid > 0)
exit(0); /* parent from second fork == first child */ /*
* We're the second child; our parent becomes init as soon
* as our real parent calls exit() in the statement above.
* Here's where we'd continue executing, knowing that when
* we're done, init will reap our status.
*/
sleep(60);
printf("second child, parent pid = %ld\n", (long)getppid());
exit(0);
} if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
err_sys("waitpid error"); /*
* We're the parent (the original process); we continue executing,
* knowing that we're not the parent of the second child.
*/
sleep(30);
exit(0);
}

为了便于测试效果明显,我将父进程sleep了30秒,然后退出,将孙进程sleep了60秒,然后退出。在这段代码中,最先退出的是子进程,为了防止孙进程僵死,我们让它sleep的时间更长,而父进程执行完毕退出后,孙进程被init进程收养,孙进程执行自己的任务,执行完毕后正常退出,使得该过程有效的避免了僵死进程。

此外,waitid类似于waitpid,但更为灵活,wait3、wait4的附加参数可以返回所有子进程使用的资源概况,不在此详细说明。

二、函数exec

当我们fork一个进程后,新的进程往往要调用exec执行一个启动例程,如第七章所述图:

因此,调用exec并不创建新的进程,只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段、栈段。

7个不同的exec函数:

#include <unistd.h>

int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );

int execv(const char *pathname, char *const argv[]);

int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );

int execve(const char *pathname, char *const argv[], char *const envp[]);

int execlp(const char *filename, const char *arg0, ... /* (char *)0 */ );

int execvp(const char *filename, char *const argv[]);

int fexecve(int fd, char *const argv[], char *const envp[]);

返回值:成功不返回,出错返回-1

七个exec函数的区别:

(1).前四个取路径名作为参数,后两个取文件名作为参数,最后一个以fd作为参数

(2).参数表的传递不同(l表示列表list,v表示矢量vector)包含l和包含v

(3).向新程序传递环境表不同。包含e和不含e

七个exec直接的关系如下图,只有execve是内核的系统调用,另外6个只是库函数,它们最终都要调用该execve系统调用。

exec函数使用例程:

#include "apue.h"
#include <sys/wait.h> char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL }; int
main(void)
{
pid_t pid; if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* specify pathname, specify environment */
if (execle("/home/webber/test/echoall", "echoall", "myarg1",
"MY ARG2", (char *)0, env_init) < 0)
err_sys("execle error");
} if (waitpid(pid, NULL, 0) < 0)
err_sys("wait error"); if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* specify filename, inherit environment */
if (execlp("echoall", "echoall", "only 1 arg", (char *)0) < 0)
err_sys("execlp error");
} exit(0);
}

echoall.c文件代码如下:

#include "apue.h"
int
main(int argc, char *argv[])
{
int i;
char **ptr;
extern char **environ; for (i = 0; i < argc; i++) /* echo all command-line args */
printf("argv[%d]: %s\n", i, argv[i]); for (ptr = environ; *ptr != 0; ptr++) /* and all env strings */
printf("%s\n", *ptr);
sleep(100);
exit(0);
}

这里同样是为了测试,我们sleep了100秒,模拟子进程的工作时间。

注意,需要预先把echoall.c文件编程,gcc echoall.c -o echoall ,否则,当我们exec找到该文件后无法执行。这样,我们就完成了启动子进程的一整个流程

Unix环境高级编程—进程控制(二)的更多相关文章

  1. UNIX环境高级编程——进程控制

    一.进程标识符 ID为0的进程是调度进程,常常被称为交换进程.该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程.进程ID 1通常是init进程,在自举过程结束时由内核调用.ini ...

  2. Unix环境高级编程—进程控制(三)

    一.解释器文件 解释器文件属于文本文件,起始行形式为: #! pathname[optional-argument] 我们创建一个只有一行的文件如下: #!/home/webber/test/echo ...

  3. unix环境高级编程----进程控制wait()

    一.wait()函数 当一个进程中调用wait()函数的时候 (1)假设其全部的子程序都还在执行,则堵塞 (2)假设一个子进程已终止.则等待父进程获取其终止状态. (3)假设没有子进程,则返回错误. ...

  4. UNIX环境高级编程——进程管理和通信(总结)

    进程管理与通信 进程的管理 进程和程序的区别: 进程: 程序的一次执行过程   动态过程,进程的状态属性会发生变化 程序:存放在磁盘上的指令.数据的有序集合  是个文件,可直观看到 程序program ...

  5. UNIX环境高级编程——进程基本概述

    一.什么是进程 从用户的角度来看进程是程序的一次执行过程.从操作系统的核心来看,进程是操作系统分配的内存.CPU时间片等资源的基本单位.进程是资源分配的最小单位.每一个进程都有自己独立的地址空间与执行 ...

  6. UNIX环境高级编程——进程关系

    一.终端的概念 在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),控制终端是保存在PCB中的信息,而我们 ...

  7. UNIX环境高级编程——进程环境

    一.main函数 C程序总是从main函数开始.当内核执行C程序时,在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址--这是由连接编译器设置的,而连接编译器则由 ...

  8. Unix环境高级编程—进程关系

    终端登录 网络登录 进程组 getpgrp(void) setpgid(pid_t pid, pid_) 会话: 是一个或多个进程组的集合,通常由shell的管道将几个进程编成一组. setsid(v ...

  9. UNIX环境高级编程——进程间通讯方法整理

    一.无名管道pipe #include <unistd.h> int pipe(int fd [2]) 二.fifo #include <sys/stat.h> int mkf ...

随机推荐

  1. JavaScript变量提升和函数声明预解析

    1.首先理解函数作用域 在JavaScript中,变量的定义并不是以代码块作为作用域的,而是以函数作用作用域的.也就是说,如果变量是在某个函数中定义的,那么它在函数以外的地方是不可见的.而如果该变量是 ...

  2. K皇后问题递归解法

      #include<iostream> #include<cmath> #include<ctime> using namespace std; bool che ...

  3. 牛客练习赛10 E题 数列查找 (分块思想 + 莫队算法)

    题目链接  数列查找 考虑分块然后跑莫队, 设$c[i]$为$i$在当前维护的区间内出现的次数, $g[i]$为在当前维护的区间内有多少个数出现次数为$i$, $bg[i]$把出现次数分块,$bg[i ...

  4. Hadoop1和Hadoop2的区别是什么?

    [学习笔记] Hadoop1和Hadoop2的区别是什么?马 克-to-win @ 马克java社区:原来的Hadoop1的Mapreduce又管资源管理,又管数据处理和计算.而Hadoop2中的Ma ...

  5. ABS已死: Archlinux 放弃支持 ABS

    今天访问archlinux官网,突然看到官方放弃支持ABS的新闻,声明如下: 由于 Arch Build System 的相关服务器端脚本的维护开销日益增高,我们决定放弃 abs及其相关的通过 rsy ...

  6. mysql赋给用户权限grant all privileges on

    查看mysql用户表的结构,Field项都是各类权限限制 Host限制登录的IP,User限制登录的用户,Delete_priv限制删除权限,Grant_priv限制权限授予,Super_priv为超 ...

  7. 关于Web应用和容器的指纹收集以及自动化软件的制作

    一次对Web应用的渗透,九成都是从信息收集开始,所以信息收集就显得尤为重要.关键信息的收集可以使你在后期渗透的时候更加的得心应手,把渗透比喻成走黑暗迷宫的话,那信息收集可以帮你点亮迷宫的大部分地图. ...

  8. overlay和overlay2的区别

    docker作为一个容器平台,它有一套自己的存储系统.它支持的driver有overlay,overlay2, aufs等等. 这篇文章主要分析overlay和overlay2的区别. overlay ...

  9. mysql 远程登陆不上

    当使用 TCP/IP 连接 mysql 时, 出现 : Can't connect to MySQL server on 'xxx.xxx.xxx.xxx.'(111) 这个错误. 经过重复折腾: 确 ...

  10. 学习日记之享元模式和Effective C++

    享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象. (1),享元模式能够避免大量很相似的开销.在程序设计中,有时须要生成大量细粒度的类实例来表示数据.假设能发现这些实例除了几个參 ...