第12课-多进程程序设计
1. 创建进程
(1) 函数名
fork
(2) 函数原形
pid_t fork(void)
(3) 函数功能
创建一个子进程
(4) 所属头文件
#include<unistd.h>
(5) 返回值
成功:在父进程中返回子进程的pid,在子进程中返回的是0.
失败:-1
(6) 参数说明
无参数
(7) 范例程序
#include<unistd.h>
void main()
{
fork();
printf("program is end ");
exit(0);
}
当我们运行这段程序的时候,我们发现program is end被打印了两次,下面我们来分析一下原因:
我们把上面的程序可以看成是父进程,当父进程运行到fork()函数的时候。会出现一个子进程,子进程的代码与父进程的一样。但是子进程并不是从代码的开头去运行,而是从fork()函数后面的代码开始运行。于是printf()函数一共被调用了两次。我们现在将程序做如下的修改:
#include<unistd.h>
void main()
{
pid_t pid;
pid = fork();
printf("pid is %d ",pid);
exit(0);
}
程序被打印了两次,一次打印的是父进程的pid(是个数),一次打印的是子进程的pid(是0)。
这里面也出现了一个问题。那就是,既然父程序和子程序代码是一样的,我们怎么能让一样的代码去执行不一样的程序呢。实际我们只需要一个if语句就行,程序如下:
#include<unistd.h>
void main()
{
pid_t pid;
pid = fork();
if(pid>0)
{
printf("father process! ");//操作1
exit(0);
}
else
{
printf("son process! "); //操作2
exit(0);
}
}
打印的结果是:father process!
son process!
2. 创建进程2
(1) 函数名
vfork
(2)函数原形
pid_t vfork(void)
(3)函数功能
创建一个子进程并且阻塞父进程
(4)所属头文件
#include<sys/types.h>
#include<unistd.h>
(5)返回值
成功:在父进程中返回子进程的pid,在子进程中返回的是0.
失败:-1
(6)参数说明
无参数
(7)范例程序
#include<unistd.h>
void main()
{
pid_t pid;
pid = vfork();
if(pid>0)
{
printf("father process! ");//操作1
exit(0);
}
else
{
printf("son process! "); //操作2
exit(0);
}
}
打印的结果是:son process!
father process!
我们发现这里的程序和fork()的程序是一样的,但是产生的结果却是相反的。下面我们来分析一下:当我们创建了一个子进程后,到底是父进程还是子进程先运行呢?;而vfork()的作用是让子进程先运行;fork(()函数的作用不一定让那个程序先运行。
下面我们来分析一下fork()函数和vfork()函数的区别:
(1)父进程和子进程运行的顺序不同。
(2)我们来看一下下面两组程序的运行结果
#include<unistd.h>
#include<sys/types.h>
void main()
{
pid_t pid;
int count = 0;
pid = fork();
count++;
printf("count = %d ",count);
exit(0);
}
运行结果:count = 1;
count = 1;
#include<unistd.h>
#include<sys/types.h>
void main()
{
pid_t pid;
int count = 0;
pid = vfork();
count++;
printf("count = %d ",count);
exit(0);
}
运行结果:count = 1;
count = 2;
分析:count是存放在栈中的,当我们执行fork()时,我们将父进程中的栈也复制过来了,也就是子进程拥有自己独立的数据段和堆栈;在我们使用vfork()的时候,子进程是和父进程共享数据段和堆栈的。
3. 进程退出
我们在父进程中退出,可以使用return 0,也可以使用exit(),(exit(0)表示正常退出,exit(1)表示非正常退出);在子进程中我们只能使用exit()进行退出。
(1) 函数名
exit()
(2) 函数原形
exit()
(3) 函数功能
退出进程
(4) 所属头文件
#include<unistd.h>
(5) 返回值
无
(6) 参数说明
无
4. 进程等待
(1) 函数名
wait
(2) 函数原形
pid_t wait(int *status);
(3) 函数功能
挂起调用它的进程,直到其子进程结束。
(4) 所属头文件
#include<sys/types.h>
#include<ysy/wait.h>
(5) 返回值
成功:返回终止的那个子进程的id
失败:-1
(6)参数说明
status:记录子进程的退出状态
(7)范例程序
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
void main()
{
pid_t pid;
pid = fork();
if(pid>0)
{
wait(NULL);
printf("father process! ");//操作1
exit(0);
}
else
{
printf("child process! "); //操作2
exit(0);
}
}
结果显示:chlid process!
father process!
分析:在运行到父进程的时候进入wait(NULL)函数,直到子进程运行结束再运行父进程。
5. 执行程序
执行程序是一个exec族,其中包含:execl,execv,execle,execve,execlp,execvp。我们现在只对其中一个做介绍,因为它们的功能都是差不多的。我们可以自学system这个函数,它的功能和exec类似。
(1) 函数名
execl
(2) 函数原形
int execl(const char *pathname,const char *arg,...);
(3) 函数功能
运行可执行文件
(4) 所属头文件
#include<unistd.h>
(5) 返回值
成功:不返回
失败:有返回
(6) 参数说明
pathname:要运行的可执行文件的路径
arg(包括后面的参数):可执行文件运行需要的参数。
(7) 范例程序
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
void main()
{
pid_t pid;
pid = fork();
if(pid>0)
{
wait(NULL);
printf("father process! ");//操作1
exit(0);
}
else
{
execl("/bin/ls","ls","/home",NULL);
printf("child process! "); //操作2
exit(0);
}
}
运行结果:free test.txt work
father process!
在子进程中的运行结果和我们直接运行ls命令是一样的。但是子进程中的打印程序没有被显示出来。原因是,当我们运行execl()命令时,我们会保留原有的进程,它后面的内容会被覆盖。这也是它和fork()函数的区别。