Linux 进程关系

发布于 2023-03-01 20:59:14 字数 4093 浏览 85 评论 0

1 终端登录过程

  1. init 进程针对每个 tty frok 并 exec 一个 getty 子进程
    • init 进程会读取 /etc/rc 中的脚本来启动基本服务
  2. getty 程序在每个tty设备上打开 stdin、stdout、stderr 套接字
  3. getty 输出 login: 等待用户输入用户名、密码
  4. getty exec login 程序,并传递用户名、密码到 login 程序

    execle("/bin/login","login","-p",username,(char*)0,envp);
    
    • 由于exec并不更改进程ID,因此getty,login的进程ID一样,且父进程为都为init,该init进程的PID为`1'
    • 若用户输入几次用户名,密码错误,则login程序调用`exit 1',父进程init收到login退出后会接着创建子进程getty,并重复上面过程.
  5. login将当前工作目录更改为该用户的起始目录
  6. login调用chown改变终端所有权为登录用户
  7. login将对该终端设备的访问权限改为用户可读写
  8. login调用 setgid 和 initgroups 设置进程的组ID
  9. login 用 login 所得到的信息初始化环境变量:起始目录(HOME),shell(SHELL),用户名(USER,LOGNAME),系统默认路径(PATH)
  10. login 进程设置自己的三个用户id为登录用户id,并exec该用户的登录shell
    • shell退出后,init会得到通知,重复上述 fork 并 exec getty 的过程
  11. 登录 shell 读取其配置文件

2 网络登录过程

网络登录过程与终端登录过程不同点在于网络登录过程不过是一种网络服务,系统使用了一种称为伪终端的软件驱动来实现终端操作与网络操作之间的映射

网络服务进程会取代 getty 的作用将 stdin、stdout、stderr 与伪终端相连接,并 exec login 程序,随后的过程与终端登录过程类似

3 进程组

每个进程除了有一个进程ID外,还属于一个进程组.

多个用管道线连接起来的进程属于同一个进程组. 例如:

ls |more

中 ls 与 more 属于同一个进程组

同一进程组的多了进程会接收到来自同一终端的各种信号,即在终端中输入的信号会被所有进程都接收到.

每个进程组都有一个唯一的进程组ID,并也用pid_t数据类型存储

每个进程组都可以有一个组长进程,其进程ID等于进程组ID

组长进程可以创建一个进程组,创建进程组中的进程.

进程组中只要有一个进程存在,则该进程组就存在,与其组长进程是否终止无关.

3.1 获取进程的所属进程组

#include <unistd.h>

pid_t getpgrp();

/* 若pid == 0,则返回调用进程的进程组ID */
pid_t getpgid(pid_t pid);

3.2 加入现有的/创建新进程组

#include <unistd.h>

int setpgid(pid_t pid,pid_t pgid);
  • 若 pid 为0,表示使用调用者的进程 ID
  • pgid 为 0,表示使用参数 pid 的值
  • 一个进程只能为它自己或它的子进程设置进程组 ID,且 在其子进程调用 exec 函数后,就不能再更改该子进程的进程组ID了
  • 一般的操作是,在 fork 后,父、子进程都调用 setpgid 函数设置子进程的进程组 ID,这样做是为了保证无论父子进程哪个优先运行,都正确地设置了进程组

4 会话

会话是一个或多个进程组的集合. 这些进程组可以分成一个前台进程组和多个后台进程组.

会话ID即为会话首进程的进程ID,所谓会话首进程是指调用setsid函数创建会话的进程.

一个会话可以有一个控制终端,该终端可能为实际的终端设备或者伪终端

如果一个会话有一个控制终端,则它有一个前台进程组,会话中的其他进程为后台进程组

在终端中输入的中断键和退出键,其产生的信号会发送到前台进程组中的 所有进程

建立与控制终端连接的会话首进程被称为控制进程

如果终端接口断开连接,则挂断信号会发送給控制进程(会话首进程,一般为shell或sshd,telnetd等服务进程)

4.1 setsid 创建新会话

进程调用 setsid 函数建立一个新会话和新进程组

#include <unistd.h>

pid_t setsid();                 /* 成功则返回进程组ID,否则返回-1 */

调用该函数的进程 不能是 一个进程组的组长,否则会报错,该函数创建一个 新会话和新进程组,其结果是发生三件事

  1. 改进程变成新会话的首进程. 此时该进程是新会话中唯一的进程.
  2. 该进程称为一个新进程组的组长进程,新进程组ID是该调用进程的进程ID
  3. 该进程没有控制终端.(即使调用setsid之前该进程有控制终端,此时这种联系也会中断)

4.2 getsid 获取会话 ID

#include <unistd.h>

pid_t getsid(pid_t pid);        /* 若成功返回会话ID,否则返回-1 */

#include <termios.h>

/* 返回tty_filedes相关终端对应的会话ID,出错返回-1 */
pid_t tcgetsid(int tty_filedes);

处于安全考虑,若参数 pid 并不属于调用者所在的会话 ID,则会出错

#include <unistd.h>
#include <stdio.h>

int main()
{
  printf("本进程所属会话ID为%d\n",getsid(getpid()));
  printf("进程1所属会话ID为%d\n",getsid(1));
  return 0;
}

本进程所属会话ID为5992
进程1所属会话ID为-1

5 前后台进程组

5.1 设置/获取前台进程组

需要有一种方法来通知内核哪一个进程组是前台进程组,这样终端设备驱动程序就能了解将终端输入和终端产生的信号送到何处

#include <unistd.h>

/* 返回与tty_filedes相关联的终端的前台进程组ID,失败则返回-1 */
pid_t tcgetpgrp(int tty_filedes);

/* 设置与tty_filedes相关联的终端的前台进程组,成功返回0,失败返回-1 */
int tcsetpgrp(int tty_filedes,pid_t pgrpid);

5.2 后台作业的 IO 处理

若后台作业试图读取终端,则终端驱动程序将检测到这种情况,并且向后台作业发送一个特定信号 SIGTTIN,该信号通常会暂停此后台作业,而 shell 向有关用户发出作业被暂停的通知。

若后台作业试图输出到终端,则根据终端的设置,可能马上输出,也可能等到后台作业切换到前台后才输出,若设置为禁止输出,则当后台作业尝试输出时,驱动程序会向该作业发送 SIGTTOU 信号,该信号通常也会暂停该后台作业,而shell向有关用户发出作业被暂停的通知。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

肤浅与狂妄

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文