深入浅出进程关系分析Job Control(一)

来源:互联网 发布:2017年泰国耽美网络剧 编辑:程序博客网 时间:2024/06/10 16:02

一:写在前面

  笔者最近在研究基于kernel 4.14Qemu-kvm环境的搭建,并做GDB源码追踪。为此笔者补充了很多知识,例如:initrfs,initramfskernel启动过程等,参考的资料主要来源自与kernel官方文档linux官方网站stackoverflow精彩回答,及CSDN一些优质博文。本文主要着眼点还是process_relation

二:理论先行

2.1 Terminal Logins

  现在的linux都是使用systmd来实现终端登录了,关于这点会在别的文章一探究竟。这里还是着眼于经典的实现方式。IBM 中关于systemd的论述,本文如果探究systmd将又是一番非常费事的差事,
  以前的系统启动的时候,由init进程(此init位于真正的根文件系统上)读取/etc/ttys并为其中每一个允许用户登录的设备条目创建一个子进程并执行getty。然后由getty打开终端设备,并提示我们输入用户名。当我们输入完用户名之后,gettyexec loginlogin会继续调用getpwnam获取password文件的信息,之后调用 getpass去提示我们输入密码,当我们键入回车的时候调用crypt进行加密并和password文件中的信息进行对比,如果我们输入的密码是错误的,那么login调用exit,此子进程结束的状态会通知init,然后由其重复之前的操作。可惜,笔者基于fedora 27进行相关实验,所以上面提到的一些操作已经被废弃了。

这里写图片描述

  正常的login会再进行很多“初始化”工作,这里不赘述。但是有一个疑问:

File descriptors 0, 1, and 2 for our login shell are set to the terminal device.

思考一:linux中一切设备皆文件,这句话是指shell打开终端对应的文件,并且将FD 0,1,2分别映射到该文件上吗?

2.2 Network Logins

  在系统启动的时候,initfork一个shell去执行/etc/rc的脚本,由该脚本启动的一个服务inetd,去捕获来自远程telnet client的登录请求。inetdfork一个子线程并执行telnetd,接着telanetd会再次fork形成两个进程,一个负责处理网络来的信息,一个执行Login,这两个进程通过telanetd打开的pseudo terminal device进行联系。如果我们正常登陆的话,后面进行的操作和前面Terminal Logins相近。最后比较重要的一个总结:

we have a login shell with its standard input, standard output, and standard error connected to either a terminal device or a pseudo terminal device.

这里写图片描述

2.3 Process Groups

#include <unistd.h>pid_t getpgid(pid_t pid);Returns: process group ID if OK, −1 on errorint setpgid(pid_t pid, pid_t pgid);Returns: 0 if OK, −1 on error

  比较重要的几个概念:

  • Each process group can have a process group leader. The leader is identified by its process group ID being equal to its process ID.

  • The process group still exists, as long as at least one process is in the group, regardless of whether the group leader terminates.

  • The last remaining process in the process group can either terminate or enter some other process group

  Setpgid只能设置进程自身,或者该进程的子进程。pid为0时,代表调用setpgid的进程,而pgid为0时,pid的值将会被用在pgid上。这里对应实验一,确定下我的猜想。

2.4 Sessions

#include <unistd.h>pid_t setsid(void);Returns: process group ID if OK, −1 on errorpid_t getsid(pid_t pid);Returns: session leader’s process group ID if OK, −1 on error

  Sessions由多个process group组成,当不是Process group leader的进程调用setsid时,会有以下几点保证:

  1. The process becomes the session leader of this new session. (A session leader is the process that creates a session.) The process is the only process in this new session.
  2. The process becomes the process group leader of a new process group. The new process group ID is the process ID of the calling process.
  3. The process has no controlling terminal. (We’ll discuss controlling terminals in the next section.) If the process had a controlling terminal before calling setsid, that association is broken.

  如果一个进程是Group leader,调用该函数会返回错误。所以一般由父进程fork新的子进程之后终止,并由该子进程调用setsid建立新的会话,此时会话的session ID就是该子进程的process ID也是其process group ID这里对应实验二,来确定我的理解是否正确。

2.5 Controlling Terminal

  • A session can have a single controlling terminal.
  • The session leader that establishes the connection to the controlling terminal is called the controlling process.
  • The process groups within a session can be divided into a single foreground process group and one or more background process groups.
  • If a session has a controlling terminal, it has a single foreground process group and all other process groups in the session are background process groups.

  一般登录的时候就会自动建立和treminal的联系,linux使用open without O_NOCTTY(session leader打开的第一个终端设备文件)或者TIOCSCTTY ioctl command两种方式显示的建立Controlling Terminal。/dev/tty是该终端所对应的文件。

2.6 Tcgetpgrp, tcsetpgrp, and tcgetsid Functions

#include <unistd.h>pid_t tcgetpgrp(int fd);Returns: process group ID of foreground process group if OK, −1 on errorint tcsetpgrp(int fd, pid_t pgrpid);Returns: 0 if OK, −1 on error#include <termios.h>pid_t tcgetsid(int fd);Returns: session leader’s process group ID if OK, −1 on error

  tcgetpgrp用来得到fd指定的terminal所对应的前台工作组的Group ID。而tcsetpgrp只能被session leader使用用来设置前台工作组的Group ID,这个ID必须隶属于同一个session。这里的概念都好理解,不像前面有些地方模棱两可,不做实验无法确定。
