进程号的获取
在Linux运行一个程序时,系统会分配相应的进程ID,getpid()函数返回值为本程序的运行时的进程号,getppid()函数返回值为创建此进程的父进程的进程号,进程ID类似文件描述符,方便系统管理调度。
为什么要创建子进程?
我认为,主要为了提高运行效率,创建子进程才能多道程序并发执行,进程是资源分配的单位,是运行的程序。既然是运行的程序,一个进程自然只能代表一个程序,多道程序设计自然而然就有了多进程的概念。举个例子,多进程(线程)下载,我们可以给一个需要下载的资源分片,多个进程从不同的片分时下载,这样就提高了下载速度,因为对一个程序分配的更多的资源,你试试开迅雷的时候打开个网页,保证你觉得奇卡无比,因为网络带宽(资源)被迅雷的多个进程占用了。其实在本地的多进程程序并不多见,比如word算是个典型的多进程程序,有个进程接受你的键盘输入,有拼写检查进程,有显示进程等等。大多数都用到网络上了,比如服务器。一台服务器要在“同一时间”处理来自很多客户端的请求,这就必须使用多进程。
进程创建函数fork()
fork()函数没有参数,在子进程中返回值为0;在父进程中返回值为父进程所创建子进程的进程号,可以当做链表来理解
父进程->子进程->NULL;如果进程创建失败则返回一个负数。
示例代码如下:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
int main ()
{
pid_t fpid; //fpid表示fork函数返回的值
fpid=fork();
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf(" child process id is %d\n",getpid());
printf("child's father process id is %d\n",getppid());
}
else {
printf("father's process id is %d\n",getpid());
} return 0;
}
程序运行结果如下所示:
[root@localhost fork]# gcc fork.c -o run
[root@localhost fork]# ./run
father's process id is 4101
child process id is 4102
child's father process id is 4101
打印结果可以看出
父进程ID为4101 子进程ID为4102 父进程指向子进程。
如果子进程输出自身ID后,添加延时或者执行其它任务之后,再次打印父进程ID会发现,父进程ID变为1,这是因为,父子进程创建完毕时几乎是“同时”运行,子进程运行时间较长,运行到打印父进程ID时,父进程已经结束,此时子进程变为孤儿进程,由系统进程 1 进行暂时领养。
关于fork 与 vfork区别这里指两个例子
首先是fork()代码如下
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
int main ()
{
pid_t fpid;
int count=0;
fpid=fork();
count++;
printf("count is %d\n",count);
return 0;
}
运行结果如下:
[root@localhost fork]# gcc fork.c -o run
[root@localhost fork]# ./run
count is 1
count is 1
结果显示无论子进程还是父进程,显示结果最终都为1,这个简单的可以说明fork创建的进程虽然代码段子进程与父进程共用,但是数据段并不共用,当父进程创建子进程时,同时将数据“复制”了一份给子进程。两个进程相互独立互不干扰。
那么将上面程序中的fork()换为vfork()代码如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
pid_t fpid;
int count=0;
fpid=vfork();
count++;
printf("count is %d\n",count);
exit(0);
}
再来观察程序运行结果:
[root@localhost fork]# gcc fork.c -o start
[root@localhost fork]# ./start
count is 1
count is 2
结果显示
count 在数据上有了正常叠加,这说明vfork()创建的子进程父进程共享数据段与代码段,父进程创建子进程时不会将数据复制一份给子进程,而是两个进程共享数据段。这里为了简便没有区分父子进程,如果区分,显示结果将是子进程count结果为1,父进程打印结果为2。
除此之外这两个函数在进程执行顺序上有不同,fork()创建子进程后,子进程与父进程的执行顺序是随机的,而vfork()创建子进程后,父进程等待子进程执行完毕后,才来执行父进程。
注意:fork() vfork()两个函数运用时会发现,vfork创建进程结束时调用exit()函数,而不是return()返回值方式结束,若采用return()方式结束进程,运行程序时程序会发生段错误,导致程序不断执行无法正常结束,但是采用fork()创建进程,采用return方式结束却不会发生错误,这是为什么呢?return是语言级别的结束,exit是系统进程调用级别的结束,主函数执行return时会认为程序结束释放程序堆栈空间等数据,exit()函数结束进程,将相关信息返回OS供OS处理下一步任务,还是那一点,fork创建父子进程之后,两个进程完全独立,数据空间互不干扰,进程末尾return()返回以后,系统释放各自任务堆栈空间,父子进程正常结束,不会发生错误。但是vfork()不同,父子进程共享数据段代码段,执行时阻塞掉父进程先执行子进程,若子进程采用return结束进程,函数认为执行完毕,释放任务堆栈等信息,从而导致父进程进程运行参数被破坏,程序无法正常执行,导致段错误的产生(个人理解)。