fork函数
由fork创建的新进程被称为子进程(child process),fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getppid以获得其父进程的进程ID(进程ID 0 总是由内核交换进程使用,所以一个子进程的进程ID不可能为 0)。子进程是父进程的副本。
//fork - create a child process,返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
#include <unistd.h>
pid_t fork(void);
父、子进程之间的区别是:
- 列表内容
- fork的返回值
- 进程ID不同
- 两个进程具有不同的父进程ID:子进程的父进程ID是创建它的进程的ID,而父进程的父进程ID则不变
- 子进程的tms_utime,tms_stime,tms_cutime以及tms_ustime均被设置为0
- 父进程设置的文件锁不会被子进程
- 子进程的未处理的闹钟( alarm )被清除
- 子进程的未处理信号集设置为空集
fork有两种用法:
- 一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的–父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
- 一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec
使用fork失败的主要原因是:
- 系统中已经有了太多的进程
- 该实际用户ID的进程总数超过了系统限制
/*****************************************
> File Name : fork.cpp
> Description : fork两次,避免僵死进程
gcc -g -o fork fork.cpp
> Author : linden
> Date : 2016-01-25
*******************************************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void)
{
pid_t pid;
if ((pid = fork()) < 0)
{
fprintf(stderr, "Fork error!\n");
exit(-1);
}
else if (pid == 0) /* first child */
{
if ((pid = fork()) < 0)
{
fprintf(stderr, "Fork error!\n");
exit(-1);
}
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 */
{
fprintf(stderr, "Waitpid error!\n");
exit(-1);
}
/*
* We're the parent (the original process); we continue executing,
* knowing that we're not the parent of the second child.
*/
exit(0);
}
wait和waitpid函数
调用wait或waitpid的进程可能会发生的情况:
- 如果其所有子进程都还在运行,则阻塞
- 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
- 如果它没有任何子进程,则立即出错返回
如果进程由于接收到SIGCHLD信号而调用wait,则可期望wait会立即返回。但是如果在任意时刻调用wait,则进程可能会阻塞。
//参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数指定为空指针。两者返回值:若成功返回进程ID,0,若出错返回-1
#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
两函数区别:
- 在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞
- waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程
/*****************************************
> File Name : fork.cpp
> Description : fork示例代码
gcc -g -o fork fork.cpp
> Author : linden
> Date : 2016-01-22
*******************************************/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
void pre_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 fire generated) " : "");
#else
"");
#endif
}
else if (WIFSTOPPED(status))
{
printf("child stopped,signal number = %d\n",WSTOPSIG(status));
}
}
int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
if (0 > pid)
{
perror("fork error:");
}
else if (0 == pid)
{
printf("This is the child process: %d, ppid = %d.\n", getpid(),getppid());
exit(0);
}
printf("This is the parent process: %d.\n", getpid());
//判断子进程结束:wait子进程结束
int status;
if (wait(&status) != pid)
{
perror("Error --> wait:");
}
pre_exit(status);
return 0;
}