5. 等待函数

(1)wait和waitpid

头文件

#include <sys/types.h>

#include <sys/wait.h>

函数

pid_t wait(int* status);

pid_t waitpid(pid_t pid, int* status, int options);

返回值

成功返回子进程ID,出错返回-1

功能

等待子进程退出并回收,防止僵尸进程的产生

参数

(1)status参数:

  ①为空时,代表任意状态结束的子进程;

  ②不为空时,则等待指定状态结束的子进程

(2)waitpid的pid参数

  ①pid == -1 :等待任一子进程,功能与wait等效。

  ②pid > 0 :等待其进程ID与pid相等的子进程

  ③pid == 0:等待其组ID等于调用进程的组ID的任一子进程

  ④pid < -1:等待其组ID等于pid的绝对值的任一子进程。

(3)options参数

  ①WNOHANG:如果pid子进程未结束则立即返回,不会阻塞,此时返回值为0。如果pid进程己退出,则返回这个进程的pid。

  ②WUNTRACED:使waitpid报告那些己经被停止的未报告子进程的状态。

备注

wait的waitpid的区别:

  ①在一个子进程终止前,wait使用调用者阻塞

  ②waitpid的options可使调用者不阻塞

  ③waitpid等待一个指定的子进程,而wait等待所有的子进程,返回任一终止子进程的状态。

(2)检查wait和waitpid函数返回的终止状态的宏

判断终止状态

获取终止状态值

说明

WIFEXITED(status)

WEXITSTATUS(status)

判断子进程是否正常终止及获取退出码

WIFSIGNAL(status)

WTERMSIG(status)

判断子进程是否异常终止及获取异常终止的信号编码

WIFSTOPED(status)

WSTOPSIG(status)

判断子进程是否被暂停及获取暂停的信号编码

【编程实验】判断进程的终止状态

//process_wait.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> void out_status(int status)
{
if(WIFEXITED(status)){
printf("normal exit: %d\n", WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){
printf("abnormal terminate: %d\n", WTERMSIG(status));
}else if(WIFSTOPPED(status)){
printf("Stopped signal: %d\n", WSTOPSIG(status));
}else{
printf("unknow signal\n");
}
} int main(void)
{
int status;
pid_t pid; //第1个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("pid: %d, ppid: %d\n", getpid(), getppid());
exit(); //正常终止
} //等待子进程结束(也可防止僵尸进程)
if(wait(&status) != pid)
perror("wait error");
out_status(status);
printf("----------------------------------------\n"); //第2个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("pid: %d, ppid: %d\n", getpid(), getppid());
abort(); //异常终止
} //等待子进程结束(也可防止僵尸进程)
if(wait(&status) != pid)
perror("wait error");
out_status(status);
printf("----------------------------------------\n"); //第3个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("pid: %d, ppid: %d\n", getpid(), getppid());
int i = , j = ;
int k = i /j; //除0
printf("k: %d\n", k);
} if(wait(&status) != pid)
perror("wait error");
out_status(status);
printf("----------------------------------------\n"); //第4个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
printf("pid: %d, ppid: %d\n", getpid(), getppid());
pause(); //暂停,需要用带WUNTRACED的waipid来等待。
//同时,该子进程需发kill -SIGSTOP 来结束
}
//阻塞方式
waitpid(pid, &status, WUNTRACED); // //非阻塞方式
// do
// {
// //当waitpid返回0,表示子进程未结束
// pid = waitpid(pid, &status, WNOHANG | WUNTRACED);
//
// if (pid == 0) sleep(1);
// }while(pid == 0); out_status(status); return ;
}
/*输出结果:
pid: 1686, ppid: 1685
normal exit: 0
----------------------------------------
pid: 1687, ppid: 1685
abnormal terminate: 6
----------------------------------------
pid: 1688, ppid: 1685
abnormal terminate: 8
----------------------------------------
pid: 1689, ppid: 1685
Stopped signal: 19
*/

6. exec函数

(1)exec函数的主要作用

  ①在fork函数创建子进程后,子进程往往要调exec函数来执行另一个程序。

  ②当进程调用exec函数时,该进程完全由新程序代换,替换原有进程的正文,而新程序则从其main函数开始执行。因为exec并不创建新进程,所以前后的进程ID并不改变。

  ③exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段

(2)函数原型

头文件

