一、fork函数
fork()返回值是pid_t类型的
创建成功时子进程返回0,父进程返回子进程id,所以一次fork调用两次返回
二、创建子进程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main(int char, char *argv[])
{
printf("before fork1...\n");
printf("before fork2...\n");
printf("before fork3...\n");
printf("before fork4...\n");
//模拟执行了很多条语句
pid_t pid = fork();
if(pid == -1)//没有创建成功就返回-1
{
perror("fork error");
exit(1);
}else if(pid == 0){//说明是子进程
printf("child is created\n");
}else if(pid >0)//是父进程,父进程返回子进程的pid
{
printf("parent process: my child is %d\n", pid);
}
printf("========end of file\n");
return 0;
}
结果👇
要注意的奇怪的地方:首先先返回父进程的打印,再返回子进程的打印,最后printf("========end of file\n");
运行了两次
原因: 这是因为fork就相当于是拷贝,所有父进程有的代码子进程也有,只不过在fork之前的代码子进程没有机会去运行,而剩余的就会执行两次,一次父进程一次子进程,另外,从父子进程printf是不是可以推测出先执行子进程再执行父进程? 不,父子进程的执行顺序是随机的,一般推荐子进程优先执行
三、getpid和getppid:获取自己pid和获取父进程pid
四、循环创建n个子进程
要求子进程打印出自己是第几个被创建的。//父进程不是打印一次就好了,当前的每一个进程在下次打印的时候都会去fork即创建子进程成为父进程,那么这就涉及到了当前的父子进程谁先去创建呢=====》简单就是不让子进程创建进程,只让父进程去创建,这样只需要一个loop加一个判断就可以了
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main(int char, char *argv[])
{
int i;
for(i = 0; i<5; ++i)
{
if(fork() == 0)//如果是子进程就跳出循环
break;
}
if(i == 5)
printf("i am parent\n");
else printf("i am %dth child \n",i+1)
return 0;
}
//解释一下代码:
首先:if(fork() == 0) break;
因为fork会有两次返回,所有的子进程就直接退出没有机会去创建下一个子进程,只有父进程要下一次循环中,
再就到判读语句中,所有循环结束创建了子进程0~4,5是父进程自己,一一判断即可,如果想要顺序输出,可以加入sleep(i)
函数👇
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h
int main(int argc, int *argv[])
{
int i;
for(i = 0; i<5 ++i)
{
if(fork()==0)
break;//子进程都退出,父进程继续创建子进程
}//创建完毕,现在是0~4子进程
if(i ==5)
{
sleep(5);
printf("i am the parent \n");
}else
{
sleep(i);
printf("i am the %dth child\n", i+1);
}
return 0;
}
五、父子进程共享:读时共享,写时复制
刚fork之后:
**父子进程相同处:**全局变量、data、.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式……
不同处: 进程ID、 fork()返回值、父进程ID、进程运行时间、闹钟(定时器)、未决信号集
似乎,子进程复制了父进程0-3G用户空间内容以及父进程的PCB,但pid不同。事实真的是每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份,然后再映射到物理内存吗?
当然不是,并不是每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份过去,父子进程间遵循着读时共享写时复制的原则,这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销
注意看第22行,如果注释掉第22行,子进程就只进行了读操作,读的就还是fork时的结果,子进程返回的是100;如果去掉注释,那么子进程就进行了写操作,这时返回的结果是200。所以父子进程对全局变量读时复制写时共享
【重点:】父子进程共享:1. 文件描述符(打开文件的结构体),2. mmap建立的映射区(之后进程间通信详解)
另外,注意fork之后父进程先执行还是子进程先执行时不确定的,取决于内核所使用的调度算法