1.终端登录
登录过程:内核--->init[进程ID=1]--->(fork)init--->(exec)getty--->(exec)--->login
登录结束过程:进程1=init->登录shell<->终端设备驱动程序<->使用终端的用户
2.网络登录
如下图
3.进程组
1).每个进程属于一个进程组,这个进程组从同样的终端获得信号
函数getpgrp返回调用进程的进程组ID,结果也用pid_t表示:
#include <unistd.h> pid_t getpgrp(void); //返回调用进程的进程组ID。
2)SUS定义getpgid函数作为一个XSI扩展来效仿1)中函数的行为
#include <unistd.h> pid_t getpgid(pid_t pid); //成功返回进程组ID,错误返回-1
如果pid为0,返回调用进程的进程组ID。因而getpgid(0)等价于getpgrp();
3).每个进程组可以有一个进程组长。这个组长和它的进程组ID相等的进程ID于来标识(Leader的ProcessID = Process Group ID)。可能一个进程组长会创建一个进程组、创建这个组的进程,然后终止。只要至少有一个进程在这个组里,这个进程组便仍然存在,不管进程组长是否终止。一个进程加入一个存在的进程组或创建一个新的进程组,通过调用setpgid:
#include <unistd.h> int setpgid(pid_t pid, pid_t pgid); //成功返回0,错误返回-1
setpgid函数将pid进程的进程组ID设置为pgid。如果两个参数相等,则由pid指定的进程成为进程组长。如果pid为0,那么使用调用者的进程ID。同样,如果pgid为0,由pid指定的进程ID作为进程组ID被使用。
PS:一般使进程设置其子进程的进程组ID,并且使子进程设置其中经的进程组ID
4.会话
会话是一个或多个进程组的集合,下面一个会话中有三个进程组
进程调用setsid函数来建立一个新的会话
#include <unistd.h> pid_t setsid(void); //返回进程组ID;错误返回-1
如果调用进程不是一个进程组长,那么这个函数创建一个新的会话。有三件事发生:
1).该进程变成新会话的会话首进程。(会话首进程是创建该会话的进程。)该进程是这个新会话里的唯一进程。
2).该进程变为一个新进程组的进程组长。新进程组ID就是该调用进程的进程ID。
3).该进程没有控制终端。如果进程在调用setsid之前有一个控制终端,那么这个关联被中断。
PS:如果调用者已经是一个进程组长,那么这个函数返回一个错误。为了保证不出现这种情况,通常的做法是调用fork,然后使父进程终止,而让子进程继续。
getsid函数返回一个进程的会话领导的进程组ID
#include <unistd.h> pid_t getsid(pid_t pid); //返回会话领导的进程组ID,错误返回-1
如果pid为0,getsid返回调用进程的会话领导的进程组ID。由于出于安全原因考虑,一些实现可能有如下限制:如果pid不属于调用者所在的会话,调用进程不能得到会话领导的进程组ID。
5.控制终端
会话和进程组有几个其它的特性::
1).会话可以有单一的控制终端。这通常是在登录到其上的终端设备(在终端登录的情况下)或者伪终端设备(在网络登录的情况下)。
2).建立与控制终端连接的会话首进程被称为控制进程。
3).一个会话里的几个进程组可以被分为一个前台进程组和一个或多个后台进程组。
4).如果一个会话有一个控制终端,那么它有一个前台进程组,而在这个会话里的所有其它进程组都是后台进程组。
5).每当我们输入终端的中断键(经常是DELETE或Control-C),就会将中断信号发送给前台进程组的所有进程。
6).无论何时我们输入终端的退出键(经是Control+),就会将退出信号发送给前台进程组的所有进程。
7).如终端接口检测到调制解调器或者网络连接断开连接,则将挂起信号发送给控制进程(会话首进程)。
6.tcgetpgrp、tcsetpgrp和tcgetsid函数
需要一种方法来告诉内核哪个进程组是前台进程组,这样终端设备驱动程序卡就能了解终端输入和终端产生的信号到何处。
#include <unistd.h> pid_t tcgetpgrp(int filedes); //返回前台进程组的进程组ID;错误返回-1 int tcsetpgrp(int filedes, pid_t pgrpid); //返回0,错误返回-1
函数tcgetpgrp返回和在filedes上打开的终端相关联的前台进程组的进程组ID。如果进程有一个控制终端,进程可以调用tcsetpgrp来设置前台进程组ID给pgrpid。
SUS定义了一个XSI扩展,被称为tcgetsid,来允许一个应用程序来得到会话领导的进程组ID,给定一个控制TTY的文件描述符。
#include <termios.h> pid_t tcgetsid(int filedes); //返回会话领导的进程组ID。错误返回-1
需要管理控制终端的应用程序可以使用tcgetsid来标识控制终端会话首进程的会话ID(它等价于会话首进程的进程组ID)。
7.作业控制
作业控制要求三种形式的支持:
(1) 支持作业控制的shell。
(2) 内核中的终端驱动程序必须支持作业控制。
(3) 必须提供对某些作业控制信号的支持
8.shell执行程序
这里仅给出实例 详见APUE
shell命令:
ps -o pid,ppid,pgid,sid,comm | cat1 | cat2
其输出:
PID PPID PGID SID COMMAND
949 947 949 949 sh
1888 949 949 949 cat2
1889 1823 949 949 ps
1890 1988 949 949 cat1
该管道中的最后一个进程是shell的子进程,而执行管道中其他命令的进程则是该最后进程的子进程
9.孤儿进程组
一个父进程已终止的进程称为孤儿进程(orphan process),这种进程由init进程收养。