#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* pathname, const char* arg0,…/*(char*)0*/);

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

返回值

出错返回-1,成功则不返回

功能

执行程序

参数

(1)argv参数为新程序执行main函数中传递的argv参数,最后一个元素为NULL。

(2)envp为进程环境表。

(3)execlp和execvp函数中的pathname,相对和绝对路径均可使用。其它四个函数中的pathname只能使用绝对路径。相对路径一定要在进程环境表对应的PATHT中。

备注

(1)execve函数为系统调用,其余为库函数。当成功执行execve后就不返回 execve后面的代码去执行,进程进入新的程序执行,最后从新程序中退出。但执行execve出错时,仍会返回原程序。

(2)函数名后带字母“l”,表示第2个参数为列表(List),列表中的第1个元素为要执行的程序名,最后一个参数必须为NULL。

(3)带“p”的函数,表示第1个参数可以是相对或绝对路径。如果是相对路径,则会在环境变量PATH指定的路径中搜索。p表示PATH

(4)带字母“v”表示参数列为通过一个字符串数组来传递,相当于main函数的argv参数,数组中的第1个元素必须是程序名,最后一个参数也必须为NULL。

(5)带“e”的函数,用户可以自己设置程序接收一个设置环境变量的数组。

(3)6个函数的关系

(4)exec执行后新进程保留原进程的一些特性

  ①进程ID、父进程ID、实际用户和组ID、进程组ID

  ②会话ID、控制终端;

  ③当前工作目录、根目录

  ④文件锁,进程信号屏蔽、未处理信号、资源限制

  ⑤闹钟尚余留的时间、tms_utime、tms_stime、tms_cutime以及tms_cstime的值。

  ⑥对打开文件的处理与每个描述符的(close-on-exec)标志有关。如果此标志设置,则在执行exec时关闭该描述符,否则该描述符仍打开。(一般系统默认这个标志位是关闭,也可以用fcntl来设置该标志位)。

  ⑦在执行exec时会自动关闭打开的目录流(调用opendir函数打开的目录),也可以用fcntl函数为打开的目录流的描述符设置close-on-exec标志。

【编程实验】打开新程序

//process_exec.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> char* cmd1 = "cat"; //相对路径
char* cmd2 = "/bin/cat"; //绝对路径
char* argv1 ="/etc/passwd";
char* argv2 = "/etc/group"; int main(void)
{
pid_t pid; //第1个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
//调用exec函数执行新的程序
//if(execl(cmd1, cmd1, argv1, argv2, NULL) < 0){ //错误,execl只能用绝对路径
if(execl(cmd2, cmd1, argv1, argv2, NULL) < ){
perror("execl error");
exit();
}else{
printf("execl %s success\n", cmd1); //不返回这里
} //当exec成功调用后,执行流进入新的程序,不返回到这里。
printf("after exec...\n");
}
//只有父进程会执行到这里,子进程因exec进入了新的程序
wait(NULL);
printf("--------------------------------\n"); //第2个子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){ //child process
char* argv[]={cmd1, argv1, argv2, NULL};
if(execvp(cmd1, argv) < ){
perror("execvp error");
exit();
}else{
printf("execvp %s success\n", cmd1); //不返回这里
}
} wait(NULL); return ;
}

7. system函数

头文件

#include <stdlib.h>

函数

int system(const char* command);

返回值

成功返回执行命令的状态,出错返回-1

功能

简化exec函数的使用

备注

①system函数内部构建一个子进程,由子进程调用exec函数

②/bin/bash -c "cmd"或者exec("bash","-c","cmd");

【编程实验】自定义system函数

//process_system.c

#include <unistd.h>
#include <stdlib.h> const char* cmd1 = "date > s1.txt";
const char* cmd2 = "date > s2.txt";
//模仿system函数
void mysystem(const char* cmd)
{
pid_t pid; //1.创建子进程
if((pid = fork()) < ){
perror("fork error");
exit();
}else if(pid == ){//子进程
//2. 通过exec函数创/bin/bash进程来执行命令
if(execlp("/bin/bash", "bin/bash","-c", cmd, NULL) < ){
perror("execlp error");
exit();
}
}else{ //父进程
wait();
}
} int main(void)
{
system("clear"); //清屏
system(cmd1);
mysystem(cmd2); //自定义的mysystem return ;
} /*输出结果
root@bogon 6.process]# cat s1.txt
2017年 01月 26日 星期四 19:26:23 CST
[root@bogon 6.process]# cat s2.txt
2017年 01月 26日 星期四 19:26:23 CST
[root@bogon 6.process]#
*/

第6章 进程控制(3)_wait、exec和system函数的更多相关文章

  1. UNIX环境高级编程 第8章 进程控制

    本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...

  2. 《Unix环境高级编程》读书笔记 第8章-进程控制

    1. 进程标识 进程ID标识符是唯一.可复用的.大多数Unix系统实现延迟复用算法,使得赋予新建进程的ID不同于最近终止所使用的ID ID为0的进程通常是调度进程,也常被称为交换进程.它是内核的一部分 ...

  3. APUE8进程控制 fork vfork exec

  4. Unix环境高级编程(六)进程控制

    本章介绍Unix的进程控制,包括进程创建,执行程序和进程终止,进程的属性,exec函数系列,system函数,进程会计机制. 1.进程标识符 每一个进程都有一个非负整数标识的唯一进程ID.ID为0表示 ...

  5. APUE学习之进程控制 - fork 与 vfork

    最后编辑: 2019-11-6 版本: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11) 一.进程标识 每一个进程都有一个唯一的非 ...

  6. UNIX环境编程学习笔记(22)——进程管理之system 函数执行命令行字符串

    lienhua342014-10-15 ISO C 定义了 system 函数,用于在程序中执行一个命令字符串.其声明如下, #include <stdlib.h> int system( ...

  7. 进程控制之exec函数

    用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...

  8. Linux进程控制——exec函数族

    原文:http://www.cnblogs.com/hnrainll/archive/2011/07/23/2114854.html 1.简介 在Linux中,并不存在exec()函数,exec指的是 ...

  9. Linux网络编程学习(二) ----- 进程控制(第三章)

    1.进程和程序 程序是一个可执行文件,而一个进程是一个执行中的程序实例.一个进程对应于一个程序的执行,进程是动态的,程序是静态的,多个进程可以并发执行同一个程序.比如几个用户可以同时运行一个编辑程序, ...

随机推荐

  1. cocos2d-x移植:xcode到eclipse

    xcode程序移植到eclipse 必要组件: 1.macos gcc编译器,若没有,在xcode->preference->downloads中下载command line tools( ...

  2. 冷知识:excel 2013有多少行列

    XFD1048576 列:24*26*26+6*26+4=16384 行:2^20=1048576

  3. input 文件上传

    <button class="blueButton fileinput-button" style="width:165px;" @click=" ...

  4. PAT 数列求和-加强版   (20分)(简单模拟)

    给定某数字A(1≤A≤9)以及非负整数N(0≤N≤100000),求数列之和S=A+AA+AAA+⋯+AA⋯A(N个A).例如A=1, N=3时,S=1+11+111=123 输入格式: 输入数字A与 ...

  5. Windows Security Login

    /********************************************************************************* * Windows Securit ...

  6. 状压dp终极篇(状态转移的思想)

    状压dp是将每种状态都压缩成用一个二进制串,然后利用位运算进行操作的dp,而凡是dp都需要进行状态转移 对于简单的dp问题只需要一个二维数组dp[ i ][ j ]就能解决 具体操作为首先把状态压缩为 ...

  7. poj2387 最短路

    题意:给出一堆双向路,求从N点到1点的最短路径,最裸的最短路径,建完边之后直接跑dij或者spfa就行 dij: #include<stdio.h> #include<string. ...

  8. 使用Visual Studio Code开发Asp.Net Core WebApi学习笔记(二)-- Web Api Demo

    在上一篇里,我已经建立了一个简单的Web-Demo应用程序.这一篇将记录将此Demo程序改造成一个Web Api应用程序. 一.添加ASP.NET Core MVC包 1. 在project.json ...

  9. tomcat源码阅读之SingleThreadModel

    一.接口简介: 实现了SingleThreadModel接口的servlet类只能保证在同一时刻,只有一个线程执行该servlet实例的service方法,在tomcat实现中会创建多个servlet ...

  10. spring boot 项目文件结构

    启动类建议放在RootPackage的原因: 根据SpringBoot的内置实现,Spring默认从@ComponentScan注解所在的位置开始向后扫描,也就是说,只要我们将组件放在启动类所在的Ro ...