Last Updated: 2023-11-05 08:21:23 Sunday
-- TOC --
Linux下著名的fork系统调用,它用来创建子进程,它没有入参!
// fork - create a child process
// man 2 fork
#include <unistd.h>
pid_t fork(void);
fork的一个奇妙之处是,它仅仅被调用一次,却能够返回两次,可能有三种不同的返回值:
fork创建子进程,是通过duplicating the calling process
的方式,在fork的时刻,父进程和子进程的memory space内容相同,但父子进程确又是不同的两个进程。fork采用copy-on-write pages的方式,memory只有在write的时候,才会copy到不同的地址,以便将两个进程区别开来。fork创建了一个与自身完全一样的进程,fork执行成功后,两个拥有相同代码的进程在fork调用之后开始分叉。
下面是一段测试代码:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define _PEXIT \
do {\
char ebuf[64] = {0};\
sprintf(ebuf, "%s: %d", __FILE__, __LINE__);\
perror(ebuf);\
exit(errno);\
}while(0)
int main(void) {
pid_t c1, c2;
printf("pid: %u\n", getpid());
if ((c1=fork()) == 0) { // child
printf("in child 1, ppid: %u, pid: %u\n", getppid(), getpid());
return 0;
}
else if (c1 == -1)
_PEXIT;
printf("child 1 pid: %u\n", c1);
sleep(1); // sync with child 1
if ((c2=fork()) == 0) {
sleep(2);
printf("in child 2, ppid: %u, pid: %u\n", getppid(), getpid());
return 0;
}
else if (c2 == -1)
_PEXIT;
printf("child 2 pid: %u\n", c2);
return 0; // early stop
}
这段测试代码故意让父进程提前return,然后child 2再执行结束。测试得到如下输出:
$ gcc -Wall -Wextra test_fork.c -o test_fork
$ ./test_fork
pid: 281824
child 1 pid: 281825
in child 1, ppid: 281824, pid: 281825
child 2 pid: 281826
in child 2, ppid: 1, pid: 281826
父进程结束之后,child 2的ppid就成了1。
类似前面这种情况,父进程先于子进程结束,子进程的PPID编号变为1(被1号进程领养了),此时的子进程就是所谓的孤儿进程!(没父亲了,为什么不说没妈了...)
fork出来的子进程会继承父进程所有的open file,除此之外,还是有很多细节上的差异,具体请参考manpage。常见fork与exec系列接口配合,来实现子进程执行不同于父进程的程序。Python标准os模块有封装fork接口,下面是一个Python版的测试代码,跟上面的测试代码功能完全相同:
import os
import sys
from time import sleep
print('pid:', os.getpid())
try:
pid = os.fork()
if pid == 0:
print('in child 1, ppid: %d, pid: %d' % (os.getppid(), os.getpid()))
sys.exit(0)
except OSError as e:
print(os.strerror(e.errno))
sys.exit(e.errno)
print('child 1 pid:', pid)
sleep(1)
try:
pid = os.fork()
if pid == 0:
sleep(2)
print('in child 2, ppid: %d, pid: %d' % (os.getppid(), os.getpid()))
sys.exit(0)
except OSError as e:
print(os.strerror(e.errno))
sys.exit(e.errno)
print('child 2 pid:', pid)
sys.exit(0)
OSError异常后,异常对象的errno值可取,这个值与OS Kernel返回的错误码是对应的。
本文链接:https://cs.pynote.net/sf/linux/prog/202202041/
-- EOF --
-- MORE --