创建进程之 fork函数
一个现有的进程可以调用fork函数创建一个新进程。
#include<unistd.h>
pid_t fork(void);
//返回值:子进程返回0,父进程返回子进程ID:若出错,返回-1
由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但返回两次,子进程返回0,父进程返回子进程ID:
将子进程ID返回给父进程的理由是:
因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。
fork 使子进程得到返回值0的理由是:一个进程只会有一个父进程,
所以子进程总是可以调用getpid以获得其父进程的进程ID,进程PID 0 总是被系统内核交换进程使用,所以一个子进程的进程ID不可能为0。
子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。
父进程希复制自己,使父子进程执行不同的代码段。
常见于网络编程中,父进程等待客户端的请求,当请求到达时,父进程调用 fork,让子进程处理此请求,
父进程则继续等待下一个服务请求到达。
子进程获得父进程数据空间、堆和栈的副本。父进程和子进程并不共享这些存储空间部分但是父进程和子进程正文段共享。
链接
what正文段??前面学习了c程序存储空间布局。
//demofork.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int globvar = 100;
char buf[] = "a write to stdput\n";void err_sys(const char * str)
{
fprintf(stderr,"%s\n",str); exit(1);
}int main()
{
int var;pid_t pid;var = 200;if(write(STDOUT_FILENO, buf,sizeof(buf)-1) != sizeof(buf)-1)err_sys("write error");printf("before fork\n");if((pid = fork()) < 0){
err_sys("fork error");}else if(pid == 0){
globvar++;var++;}else{
sleep(2);//主进程休眠2s}printf("pid = %ld, glob = %d, var = %d\n",(long)getpid(),globvar,var);exit(0);
}
走一个:
chenxx@ubuntu:~/demoFP$ ./a.out
a write to stdput
before fork
pid = 2793, glob = 101, var = 201 //子进程值已经被改变
pid = 2792, glob = 100, var = 200 //父进程
明显可以看出,子进程值已经被改变,父进程值并未改变。
通常,fork 之后我们不能预料哪一个进程先运行。
即使我们知道哪一个进程先运行,在该进程开始运行后所发生的事情也依赖于系统负载以及内核的调度算法。
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,此时竞争条件就产生了。
创建进程之 vfork函数
pid_t vfork(void)
用法与fork类似,不同点是:
vfork()函数直接使用父进程存储空间,不拷贝。
vfork()函数保证子进程先运行,当子进程调用exit()函数退出后,父进程才执行。
进程与线程常用函数:
进程原语 | 线程原语 | 描述 |
---|---|---|
fork | pthread_create | 创建新的控制流 |
exit | pthread__exit | 从现有控制流退出 |
waitpid | pthread_join | 从控制流中得到退出状态 |
atexit | pthread_cancel_push | 注册在退出控制流时调用的函数 |
getpid | pthread_self | 获取控制流ID |
abort | pthread_cancel | 请求控制流非正常退出 |