一 进程创建
1、fork函数
fork函数很重要,函数功能是:从已存在的进程中创建一个新进程,新创建出的进程为子进程,而原进程为父进程。
#include<unistd.h>
pid_t fork(void);
//返回值:子进程中返回0,父进程返回子进程id,出错返回-1
分配新的内存块和内核数据结构给子进程
将父进程部分数据结构内容拷贝至子进程
添加子进程到系统进程列表中
fork()返回,开始调度器调度
2、代码实现
运行:
注意:
打印了一次Before,两次After,原因在于fork之前父进程独立执行,fork之后,父子两个执行流分别执行,fork之后谁先执行由操作系统调度器决定。
fork函数,子进程返回0,父进程返回子进程的pid。
3、fork用法
父进程希望拷贝自己,使得父子进程同时执行不同的代码段,比如:父进程等待客户端请求,子进程处理请求;
一个进程执行不同的程序,例如:子进程从fork返回后,调用exec函数。
fork调用失败原因:
系统中进程太多
内存不够
4、vfork函数
vfork也是用来创建子进程
vfork用于创建一个子进程,而子进程和父进程共享地址空间,也就是共用虚拟地址空间,fork的子进程具有独立的地址空间
vfork保证子进程先运行,父进程挂起,直到子进程结束,父进程才执行,只有调用exec或exit之后父进程才可能被调度运行
ps 相当于任务管理器,从pcb读取信息
ps -aux getpid/getppid
5、代码实现
运行结果:
二 进程终止
1、进程退出场景
代码运行完毕,结果正确
代码运行完毕,结果不正确
异常退出
(往往因为收到信号才退出)
2、进程退出的方法
正常终止
1、从main返回
2、调用exit
3、_exit异常终止
ctrl +c,信号终止
3、_exit函数
#include<unistd.h>
void _exit(int status);
//参数:status定义了进程终止状态,父进程通过wait来获取该值
虽然status是int类型,但是仅仅低八位被父进程所用,所以_exit(-1)时,在终端执行$?发现返回值为255。
4、exit函数
#incldue<unistd.h>
void exit(int status);
执行exit(n) 相当于return (n),因为调用main的运行函数会将main的返回值当做exit的参数。
三 进程等待
1、为什么要进程等待
子进程退出,父进程不理,就可能造成僵尸进程的问题,进而内存泄漏
进程一旦变成僵尸进程,kill -9也无能为力
父进程派给子进程的任务完成的结果
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
2、wait方法等待
#include<sys/types.h>
#incldue<sys/wait.h>
pid_t wait(int *status)
//返回值:
//成功返回被等待的进程PID,失败返回-1。
//父进程有多个子进程,只有返回PID,才能准确知道是哪个子进程。
//参数:
//输出型参数,获取子进程退出状态,不关心可以设置成NULL
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
3、waitpid方法
pid_t waitpid(pid_t pid,int *status,int options);
从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数:
pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用”|”运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);
如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。
看到这里,聪明的读者可能已经看出端倪了:wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:
static inline pid_t wait(int * wait_stat)
{
return waitpid(-1,wait_stat,0);
}
返回值和错误
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
1、当正常返回的时候,waitpid返回收集到的子进程的进程ID;
2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD。