在Unix系统中,只有一个系统调用可以用来创建新进程:fork。
由fork创建的新进程被称为子进程。fork函数被调用一次,但返回2次。2次返回的区别是子进程的返回值是0,而父进程的返回值是新创建的额子进程的进程ID。
子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间。
例如
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int n=0;;
pid_t pid;
if((pid=fork())<0)
{
fprintf(stderr,"fork error:[%s]\n",strerror(errno));
exit(1);
}
else if(pid>0) //父进程
{
sleep(2); //为了保证子进程先执行,父进程睡眠2s
}
else //子进程
{
n++;
}
printf("pid = %ld, n = %d\n",(long)getpid(),n);
exit(0);
}
运行的结果为
可以看到,子进程的变量值改变了,父进程的变量值没有改变
文件共享
fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中。我们说“复制”是因为对每个文件描述符来说,就好像执行了dup函数。父进程和子进程每个相同的打开描述符共享同一个文件表项。
重要的一点是, 父进程和子进程共享同一个文件偏移量。考虑下述情况: 一个进程fork了一个子进程, 然后等待子进程终止. 假定,作为普通处理的一部分, 父进程和子进程都向标准输出进行写操作。如果父进程的标准输出己重定向(很可能是由shell实现的), 那么子进程写到该标准输出时, 它将更新与父进程共亭的该文件的偏移量. 在这个例子中, 当父进程等待子进程时, 子进程写到标准输出:而在子进程终止后, 父进程也写到标准输出上, 并且知道其输出会追加到子进程所写数据之后。如果父进程和子进程不共享同一文件偏移量,要实现这种形式的交互就要困难得多。
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#define MAXLEN 1024
int main()
{
int n;
int fd[2];
pid_t pid;
char buf[MAXLEN];
int file_fd;
file_fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC); //打开一个文件
if((pid=fork())<0)
{
fprintf(stderr,"fork error:[%s]\n",strerror(errno));
exit(1);
}
else if(pid>0)
{
sleep(2);
write(file_fd,"parent:hello world\n",19); //父进程写入
}
else
{
write(file_fd,"child:hello world\n",18); //子进程写入
}
exit(0);
}
运行结果
可以看到,父进程的写入的数据在子进程写入的数据之后。