进程的创建
1.详解进程创建的几类函数的说明:fork,vfork,exec,system?
(1)获取ID
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void) 获取本进程ID。
pid_t getppid(void) 获取父进程ID
(2)启动进程
(a)pid_tfork(void)
功能:创建子进程
fork的奇妙之处在于它被调用一次,却返回两次,
它可能有三种不同的返回值:
0: 子进程
子进程ID(大于0):父进程
-1: 出错
实例:
#include<sys/types.h>
#include<unistd.h>
intmain()
{
pid_t pid;
/*此时仅有一个进程*/
pid=fork();
/*此时已经有两个进程在同时运行*/
if(pid<0)
printf("error in fork!");
else if(pid==0)
printf("I am the childprocess, ID is %d\n",getpid());
else
printf("I am the parentprocess,ID is %d\n",getpid());
}
说明:
v 在pid=fork()之前,只有一个进程在执行,但在这条语句执行之后,就变成两个进程在执行了,这两个进程的共享代码段,将要执行的下一条语句都是if(pid==0).
v 两个进程中,原来就存在的那个进程被称作“父进程”,新出现的那个进程被称作“子进程”,父子进程的区别在于进程标识符(PID)不同.
(b)pid_t vfork(void)
功能:创建子进程
表头文件: #include<unistd.h>
定义函数: pid_t vfork(void);
函数说明:
vvfork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
v子进程不会继承父进程的文件锁定和未处理的信号。
v注意,Linux不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。
(c)exec函数族
exec用被执行的程序替换调用它的程序。
区别:
fork创建一个新的进程,产生一个新的PID。
exec启动一个新程序,替换原有的进程,因此进程的PID不会改变
更多详情见博文——exec函数族
2.举例对比fork,vfork的区别,函数出现位置的不同能否区分代码出现的原因?
区别:
(1)fork:子进程拷贝父进程的数据段
vfork:子进程与父进程共享数据段
(2)fork:父、子进程的执行次序不确定
vfork:子进程先运行,父进程后运行
(a)fork函数
#include<unistd.h>
#include<stdio.h>
intmain(void)
{
pid_t pid;
int count=0;
pid = fork();
count++;
printf( “count = %d\n", count );
return0;
}
输出:
count = 1
count = 1
count++被父进程、子进程一共执行了两次,为什么count的第二次输出为什么不为2?
v 子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。
v 在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0
(b)vfork函数
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t pid;
int count=0;
pid = vfork();
count++;
printf( “count = %d\n", count );
return 0;
}
输出:
count = 1
count = 2
思考:如何新创建一个进程,然后执行一个程序?
进程的等待
1. 僵尸进程的来源?如何避免?
来源:
如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。
v 父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。
v 子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。
v 因此一个僵尸存在于其终止到父进程调用wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。
避免:
一、让僵尸进程的父进程来回收,父进程每隔一段时间来查询子进程是否结束并回收,调用wait()或者waitpid(),通知内核释放僵尸进程。
二、采用信号SIGCHLD通知处理,并在信号处理程序中调用wait函数。
三、让僵尸进程变成孤儿进程,由init回收,就是让父亲先死。
2. wait和waitpid的用法,两者什么时候是相等的?掌握不同情况下的参数和返回值
wait
函数的作用: 进程的等待,阻塞进程,等待某个子进程退出;
函数的原型: pid_t wait(int *status);
返回值:成功返回子进程PID,出错-1;
waitpid
函数的作用:等待退出,等待信号,或者指定的进程结束
函数的原型:pid_t waitpid(pid_t pid , int *status, intoptions);
函数的参数:
pid <-1 :等待进程的Pid绝对值的任何的子进程;
pid=-1, 任何子进程,---等于wait;
pid=0,
pid >0, 等待子进程为pid的子进程退出
options:
WNOHANG:如果没有子进程退出,马上返回不等待
WUNTRACED:如果子进程进入暂停执行情况,马上返回,
返回值:如果执行成功返回的是子进程的PID,失败-1;
如果使用WNOHANG的时候,没有子进程退出,
进程的退出
1. 掌握exit和_exit的异同点,在内核中有什么区别?举例论证
exit,_exit用于终止进程
区别:
_exit:直接使进程停止,清除其使用的内存,并清除缓冲区中内容
exit与 _exit的区别:在停止进程之前,要检查文件的打开情况,并把文件缓冲区中的内容写回文件才停止进程。
exit():正常结束进程
表头文件: #include<stdlib.h>
定义函数: void exit(int status);
函数说明:
exit()用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。
_exit():结束进程执行
表头文件: #include<unistd.h>
定义函数: void _exit(int status);
函数说明:
_exit()用来立刻结束目前进程的执行,并把参数status返回给父进程,并关闭未关闭的文件。
此函数调用后不会返回,并且会传递SIGCHLD信号给父进程,父进程可以由wait函数取得子进程结束状态。
2. 掌握return,exit等退出进程的异同点
return与exit()均可用于函数的返回,但return只是本函数的返回,而exit()则是整个程序的退出。