一、定义
1.fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器,在父进程中使用的相同打开文件。
它不需要参数并返回一个整数值。下面是fork()返回的不同值。
负值:创建子进程失败。
零:返回到新创建的子进程。
正值:返回父进程或调用者。该值包含新创建的子进程的进程ID。
2.所要用到的头文件有:
#include<sys/types.h>
#include<unistd.h>
3.说明:
——父、子进程完全一样(代码、数据),子进程从fork内部开始执行,fork返回子进程的pid后,接着执行下一条语句。
——该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是子进程的ID。
——一般来说,在fork之后是父进程先执行还是子进程先执行是取决于内核锁使用的调度算法。
4.示例代码(一)——这段程序通过fork函数不同的返回值来验证不同的进程在运行,同时通过一些全局变量和局部变量来看到父子进程之间的继承关系。
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<unistd.h> int galbol = 6;//定义全局变量 char buf[] = "Hello Linux";//定义一个字符串 int main(){ int var = 88;//定义一个局部变量 pid_t pid;//(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中) puts(buf);//输出字符串 pid = fork();//执行fork函数并把返回值赋给pid if (pid < 0)//如果返回值小于0,则创建失败 { printf("error "); exit(0); } if (pid == 0)//返回值等于0,表示创建成功,是子进程运行 { printf("This is children process! "); galbol++; var++; } else //如果返回ID就是父进程 { printf("This is parent process "); } printf("pid=%d,ppid=%d,galbol=%d,var=%d ",getpid(),getppid(),galbol,var);//用来测试的变量 return 0; }
运行结果
讲解一下运行结果,首先输出了"Hello Linux!",这是我们放在创建进程之前的一个输出,接着创建一个进程,返回值是ID,则表明当前是父进程,接着输出几个变量,当前进程ID和父进程ID,变量galbol和val,由于在父进程中没有对两个变量的值进行修改,所以两个变量的值未发生改变。那接下来就是子进程,返回值是0,输出自己是子进程,然后执行子进程的代码,可以看到子进程的pid=5263,父进程ID=5983,刚好等于前面父进程的ID,并且修改了两个变量的值。
5.示例代码(二)——这次的demo是在父进程中打开一个文本文件,并进行读操作,再看创建的子进程能否也读取与父进程一样的文本文件。
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<unistd.h> char str[10]; //定义一个数组,用来测试 int main() { FILE *fp; //fp文件指针 pid_t pid; //进程号 pid = fork(); fp = fopen("a.txt","r"); //以只读的方式打开文件 if (fp == NULL) { printf("Open error!"); //如果指针返回的值为NULL,则打开失败 exit(0); } else { if(pid < 0) //fork返回值小于0,创建失败 { printf("Error! "); exit(0); } else if (pid == 0) //fork等于0,返回到新建的子进程 { fread(str,sizeof(char),2,fp); //读两个字符,并输出到屏幕 puts(str); } else //否则返回到父进程 { sleep(2); fseek(fp,2L,SEEK_SET); //定位到子进程读取的两个字符之后开始 fread(str,sizeof(char),3,fp);//读取三个字符 puts(str); } } return 0; }
这是a.txt里面的内容
这是运行结果
可以看到,子进程打开了与父进程一样的文本文件。
6.总结一下:使用fork函数得到的子进程从父进程处继承了整个进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等