进程控制
这一章的重点是fork,wait,waitpid,exec函数,接下来就根据demo来了解这几个函数,就不抄apue了,内容太多,重点不突出哈。。。
1:fork函数
pid_t fork(void)
对于fork函数,它一次调用,两次返回,由fork创建的进程是字进程,对于父进程,fork函数返回子进程的pid,对于子进程,fork函数返回0。子进程和父进程继续执行fork调用之后的指令,子进程此时是父进程的一个副本,子进程获得父进程数据空间、队、栈的副本。由于fork后子进程一般执行exec函数,所以现在很多实现并不执行父进程数据段、堆和栈的完全副本,使用了写时复制技术。对于父进程和子进程共享的区域,内核将他们的权限设为只读,如果发生了写操作,则会产生一个缺页中断,内核只为被修改的那一页做副本。
1 #include "apue.h"
2
3 int globvar = 6;
4 char buf[] = "a write to stdout\n";
5
6 int main()
7 {
8 int var;
9 pid_t pid;
10 var = 88;
11 if(write(STDOUT_FILENO,buf,sizeof (buf) - 1) != sizeof(buf) - 1)
12 {
13 err_sys("write error");
14 }
15 printf("before fork\n");
16
17 if((pid = fork()) < 0)
18 {
19 err_sys("fork error");
20 }
21 else if(pid == 0)
22 {
23 globvar++;
24 var++;
25 }
26 else {
27 sleep(2);
28 }
29 printf("pid = %ld,glob = %d,var = %d\n",(long)getpid(),globvar,var);
30 exit(0);
31 }
上面的例子中,子进程更改了var的值,但是父进程的var不变。
当然,父进程和子进程虽然执行写时复制操作,但两者还有不同:
(1) fork(2) 的返回值不同;
(2) 父子进程的 PID 不相同;
(3) 父子进程的 PPID 不相同; // PPID 就是父进程 PID
(4) 在子进程中资源的利用量清零,否则如果父进程打开了很多资源,子进程能使用的资源量就很少了;
(5) 未决信号和文件锁不继承。
2:wait waitpid 函数
在父进程fork出子进程后,如果子进程在父进程结束前结束,那么父进程需要调用wait函数或者waitpid函数来回收子进程,否则子进程会编程僵尸进程,所谓僵尸进程就是进程虽然已经结束了,但是这个进程依旧占用某些资源。
在调用wait或者waitpid函数后,进程会有以下三种状态:
(1) 如果其所有的子进程都还在运行,则阻塞
(2) 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态并立即返回
(3)如果其没有任何子进程,则立即返回出错
wait和waitpid有以下区别
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项可使其调用者不阻塞。
waitpid并不等待在调用之后的第一个终止子进程,他有若干个选项,可以控制它等待的进程。
#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%s\n",
WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? "(core file generated)" : "");
#else
"");
#endif
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)
exit(7);
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid == fork()) < 0)
err_sys("fork error");
else if(pid == 0)
abort();
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
if((pid = fork()) < 0)
err_sys("fork error");
else if(pid == 0)
status /= 0;
if(wait(&status) != pid)
err_sys("wait error");
pr_exit(status);
exit(0);
}
1 #include "apue.h"
2 #include<sys/wait.h>
3
4 int main(void)
5 {
6 pid_t pid;
7 if((pid = fork()) < 0)
8 {
9 err_sys("fork error");
10 }
11 else if(pid == 0)
12 {
13 if((pid = fork()) < 0)
14 {
15 err_sys("fork error");
16 }
17 else if(pid > 0)
18 exit(0);
19
20 sleep(2);
21 printf("second child,parent pid = %ld\n",(long)getppid());
22 exit(0);
23 }
24 else
25 {
26 if(waitpid(pid,NULL,0) != pid)
27 err_sys("waitpid error");
28 sleep(3);
29 }
30 exit(0);
31 }
3:exec函数
当调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序从main函数处开始执行
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include "apue.h"
5
6 int main()
7 {
8 pid_t pid;
9 puts("Begin!");
10 fflush(NULL);
11 pid = fork();
12 if(pid < 0)
13 {
14 perror("fork()\n");
15 }
16 else if(pid == 0)
17 {
18 execl("/home/weilon01/study/C++_Profile/apue/chapter8/8_4_demo","8_4_demo","./",NULL);
19 perror("execl()");
20 exit(1);
21 }
22 else
23 {
24 printf("father pid\n");
25 }
26 wait(NULL);
27 puts("End!");
28 exit(0);
29 }