思考二:是否任何时候Session id都和session leader’s process id or process group id 相等?

2.7 Job Control

This feature allows us to start multiple jobs (groups of processes) from a single terminal and to control which jobs can access the terminal and which jobs are run in the background.

  上面的话总结了job control的作用,大家平时都在用job control,这里我不赞同某些博主说job control没什么作用的观点,因为现在我们使用的shell都默认开启job control,所以平时我们使用&来后台运行某些任务的时候,就已经在使用该特性了。
  由terminal driver根据ctrl+c或者ctrl+z等产生相应的信号发送给前台工作组,后台工作组不受该信号影响。对于终端的输入,后台进程尝试读取时,会收到终端所发送的SIGTTIN信号,使其停止。如果想把后台进程放置前台使用fg %num,这里num从1开始。
  
  这里写图片描述

2.8 Shell Execution of Programs

  此处笔者使用fedora 27,kernel 4.13来做相关实验,Shell默认自带Job Control

#测试1[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm  PID  PPID  PGID   SID COMMAND 1425  1424  1425  1425 bash 1942  1425  1942  1425 ps[root@localhost ~]# #测试2[root@localhost ~]# ps -o pid,ppid,pgid,sid,tpgid,comm   PID  PPID  PGID   SID TPGID COMMAND  1425  1424  1425  1425  1941 bash  1941  1425  1941  1425  1941 ps

  TPGID是terminal的一个属性,其值取自前台工作组的Group ID。所以严格上来说,Process ID不应该和TPGID有直接的关系。充其量说前台工作组的Prosess Leader的Process ID和TPGID相等。

#测试3[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm &[root@localhost ~]#   PID  PPID  PGID   SID COMMAND                      1425  1424  1425  1425 bash                      5743  1425  5743  1425 ps[1]+  Done                    ps -o pid,ppid,pgid,sid,comm#测试4[root@localhost ~]# ps -o pid,ppid,pgid,sid,tpgid,comm &[1] 1955[root@localhost ~]#   PID  PPID  PGID   SID TPGID COMMAND                      1425  1424  1425  1425  1425 bash                      1955  1425  1955  1425  1425 ps[1]+  Done                    ps -o pid,ppid,pgid,sid,tpgid,comm[root@localhost ~]#

  在后台运行的时候,测试2测试4形成对比,可以看出来测试2TPGIDps指令的Process Group ID(注意我没说是Process ID,虽然两个数值一样)。而在测试4中为bash的进程组ID,这样的结果和上面加黑的字体所述是一致的。

[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm | cat  PID  PPID  PGID   SID COMMAND 1425  1424  1425  1425 bash 1970  1425  1970  1425 ps 1971  1425  1970  1425 cat[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm | cat &[1] 5741[root@localhost ~]#   PID  PPID  PGID   SID COMMAND                      1425  1424  1425  1425 bash                      5740  1425  5740  1425 ps                      5741  1425  5740  1425 cat[1]+  Done               ps -o pid,ppid,pgid,sid,comm | cat[root@localhost ~]#[root@localhost ~]# cp /usr/bin/cat /usr/bin/cat1[root@localhost ~]# ps -o pid,ppid,pgid,sid,comm | cat | cat1  PID  PPID  PGID   SID COMMAND 1425  1424  1425  1425 bash 5735  1425  5735  1425 ps 5736  1425  5735  1425 cat 5737  1425  5735  1425 cat1[root@localhost ~]#

  cat,pscat都为bash的子进程,无论是否是在前台执行还是后台执行,这里与不支持job controlshell得到的结果不同,两者fork子进程的方式有很大区别。

[root@localhost ~]# cat > temp.foo &[1] 1979[root@localhost ~]#shuru[1]+  Stopped                 cat > temp.foo[root@localhost ~]#

  后台工作组尝试对Control Terminal读的时候,会收到来自该终端的SIGTTIN从而被终止。

2.9 Orphaned Process Groups

The POSIX.1 definition of an orphaned process group is one in which the parent of every member is either itself a member of the group or is not a member of the group’s session. Another way of saying this is that the process group is not orphaned as long as a process in the group has a parent in a different process group but in the same session.

  POSIX.1明确定义孤儿组的概念:只要该组至少有一个成员的父进程位于同一会话的别的组,那么该组就不是孤儿组。所以孤儿组要么,组里互相是父子关系,要么父进程不属于同一个会话,或者两种情况都有。同时,孤儿组里的进程都会被发送SIGHUP信号和SIGCONT信号。同时需要注意如果孤儿组里面的进程位于后台进程组却又读取Controlling Terminal的话,SIGTTIN信号将不会产生:

the process group of the reading process is orphaned, then the signal is not generated; instead, the read operation fails with errno set to EIO.

三:实验部分

题目一:

设计一段程序,设置子进程的process group id,使其子程序成为该group leader并考虑竞态的发生。

题目二:

setsid 是否会自动的设置调用该函数的进程为the new group’s leader?

题目三:

Write a small program that calls fork and has the child create a new session. Verify that the child becomes a process group leader and that the child no longer has a controlling terminal.

NOTE:避免篇幅过长,实验部分单独记录

原创粉丝点击