开发者学堂课程【物联网开发- Linux 高级程序设计全套视频:创建进程 fork 函数】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/660/detail/10985
创建进程 fork 函数
内容简介:
一、创建进程
二、常用 <!DOCTYPE> 文档类型
三、应用实例
一、创建进程
1、在Linux环境下创建进程
接下来讲一下进程的创建fork,要想在程序当中再去创建一个进程,就用fork函数。
先看一下在Linux操作系统下创建进程的方法,
如下所示:
#include<sys/types.h>
#include<unistd.h>
pid t fork(void);
pid t vfork(void);
主要就是以上两个函数,一个是 fork,一个是 vfork,这两个函数都是创建进程的,但创建的进程的效果不太一样,讲完了这两个函数之后就知道它们的区别了。
2、创建新进程
看fork函数它没有参数,返回值有一个进程号,fork函数用于从一个已经存在的进程当中创建一个新的进程,新建成为子进程,原来的进程被称为父进程。
注意此函数和其他函数不太一样,其他函数只有一个返回值,而fork函数不太一样,调一次fork它有两个返回值,因为现在有两个进程了,在子进程当中它返回值为零,在父进程当中返回值为子进程的ID,进程号都是大于等于零的,然后出错了就返回负一,注意使用fork得到的子进程是父进程的一个复制品,是克隆,然后注意它从父进程当中继承了整个地址空间。
父进程有什么样的变量,子进程就有什么样的变量,父进程有什么样的代码,子进程就有什么样的代码。
3、地址空间
那么地址空间包括什么?
进程的上下文、进程的堆栈、进程当中打开的文件描述符、信号的控制设定(后边再讲信号,比如它的父进程设定一下收到哪一个信号,去干什么事,就像按钮一样,设置一下当按钮被按下的时候干什么事一样,是类似的意思)、进程的优先级、进程组号、子进程所独有的只有它的进程号,计时器等是它独有的。
二、fork 函数及进程
1、fork 函数执行结果
使用fork函数的代价是很大的,因为它是将父进程复制了一份,父进程是有堆、有栈、有BBS区、有数据区、有代码区,等fork之后,子进程是完全复制过来的,它会创建这一块内存,然后将父进程的数据复制过来,
如下图所示:
2、fork 函数创建多任务过程
(1)输入代码
接下来写一下fork的过程,讲述一下它创建多任务的原理,代码如下:
//父进程
int main()
{
pid_t pid;
pid=fork();
//.....
if(pid<0)
{
//创建进程失败
}
else if(pid==0)
{
}
else
{
//父进程将来执行此代码
}
//公共代码
return 0;
}
//子进程
int main()
{
pid_t pid;
pid=fork();
//.....
if(pid<0)
{
//创建进程失败
}
else if(pid==0)
{
}
else
{
//父进程将来执行此代码
}
//公共代码
return 0;
}
(2)代码说明
写了一个main、写了一个代码之后编译运行,然后它就开始执行,点斜杠运行的时候便启动一个进程,那个进程是父进程,fork是创建一个子进程,创建子进程的时候,父进程当中有什么代码,子进程就有什么代码,那么父进程创造子进程,父子进程的代码是完全一样的,那么创建子进程的目的是什么?
让父进程干一个事情,子进程干另外一个事情,父子进程干不一样的事情,要不然创建子进程是没用的,那代码完全一样,怎样让父子进程执行不一样的代码?
就根据返回值来定的,子进程当中它的fork的返回值给变量赋值为零,子进程当中它的返回值是为零的,父进程返回值是子进程的进程号,所以说其是父进程,在父进程pid是大于零的,子进程号是大于零的,在子进程当中pid等于零,所以可以通过pid的值不一样来执行不同的代码,虽然父子进程的代码一样,但是pid的值是不一样的,可以通过pid的值让它执行不同的代码,如果pid小于零代表创建进程失败,然后做处理就可以了。
可以看到上述代码中子进程的代码和父进程的代码是一样的,先执行父进程,然后就创建子进程,父进程生了儿子之后,父进程再往下执行,子进程当中pid等于零,子进程当中fork的返回值等于零,注意fork只调一次,在子进程当中子进程是从第八行往后执行的,它不可能从第四行往后走,如果创建了子进程,子进程从main往后执行,那么子进程就又去fork,就又创建了子进程,子进程再去刻意创建子进程,那子子孙孙无穷无尽了,就好比克隆羊多莉,中国有克隆牛,那么克隆羊和克隆牛是从什么时候执行的?
从创建它之后去执行的,在生它之前的那些代码是不会执行的,虽然两头羊是一模一样的基因资源,但是新的羊应该是从生到了它之后再去执行的,即子进程是从第六行之后执行的,继续看父进程执行完else之后再往下执行,直到return 0。
公共代码是父进程和子进程都要执行的,也就是说父进程创建完子进程之后,父子进程的代码完全一样,父进程接下来执行else里边的代码,然后执行公共代码,而子进程从fork之后再执行,执行pid等于零的代码,然后再去执行公共代码。这样虽然父子进程的代码一样,但它执行的代码块不一样,就相当于进程干一个事,子进程干一个事,这就实现了多任务。
三、举例子说明
1、输入代码
接下来举例子:先输入以下代码:
[01_day]1s
aaa.txt dest.txt my_cp my_cp.c pid pid.c read read.c test.txt wanqi.txt
[01_day]vi fork.c
执行之后换个平台继续输入以下代码:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid=fork();
if(pid<0)
{
perror(“fork”);
return 0;
}
else if(pid == 0)
{
while(1)
{
printf(“this is son process\n”);
sleep(1);
}
}
else
{
while(1)
{
printf(“this is father process\n”);
sleep(1);
}
}
return 0;
}
现在让父进程循环打印、子进程循环打印,以前没有多任务的时候,只有一个while在循环,另外一个while不会执行到,现在有两个while同时在运行,子进程在循环干什么事?
每隔一秒打印一句话,父进程当中也给它一个循环,这两个进程是独立的,各干各的事。
2、代码实现
将以上代码执行,结果如下:
[01_day]1s
aaa.txt dest.txt my_cp my_cp.c pid pid.c read read.c test.txt wanqi.txt
[01_day]vi fork.c
[01_day]gcc fork.c -o fork
[01_day]./fork
this is father process
this is son process
this is father process
this is son process
3、代码说明
可以看到两个循环都在执行,注意它是两个独立的进程,操作系统是调度这两个进程,意思是先调到父进程,可以看到每次调的都是父进程,这是根据操作系统的调度算法来确定的,
注意:
一旦创建一个进程之后,到底是先调度哪一个进程取决于操作系统,这是不确定的,要是非得让某一个进程先执行,就得用同步互斥了。可以看到现在是两个进程在并发的在执行,两个while都在执行,这就实现了多任务,父进程干一个事情,子进程干一个事情,这是第一个多任务的程序,所以需要清楚多任务程序的框架,代码一模一样,父进程在哪执行、执行哪一块代码、子进程执行哪一块代码,因为父子进程的代码是完全一样的。
另外需要注意:
子进程是从fork之后执行的,不是从main函数开始执行的,就是生它的地方往后执行,到这里fork就已经讲完了,大家可以自己将上文中的代码实现一下,实现一个并发的程序,看看是不是两个进程同时在运行。