1【进程号PID】
每个进程都由一个进程号来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用.
进程号(PID): 标识进程的一个非负整型数
父进程号(PPID):父进程号
进程组号(PGID): 进程组是一个或多个进程的集合。
1、获取进程号的函数
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:
获取本进程号(PID)
参数:
无
返回值:
本进程号
2、获取父进程的ID
#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);
功能:
获取调用此函数的进程的父进程号(PPID)
参数:
无
返回值:
调用此函数的进程的父进程号(PPID)
3、获取进程组的ID
#include<sys/types.h>
#include<unistd.h>
pid_t getpgid(pid_t pid);
功能:
获取进程组号(PGID)
参数:
pid:进程号
返回值:
参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号
2【创建进程fork】
1、fork函数
系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型.
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
功能:
用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。
参数:
无
返回值:
成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为整型。失败:返回-1。
失败的两个主要原因是:
1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。
2)系统内存不足,这时 errno 的值被设置为 ENOMEM
2、fork出来的子进程和父进程之间的关系
使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间。 地址空间: 包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。 子进程所独有的只有它的进程号,计时器等。因此,使用fork函数的代价是很大的.
3【特殊的进程】
1、孤儿进程(无危害)
父进程先结束、子进程就是孤儿进程,会被1号进程接管(1号进程负责给子进程回收资源)
2、僵尸进程(有害)
子进程结束,父进程没有回收子进程资源(PCB),子进程就是僵尸进程。
3、守护进程
守护进程 是脱离终端的 孤儿进程。在后台运行。为特殊服务存在的。(一般用于服务器)
4【父进程回收子进程的资源】
在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等)
父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。wait、waitpid基本上都是在父进程调用
1、wait函数
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
功能:
等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
参数:
status : 进程退出时的状态信息。
返回值:
成功:已经结束子进程的进程号 失败: -1
注意:
wait阻塞若调用进程没有子进程,该函数立即返回子进程已经结束,该函数同样会立即返回,
并且会回收那个早已结束进程的资源
状态值:
WIFEXITED(status) 如果子进程是正常终止的,取出的字段值非零。
WEXITSTATUS(status) 返回子进程的退出状态,退出状态保存在status变量的8~16位
2、waitpid函数
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
pid : 参数 pid 的值有以下几种类型:
pid>0 等待进程ID等于pid的子进程。
pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等
待它。
pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。
status : 进程退出时的状态信息。和 wait() 用法一样。
options : options 提供了一些额外的选项来控制 waitpid()。
0:同 wait(),阻塞父进程,等待子进程退出。
WNOHANG:没有任何已经结束的子进程,则立即返回。
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结
束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到)
返回值:
waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:
1) 当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
2) 如果设置了选项 WNOHANG,而调用中 waitpid()还有子进程在运行,且没有子进程退出,返回0; 父
进程的所有子进程都已经退出了返回-1;返回>0表示等到一个子进程退出
3) 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的 子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid()就会出错返回,这时 errno 被
置为 ECHILD
5【进程的补充】
1、终端
用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(Controlling Terminal),进程中,控制终端是保存在PCB中的信息,而fork会复制PCB中的信息,因此由Shell进程启动的其它进程的控制终端也是这个终端
#include<unistd.h>
char *ttyname(int fd);
功能:
由文件描述符查出对应的文件名
参数:
fd:文件描述符
返回值:
成功:终端名
失败:NULL
2、进程组
多个进程的集合
当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。
进程组ID为第一个进程ID(组长进程):
进程ID和进程组ID相同的进程就是 组长进程。
可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。 进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
#include<unistd.h>
pid_t getpgrp(void); /*POSIX.1version*/
pid_t getpgid(pid_tpid);
功能:
获取当前进程的进程组ID
获取指定进程的进程组ID
参数:
无
pid:进程号,如果pid = 0,那么该函数作用和getpgrp一样
返回值:
总是返回调用者的进程组ID
int setpgid(pid_t pid,pid_t pgid)
功能:
改变进程默认所属的进程组。通常可用来加入一个现有的进程组或创建一个新进程组。
参数:
将参1对应的进程,加入参2对应的进程组中
返回值:
成功:0失败:-1
3、会话
会话是一个或多个进程组的集合。 一个会话可以有一个控制终端。
如果进程ID==进程组ID==会话ID 那么该进程为会话首进程。
创建会话的步骤:
1) 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
2) 该调用进程是组长进程,则出错返回 。
3) 该进程成为一个新进程组的组长进程
4) 需有root权限(ubuntu不需要)
5) 新会话丢弃原有的控制终端,该会话没有控制终端
6) 建立新会话时,先调用fork, 父进程终止,子进程调用setsid
#include<unistd.h>
pid_t getsid(pid_t pid);
功能:
获取进程所属的会话ID
参数:
pid:进程号,pid为0表示查看当前进程session ID
返回值:
成功:返回调用进程的会话ID
失败:-1
#include<unistd.h>
pid_t setsid(void);
功能:
创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。调用了setsid函数的进程,既是
新的会长,也是新的组长。
参数:无
返回值:
成功:返回调用进程的会话ID
失败:-1
网友评论