8(进程控制)
来源:互联网 发布:火蓝刀锋知乎 编辑:程序博客网 时间:2024/06/04 00:33
本章需要熟练掌握如下几个函数fork,exec族,_exit,wait,waitpid
1 进程标识符
#include <unistd.h>pid_t getpid(void); Returns: process ID of calling processpid_t getppid(void); Returns: parent process ID of calling processuid_t getuid(void); Returns: real user ID of calling processuid_t geteuid(void); Returns: effective user ID of calling processgid_t getgid(void); Returns: real group ID of calling processgid_t getegid(void); Returns: effective group ID of calling process
2 fork函数
#include <unistd.h>pid_t fork(void); Returns: 0 in child, process ID of child in parent, 1 on error
Fork函数执行一次但返回两次。父进程的返回值是子进程的进程ID,子进程的返回值是0(并不代表子进程的进程ID是0)
子进程和父进程并不共享存储空间,仅是父进程的副本。
父子进程调用顺序不确定
#include "apue.h"int glob = 6; /* external variable in initialized data */char buf[] = "a write to stdout\n";Int main(void){ int var; /* automatic variable on the stack */ pid_t pid; var = 88; if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) err_sys("write error"); printf("before fork\n"); /* we don't flush stdout */ if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ glob++; /* modify variables */ var++; } else { sleep(2); /* parent */ } printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0);}
3 vfork函数
#include "apue.h"int glob = 6; /* external variable in initialized data */Int main(void){ int var; /* automatic variable on the stack */ pid_t pid; var = 88; printf("before vfork\n"); /* we don't flush stdio */ if ((pid = vfork()) < 0) { err_sys("vfork error"); } else if (pid == 0) { /* child */ glob++; /* modify parent's variables */ var++; _exit(0); /* child terminates */ } /* * Parent continues here. */ printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var); exit(0);}
和fork的两个区别:
1.子进程在调用exec之前在父进程空间中运行,共享内存
2.保证子进程先运行,内核使父进程休眠,所以不用sleep函数
exit和_exit就是用来正常终止一个进程的,主要区别是_exit会立刻进入内核,而exit先执行一些清除工作(包括执行各种终止处理程序,关闭所有标准I/O等,一旦关闭了IO,例如Printf等函数就不会输出任何东西了),然后才进入内核。这两个函数会对父子进程有一定的影响,当用vfork创建子进程时,子进程会先在父进程的地址空间运行(这跟fork不一样),如果子进程调用了exit就会把父进程的IO给关掉。
接下来我们可以做个小测试,将vfork改成fork,预估glob=6,var=88.因为子进程修改的是副本
4 wait和waitpid函数
#include <sys/wait.h> pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options);成功返回进程ID,出错返回-1
当一个进程正常终止时,内核就向父进程发送SIGCHLD信号。因为子进程终止是个异步事件,所以发送的也是异步信号。
对于调用了Wait和waitpid的进程会发生如下情况:
(1)子进程还在运行,则阻塞
(2)如果子进程已终止,正在等待父进程获取其终止状态,则取得该进程的终止状态并立即返回
(3)如果没有子进程,则立即出错返回
两者区别:
1.子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞
2.Waitpid有若干选项,可以控制它所等待的进程
#include "apue.h"#include <sys/wait.h>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);}//修改代码。输出status的同时也让其输出pid。pr_exit(status)改成printf(“%d %d\n”, status, pid);
调用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(2);//保证父进程先执行 printf("second child, parent pid = %d\n", 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. */ exit(0);}
第二个子进程调用sleep保证在打印父进程ID时第一个子进程已终止。如果没有sleep,它可能比父进程先执行,那么返回的ID将会是父进程ID。,而不是init进程(值为1)。
运行程序得到
$ ./a.out$ second child, parent pid = 1
5 exec函数
当进程调用exec函数时,该进程的程序完全替换为新程序,而新程序从main函数开始执行。调用exec并不创建新进程,所以前后的进程ID并未改变。
Fork创建新进程,exec执行新程序,exit和两个wait函数处理终止和等待终止
#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 []);
#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/sar/bin/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);}
在该程序中先调用execle,它要求一个路径名和一个特定的环境。下一个调用的是execlp,它用一个文件名将环境传送给新程序。
- 8进程控制
- 专题 8 进程控制
- Chapter 8 进程控制
- 8(进程控制)
- 8章 进程控制
- 第8章 进程控制
- 第8章 进程控制
- 第8章 进程控制
- 8、进程控制(1)
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- 进程控制
- Hibernate单表操作
- 基于归并排序的实现与应用
- 知道这20个正则表达式,能让你少写1,000行代码
- 【UNET自学日志】Part10 摧毁玩家
- Eclipse IDE for C/C++ 配置
- 8(进程控制)
- 自定义初学5——自定义View显示图片
- 数位DP
- app 两端出现黑色,不能全屏
- iOS 集成极光推送,绕过一些坑
- c++第四次试验——作业
- Ubuntu 12.04下安装OpenCV 2.4.2 和 ffmpeg-0.11.1
- JVM——GC
- C++第4次实验