Unix环境高级编程 读书笔记 第九章 进程关系

终端登录

在UNIX系统中,通常的终端登录流程为:

  1. 系统自举,内核创建ID为1的init进程;
  2. init进程读取类似/etc/ttys文件,对每一个允许登录的终端,init进程调用fork,生成的子进程exec getty程序;
  3. getty程序对终端设备调用open函数,以读写方式打开终端设备,设备被打开后,文件描述符0、1、2被设置到该设备,然后getty等待用户输入用户名;
  4. 当用户输入用户名后,getty根据用户名,调用exec函数执行login程序,getty程序完成任务;
  5. login程序根据用户名,读取用户口令,并与阴影文件中的用户口令字段进行比较,如果用户输入的几次口令均错误,则调用exit(1)返回,父进程init了解到子进程的失败原因后,将再次fork,执行getty,重复以上步骤;
  6. 如果用户口令输入正确,则login程序继续执行;
  7. login程序将当前的工作目录更改为用户的起始目录;
  8. login程序调用chown将该终端所有者更改为登录用户;
  9. login程序将对该终端的访问权限更改为用户读与写;
  10. login程序设置进程的组ID;
  11. login程序初始化环境变量,包括HOME、SHELL、USER、LOGNAME等;
  12. login程序调用该登录用户的shell程序,此时登录用户的shell程序开始执行;
  13. 登录shell读取启动文件,包括.bash_profile、.bash_login等脚本文件,这些文件改变某些环境变量并增加很多环境变量。
  14. 执行完启动文件后,用户得到shell提示符,可以输入命令,使用系统。

进程组

每个进程还属于一个进程组。进程组是一个或者多个进程的集合,通常他们是在同一个作业中结合起来的,同一个进程组中的各个进程接收来自同一个终端的信号。

每个进程组有一个唯一的进程组ID。进程组ID可以存放在pid_t数据类型中。函数getpgrp返回调用进程的进程组ID:

#include <unistd.h>
pid_t getpgrp(void);
/*返回调用进程的进程组ID*/

pid_t getpgid(pid_t pid);
/*若成功,返回进程组ID,若出错,返回-1*/

对于函数getpgid,若入参pid等于0,则返回调用进程的进程组ID。

每个进程组都有一个组长进程。组长进程的进程组ID等于其进程的ID。

进程组的组长进程可以创建一个进程组,创建该进程组中的进程,终止进程。只要在进程组中有一个进程存在,则进程组存在。进程组存在与否与组长进程是否终止无关。

从进程组创建开始,到其中最后一个进程离开为止的时间区间,被称为进程组的生命周期。

进程调用函数setpgid可以加入一个现有的进程组,或者创建一个新的进程组。

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
/*若成功,返回0,若出错,返回-1*/

会话

会话是一个或者多个进程组的集合。
进程调用setsid函数建立一个新的会话。

#include <unistd.h>
pid_t setsid(void);
/*若成功,返回进程组ID,若出错,返回-1*/

如果调用函数的进程不是一个进程组的组长,则此函数会创建一个新会话。该进程变为新会话的会话首进程,该进程成为一个新的进程组的组长进程,该进程没有控制终端。

函数getsid会返回会话首进程的进程组ID:

扫描二维码关注公众号,回复: 5139230 查看本文章
#include <unistd.h>
pid_t getsid(pid_t pid);
/*若成功,返回会话首进程的进程组ID,若出错,返回-1*/

控制终端

会话与进程组具有的其他特性包括:

  1. 一个会话可以有一个控制终端,通常为终端设备或者伪终端设备;
  2. 建立与控制终端连接的会话首进程被称为控制进程;
  3. 一个会话中的几个进程组可以被分为一个前台进程组,和一个或者多个后台进程组;
  4. 如果一个会话有一个控制终端,则其有一个前台进程组,其他进程组为后台进程组;
  5. 键入的终端中断键(ctrl + c),会将中断信号发送至前台进程组的所有进程;
  6. 键入的终端退出键(ctrl + \),会将退出信号发送至前台进程组的所有进程。
  7. 如果终端接口检测到终端断开连接,则将挂断信号发送至会话首进程。

猜你喜欢

转载自blog.csdn.net/jiangzhangha/article/details/85855338