系统调用创建进程

- 指令man 2 fork 调用系统手册认识fork
- fork();函数创建一个子进程
- 头文件#include<unistd.h>
下面使用代码创建一个子进程demo:
int main()
{
printf("fork before process id : %d\n", getpid());
fork();
printf("fork after process id : %d\n", getpid());
return 0;
}

- fork()之后,变成了两个执行流
- fork()函数创建成功,pid返回的是子进程pid的是父进程,pid返回的是0的是子进程。如果返回-1,父进程(也就是准备创建的进程)创建失败
int main()
{
printf("pid: %d---ppid: %d \n", getpid(), getppid());
pid_t id = fork();
if(id == 0)
{
// 子进程
while(true)
{
printf("child process: pid-%d-ppid-%d\n",getpid(),getppid());
sleep(1);
}
}
else if(id > 0)
{
// 父进程
while(true)
{
printf("father process: pid-%d-ppid-%d\n",getpid(),getppid());
sleep(1);
}
}
else
{
// 创建进程失败
printf("create fail: pid-%d-ppid-%d\n",getpid(),getppid());
}
return 0;
}


- 监控脚本:while :; do ps axj | head -1 && ps axj | grep process | grep -v grep ; sleep 1;done
- 子进程的ppid和父进程的pid是相同的
- 可以通过指令创建子进程,也可以通过系统调用fork()创建一个子进程
fork——翻译成中文是分支的意思,fork()之后会变成两个执行流。
问题1:为什么fork要给子进程返回0,给父进程返回子进程pid?
【答】:返回不同的返回值,是为了区分让不同的执行流,执行不同的代码块。一般而言,fork之后的代码父子进程会共享。类似于树的数据结构,父进程可以有多个子进程,每一个子进程都会有自己的pid,fork之后获取对应子进程的pid,是为了父进程找到子进程;而子进程只有唯一的父进程,只需要调用getppid就可以找到自己的父进程。
问题2:一个函数是如何返回两次的?如何理解?
【答】:fork也是一个函数,fork本身一定是在操作系统中实现的。在fork函数里面,操作系统已经创建了新的子进程,而进行到函数的返回值的时候,已经存在两个进程了,也就是说两个进程同时执行了两次return语句。

问题3:一个变量id怎么会有两个不同的内容?
【答】:子进程在创建出来的时候,同样也获得了父进程的代码和数据,因为进程具有独立性,而子进程的数据是有可能会被修改的,所以不能让父进程和子进程共享同一份数据,这里需要说明共享代码和数据并不会影响独立性。理论上来讲,子进程在被创建出来的时候,会拷贝一份父进程的数据,由于父子进程各自独立,当子进程修改数据后,并不会对父进程产生影响。
但是在实际中,为了减少消耗,操作系统让子进程指向父进程的数据,如果子进程需要读取父进程的数据,直接在父进程的数据区读取即可,而当子进程需要修改父进程的数据(独立性)时,操作系统会在内存中重新开辟一块新的区域,去保存这个修改了的数据。这种技术叫做数据层面的写时拷贝。
在任何平台,进程在运行的时候,是具有独立性质的。
fork()函数return语句在执行的时候,就是发生了写时拷贝,也就是获得了两个不同的返回值。
问题4:fork()函数具体做了什么?
【答】:进程 = 内核数据结构 + 代码和数据;fork之后代码是共享的,当代码加载到内存的时候,代码是不允许被修改的,只能修改的是代码的数据。

同样的代码,为什么还需要创建子进程,让子进程和父进程完成相同的代码呢?这是为了让父进程和子进程执行不同的内容。所以需要父和子进程执行不同的代码块,此时就需要fork函数返回不同的返回值,根据返回值的不同,进行不同的执行流。
而fork的本质就是创建一个新的进程,在内存中创建新的PCB,修改PCB(task_struct)的部分属性,让这个task_struct执行父进程的代码和数据。如果不想让父子进程执行相同的执行流,就必须根据fork函数的返回值来选择执行。
问题5:fork创建两个进程后,父子进程谁先运行呢?
【答】:一般而言,一旦一个进程被创建出来,谁先运行是由调度器来决定的。
调度器:对特定数据结构进行查找的一种算法,调度器一般都是公平的,尽可能保持每一个进程的时间片相同。
代码中父进程的父进程

代码中可以通过fork函数创建子进程,而这个代码也是一个进程,这个进程的父进程是bash进程。而这个bash进程在创建子进程的时候,也一定会使用fork这个函数,创建完进程后,父进程执行命令行解释器,子进程执行解析新的命令。

2897

被折叠的 条评论
为什么被折叠?



