# fork
fork 是直接创建一个和当前进程相同的进程。通常原有的进程被称为父进程,新创建的进程是子进程。
fork 得到的子进程继承了父进程的大部分属性,但是使用不同数据段和堆栈段。
#include <sys/types.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
int main(void){ | |
pid_t pid; | |
pid = fork(); | |
if (pid<0){ | |
perror("fork failed"); | |
exit(1); | |
} | |
else if(pid == 0){ | |
printf("This is the child process, My PID is %d. My PPID is %d.\n", getpid(),getppid()); | |
} | |
else{ | |
printf("This is the parent process.My PID is %d.\n",getpid()); | |
} | |
return 0; | |
} |
执行结果:
This is the parent process.My PID is 6994.
This is the child process, My PID is 6995. My PPID is 6994.
fork 的最大特点就是一次调用,两次返回。
系统只调用了一次 fork,却分别在父进程和子进程中进行返回。
父进程中 fork 返回子进程 pid
,子进程中 fork 返回 0
。
子进程中通过 getpid
得到当前的进程的 pid,通过 ppid 获得父进程的 pid。
很好理解,子进程对应的父进程只有一个,而一个进程可能有多个进程。
执行 forkdemo 程序时的输出是会发生变化的,可能先打印父进程的信息,也可能先打印子进程的信息。
但是实际我试了几次,都是先打印父进程然后打印子进程,可以这么理解,因为父进程先执行所以先完成。
# Vfork
vfork 就是系统调用,生成的子进程和父进程共享内存地址空间,和父进程共享数据段。为了防止父进程重写子进程需要的数据,会将父进程阻塞,因此先执行子进程,再执行父进程。
子进程通过 exec 族函数执行另一个函数,执行 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,但是 exec 不会创建新的进程,他只是将该进程的数据和代码做了替换,PID 不会改变。
#include <unistd.h> | |
#include <sys/types.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
int main(void){ | |
pid_t pid; | |
pid = vfork(); | |
if (pid<0){ | |
printf("vfork error!\n"); | |
exit(1); | |
} | |
else if(pid==0){ | |
printf("Child process PID: %d.\n", getpid()); | |
char * argv[] = {"ls", "-al","/home",NULL}; | |
char * envp[] = {"PATH=/bin",NULL}; | |
if (execve("/bin/ls", argv, envp) < 0){ | |
printf("subprocess error"); | |
exit(1); | |
} | |
printf("this message would never be seen.") | |
} | |
//execv 会执行 ls 命令,执行完毕后就会推出,因为 exec 将数据完全替换了 | |
// 所以不会再执行原来的 c 程序。 | |
else{ | |
printf("Parent process PID: %d.\n",getpid()); | |
sleep(1); | |
} | |
return 0; | |
} |
执行结果:
可以看到程序先执行子进程,再执行了父进程。