关于子进程回收的方法详解:
以TCPServ 服务程序来说:
1)父进程:负责系统初始化,以及监听(listen),接受连接请求(accept);其中accept 默认阻塞调用。
2)每接受一个连接请求,动态新建(fork)一个子进程,任务完成或客户端断开,服务子进程需要退
出并收回系统资源。
3)根据linux的设计子进程的收回需要父进程参与(wait调用),而此时附进程主要服务工作在监听
客户端连接请求,同时阻塞在(accept)调用,所以父进程自身是无法"分身"去做子进程回收调用。
4)现在的问题在于如何找到父进程的一个服务入口来完成进程回收调用。
方法有2:
1)利用信号机制,由操作系统产生软中断进入进程信号服务程序,执行子进程的回收调用。
在父进程(signal/sigaction)注册SIGCHILD信号,在信号处理函数中执行waitpid调用。
疑问:信号机制可靠吗?特别是应对大频率高负载的动态请求响应场景。
2)在父进程中启动一个新的独立线程pthread,专门负责执行回收调用,此时使用wait阻塞
调用比较合适,如果使用waitpid需要不停的循环等待。注意没有子进程时wait调用立即
返回失败。(父进程在fork子进程时候,可以做一个登记).
5)在很多情况下可以考虑用多线程并发代替多进程并发,省去了很多问题。
1、基础知识
程序:存放在磁盘上的指令和数据的有序集合(文件),静态的。
进程:执行一个程序所分配的资源的总称,
进程是程序的一次执行过程,
动态的,包括创建、调度、执行和消亡。
进程内容:
进程控制块(pcb):进程标识(PID)
进程用户
进程状态、优先级
文件描述符表
进程类型:交互进程、批处理进程、守护进程
交互进程:在shell下启动。可以在前台运行,也可以在后台运行。
批处理进程:和在终端无关,被提交到一个作业队列中,以便顺序执行。
守护进程:和终端无关,一直在后台运行。
进程状态:运行态、等待态、停止态、死亡态
运行态:进程正在运行,或者准备运行。
等待态:进程在等待一个事件的发生或某种系统资源。(可中断,不可中断)
停止态:进程被中止,收到信号后可继续运行。
死亡态:已终止的进程,但pcb没有被释放。
查看进程信息:
ps 查看系统进程快照
top 查看进程动态信息
/proc 查看进程详细信息
进程相关命令:
nice 按用户指定的优先级运行进程
renice 改变正在运行进程的优先级
jobs 查看后台进程
bg 将挂起的进程在后台运行
fg 把后台运行的进程放到前台运行
2、进程相关函数
进程创建:
#include<unistd.h>
pid_t fork(void);
注:创建新的进程,失败时返回-1;
成功时父进程返回子进程的进程号,子进程返回0;
通过fork的返回值区分父进程和子进程。
例子:
pid_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
return -1;
}
else if (pid == 0) {
printf(“child process : my pid is %d\n”, getpid());
}
else {
printf(“parent process : my pid is %d\n”, getpid());
}
注:子进程继承了父进程的内容
父子进程有独立的地址空间,互不影响
若父进程先结束:
子进程成为孤儿进程,被init进程收养
子进程变成后台进程
若子进程先结束:
父进程如果没有及时回收,子进程变成僵尸进程
exec函数族:
进程调用exec函数族执行某个程序
进程当前内容被指定的程序替换
实现让父子进程执行不同的程序
1、父进程创建子进程
2、子进程调用exec函数族
3、父进程不受影响
相关函数:
#include <unistd.h>
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
注:成功时执行指定的程序;失败时返回EOF
path 执行的程序名称,包含路径
arg… 传递给执行的程序的参数列表
file 执行的程序的名称,在PATH中查找
#include <unistd.h>
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
注:成功时执行指定的程序;失败时返回EOF
arg… 封装成指针数组的形式
#include <stdlib.h>
int system(const char *command);
注:成功时返回命令command的返回值;失败时返回EOF
当前进程等待command执行结束后才继续执行
进程回收:
子进程结束时由父进程回收
孤儿进程由init进程回收
若没有及时回收会出现僵尸进程
#include <unistd.h>
pid_t wait(int *status);
注:成功时返回回收的子进程的进程号;失败时返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,哪个先结束就先回收
status 指定保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值
wait工作原理:
---子进程结束后,系统向其父进程发送SIGCHILD信号,
---父进程调用wait函数后阻塞,
---父进程被SIGCHILD信号唤醒然后去回收僵尸子进程,
---若父进程没有子进程则wait函数返回错误,
---故在父进程中将SIGCHILD信号响应方式设置为:signal(SIGCHILD, SIG_IGN);(该函数详情见进程专题二的信号)
进程返回值和结束方式:
子进程通过exit / _exit / return 返回某个值(0-255)
父进程调用wait(&status) 回收
WIFEXITED(status)----------判断子进程是否正常结束
WEXITSTATUS(status)-----获取子进程返回值
WIFSIGNALED(status)-----判断子进程是否被信号结束
WTERMSIG(status)----------获取结束子进程的信号类型
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int option);
注:成功时返回回收的子进程的pid或0;失败时返回EOF
pid可用于指定回收哪个子进程或任意子进程(-1表示任意子进程)
status指定用于保存子进程返回值和结束方式的地址
option指定回收方式,0 或 WNOHANG
(waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。使用while来轮询)
守护进程:
守护进程(Daemon)是Linux三种进程类型之一
通常在系统启动时运行,系统关闭时结束
Linux系统中大量使用,很多服务程序以守护进程形式运行
守护进程特点:
始终在后台运行
独立于任何终端
周期性的执行某种任务或等待处理特定事件
注:Linux以会话(session)、进程组的方式管理进程
每个进程属于一个进程组
会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话
终端关闭时,所有相关进程会被结束
守护进程创建(一):创建子进程,父进程退出
注:子进程变成孤儿进程,被init进程收养
子进程在后台运行
守护进程创建(二):子进程创建新会话
注:子进程成为新的会话组长
子进程脱离原先的终端
守护进程创建(三):更改当前工作目录
注:守护进程一直在后台运行,其工作目录不能被卸载
重新设定当前工作目录cwd
守护进程创建(四):重设文件权限掩码
注:文件权限掩码设置为0
只影响当前进程
守护进程创建(五):关闭打开的文件描述符
注:关闭所有从父进程继承的打开文件
已脱离终端,stdin / stdout / stderr无法再使用
示例1:
示例2: