在Linux操作系统中我们可以用fork()函数或者vfork()函数来创建进程,创建另一个进程可以实现任务的并发执行、资源的隔离、提高系统的稳定性和安全性,从而提高了系统的性能和效率。
以下是要讨论fork()函数或者vfork()函数来创建进程的区别:
一、fork()
函数原型:pid_t fork(void);
二、、vfork()
函数原型:pid_t vfork(void);
a)共同点:
由函数原型也可看出,两函数除了函数名不相同,其数据类型和形参都是一样的;都具有调用一次返回两个结果的特点(即返回0代表是在子进程中、返回大于0的正整数是在父进程中)。
b)区别:
1、fork()函数创建的子进程,子进程可以独立的被系统调用,父子进程是相互独立的,谁先执行谁先结束都是不确定的;
vfork()函数创建的子进程,则一定是子进程先运行后父进程才能运行。
2、fork()函数创建的子进程会复制父进程的地址空间(包括数据段、堆栈段、I/O流缓冲区、运行的位置) 【注意代码段是父子进程共享的、以及父子进程共享文件表----->在后面详细介绍】,也就是父子进程是相互独立的。
举个栗子:从结果看到子进程修改了a的值,导致父进程的a值也跟着改变了。
vfork()函数创建的子进程,会与父进程共享地址空间,也就是在子进程修改父进程中某个变量的值,父进程中该变量的值也会被修改,所以一般在vfork()函数创建的子进程中通过exec函数族直接启动另一个进程替换自身。当然由于用vfork()函数创建子进程时并不会复制父进程的地址空间,所以vfork()创建子进程的效率会比fork()高。
注意:vfork()创建的子进程退出时需要用exit函数结束,用return结束不了该进程!!!
三、共享文件表:
首先了解这三个名词概念:
1、文件描述符表:
我们都知道文件描述符唯一标识一个打开的文件,那么文件描述符表就是一个进程记录其打开的所有文件的表格。【每个进程都有自己独立的文件描述符表】
2、文件表:
文件表中记录了文件状态标志,当前的偏移量、一个指向i_node表的指针,引用计数等,这里解释以下引用计数:每当一个文件描述符指向该文件表时,引用计数就会+1,如果引用计数为0,那么内核就会删除这个文件表。【所有进程共享这个文件表】
3、i-node表:
记录了文件的长度、文件的访问权限、文件类型、指向文件在磁盘上位置的指针等。【i-node表所有进程共享】
总结:上面说fork()函数创建子进程时,父子进程共享文件表也就是,创建子进程时,子进程会复制父进程的文件描述符表,那么子进程也有父进程所有的文件描述符,如果只有一个父进程+一个子进程,那么此时文件表中的引用计数就应该为2;下面是一个用fork()创建子进程的例子:
首先打开一个a.txt文件,文件里的内容是abcdefg,我们让父进程睡一秒,确保子进程先运行,子进程首先读出了4个字节的内容也就是(abcd),然后子进程关闭了该文件描述符。
父进程运行后,读出(efg),可以知道子进程关闭了该文件描述符后,父进程依然可以读该文件,证明该文件是没有被关闭的(也就是文件表的引用计数并没有减到0),父进程是从该文件第五个位置开始读出内容,证明父子进程确实是共享文件表(因为文件表中记录了文件的偏移量)。