1、进程概念
2、进程的创建
进程在执行过程中,可以通过系统调用的方式创建多个新进程。Linux系统提供了两种创建进程的方式:函数fork()用来创建一个新的进程,该进程几乎是当前进程的一个完全副本;函数exec()用来启动另外的进程以取代当前运行的进程。在这里,本章主要介绍如何利用fork函数创建新进程以及fork函数的工作机制。
1、fork函数说明
fork函数原型:pid_t fork( void );
头文件:<unistd.h>
功能:fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是说两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1。调用失败大多是因为内存不够用了,或者进程太多而系统不允许创建了(受物理内存限制或管理员设定)。
除了fork函数外,还有两个在进程相关程序中常用到的函数,分别为getpid和getppid,说明如下:
1)getpid
函数原型:pid_t getpid( void );
头文件:<unistd.h>
功能:返回当前进程的标识
2)getppid
函数原型:pid_t getppid( void );
头文件:<unistd.h>
功能:返回父进程的标识
从上面fork函数的定义可以看出,这个调用非常简单,简单到连参数都没有,只有一个返回值。
2、fork函数实例程序
下面以一个简单程序说明fork函数的工作机制,源代码文件process_sfork.c存储到"exp1"目录下
#include <stdio.h>
#include <unistd.h>
int main( int argc, char *argv[])
{
pid_t pid;
int var = 0;
pid = fork();
if (pid < 0)
{
printf( "error in fork!\n" );
}
else if (pid == 0)
{
printf("This is the child process, pid is %d.\n", getpid());
var = 100;
}
else
{
printf("This is the parent process, pid is %d.\n", getpid());
var = 50;
}
printf("var is %d.\n",var);
return 0;
}
上述代码的执行结果如下
This is the parent process, pid is 18588.
var is 50.
This is the child process, pid is 18589.
var is 100.
通过var的值可以看出,最后一个printf()的输出是在两个不同的进程中。从上面实验的结果可以看出,fork函数不需要额外的参数去传递一个类似线程的那种主函数。另外,fork函数调用后,执行的就是后续代码,没有任何迹象表明哪些是父进程特有的,哪些是子进程特有的。至于为什么代码能够指向不同分支,只要是因为if语句判断了它的返回值,根据fork返回值判断当前进程是父进程还是子进程。
3、fork函数创建的子进程和父进程的区别
本质来说,fork函数启动一个新的进程,这个进程几乎是当前进程的一个副本:子进程和父进程使用相同的代码段;子进程复制父进程的堆栈段和数据段。这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。它们再交互信息时,只有通过进程间通信来实现。既然它们如此相像,Linux系统又是如何来区分它们的呢?正如前面所描述的,这是由函数的返回值来决定的。对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零。在Linux系统中,用getpid函数就可以看到不同的进程号。对父进程而言,它的进程号是由比它更低层的系统调用赋予的,而对于子进程而言,它的进程号即是fork函数对父进程的返回值。
3、连续调用多次fork函数
为了进一步考察fork函数的工作机制,下面给出了利用for循环实现连续调用两次fork函数的程序,源文件代码process_mfork.c存储在“ch06/exp2”目录下
#include <stdio.h>
#include <unistd.h>
int main( int argc, char *argv[])
{
int i = 0;
int root = 0;
printf("r\t i\t C/P\t ppid\t pid\n");
for(i = 0; i < 2 ; i++){
if(fork() > 0){
printf("%d\t %d\t parent\t %d\t %d\n", root, i, getppid(), getpid());
sleep(1);
}
else
{
root++;
printf("%d\t %d\t child\t %d\t %d\n", root, i, getppid(), getpid());
}
}
return 0;
}
上述实验的执行结果如下:
r i C/P ppid pid
0 0 parent 18396 18653
1 0 child 18653 18654
1 1 parent 18653 18654
2 1 child 18654 18655
0 1 parent 18396 18653
1 1 child 18653 18656
在上述的实验输出结果中,第1列表示当前进程是否为根进程,如果为0表示为根进程,否则为由fork创建的进程;第2列表示for循环中i的值;第3列描述当前进程是父进程还是子进程;第4列ppid表示当前进程的父进程ID;第5列pid表示当前进程ID。
多进程程序开发并不是简单的调用一下fork,产生另一个执行相同任务的进程。多进程程序的目的是创建多个进程协作完成一项任务,以提高计算资源的利用率。