apue笔记-第9章 进程关系

终端登录:

init进程知道那些终端设备可用来登录,并且为每个设备生成一个getty进程

网络登录: 

为了使同一个软件技能处理终端登录,又能够处理网络登录,系统使用了一种称为伪终端(pseudo terminal)的软件驱动程序。

BSD登录中,有一个inetd进程-->等待大多数网络连接。

inetd等待TCP/IP连接请求到达主机,当一个连接请求到达时,执行一次fork,然后生成的子进程exec执行适当的程序。

 


进程组

pid_t getpgrp()获取进程组id

int setpgid(pid_t pid,pid_t pid);加入一个现有的进程组或者创建一个新的进程组

注意:一个进程只能为它自己或者它的子进程设置进程组ID。在它的子进程调用了exec后,它就不可以更改该孩子进程的进程组了!


会话:

返回前台进程组GroupID:

#include<unistd.h>
#include "apue.h"
int main(void){

    /*returns the process group ID of the foreground process group on 
    the terminal associated to fd,  which must be the 
    controlling terminal of the calling process.`*/
    printf("前台进程组ID = %d\n",tcgetpgrp(STDIN_FILENO));
    exit(0);

}

作业控制

作业编号为[1],进程ID为11073


以下转自:https://blog.csdn.net/q1007729991/article/details/56846375

1. 控制终端
一般来说,在 bash 中运行的进程,都会伴随着一个控制终端。默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端。

需要特别注意的是,控制终端是进程的属性,它保存在进程 PCB 中。而 bash 进程在启动的时候,会自动建立一个控制终端。所以由 bash 进程启动(fork)的进程都会继承控制终端。所以在同一个会话中,控制终端都是一样的。

进程可以没有控制终端。如果要想让进程脱离控制终端,可以让该进程创建新的会话

2. 控制进程
建立与控制终端连接的会话首进程(session leader),被称为控制进程。如果终端设备与控制终端断开连接,系统会发送信号给控制进程。

如果会话首进程终止,则该信号发送到该会话前台进程组。一个进程退出导致一个孤儿进程组产生时,如果任意一个孤儿进程组中的进程处于 STOP 状态,发送 SIGHUP 和 SIGCONT 信号到该进程组中所有进程。

3. 前台进程组与后台进程组
如果一个会话有控制终端,则它有一个前台进程组,以及多个后台进程组。使用命令 ps ajx 可以查看会话的前台进程组,位于 TPGID 一栏,如果该值为 -1,表示该进程没有控制终端,所以也没有前台进程组。

当在终端输入或者终端产生信号时,都会发送到前台进程组。在很久以前学习信号时,我们使用快捷键 CTRL C、CTRL Z 等等发送信号,并没有指定进程和进程组编号,实际上,这样产生的信号默认都是发送到前台进程组中去的。

当你执行 CTRL C 时,前台进程组中所有的进程都会中断。
 


孤儿进程

#include "apue.h"
#include <errno.h>

/* 挂断信号 */
static void sig_hup(int signo){
    printf("SIGHUP received,pid = %d\n",(long)getpid());
}

static void pr_ids(char* name){

    printf("%s: pid = %ld,ppid = %ld,pgrp = %ld,tpgrp = %ld\n",name,(long)getpid(),(long)getppid(),(long)getpgrp(),(long)tcgetpgrp(STDIN_FILENO));
    fflush(stdout);
}

int main(void){

    char c;
    pid_t pid;

    pr_ids("parent");
    if((pid = fork())<0){
        printf("fork err!");
    }
    else if(pid>0){
        sleep(1); /*sleep to let child stop itself! */
    }
    else{
        pr_ids("chlid");
        signal(SIGHUP,sig_hup); /* esteblish signal handler */
        kill(getpid(),SIGTSTP); /* stop ourself */
        pr_ids("child"); /* prints only if we are continue */

        if(read(STDIN_FILENO,&c,1)!=1)
            printf("read err %d on controlling TTY\n",errno);

    }
    exit(0);

}

父进程终止后,停止的子进程成为了孤儿进程 

以下转自:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/3873323.html

1. 为什么会有孤儿进程组的概念,APUE没直接写,但是GNU有规定

孤儿进程组不可以获得终端,这是为了保证控制进程死掉后他的终端可以安全分配给新session。posix要求向新孤儿进程组中停止状态的进程(GNU说是给孤儿进程组里所有进程发消息,而APUE中文版说是给停止状态的进程发消息)发送SIGHUP(挂起)信号和SIGCONT(继续)信号。首先处理SIGHUP信号,系统默认处理是终止进程,然而也可以另行处理这样进程会继续执行,但任不可以再获得终端。

上面代码,father等待5秒后狗带,child运行结果如下:

[karenyin@host relation]$ ./orphan3
parent: pid = 15877, ppid = 15811, pgrp = 15877, tpgrp = 15877 // father是组长,也是前台进程组ID,father的father是shell,shell pid=15811
child: pid = 15878, ppid = 15877, pgrp = 15877, tpgrp = 15877  // child是组员,当前这个进程组占据了终端。结果输出在这里停了几秒,然后才打印下一句“SIGHUP received”
[karenyin@host relation]$ SIGHUP received, pid = 15878         // 进程组先退出终端,然后又在终端打印SIGHUP
child: pid = 15878, ppid = 1, pgrp = 15877, tpgrp = 15811      // 前台进程组已经不是father了,说明终端又交给了shell
read error 5 on controlling TTY                         // child试图读标准输入read(STDIN_FILENO, &c, 1),触发了异常。
^C                                     // child没有走到exit(0)
 

原因分析如下:

1. kill(15878, SIGTSTP)并不是要child杀死自己,而是暂停前台作业。

2. father自然死亡后,child成为孤儿进程,被init收养,因此ppid=1。

 child也成为进程组15877也成为孤儿进程组,因此收到SIGHUP信号(之后还会收到SIGCONT信号,表示继续)。

3. 子进程处理SIGHUP信号的时候,已经是孤儿进程组成员了,没有权限刷到标准输出,为什么成功打印了“SIGHUP received, pid = 15878”呢?同样的疑惑,为什么child能在终端打印“read error %d on controlling TTY\n”呢?关于这个疑问其实apue也有写,POSIX.1只对读控制终端作出限制。

 所以说,gnu对孤儿进程组的定义里access一词的意思是“读”,孤儿进程组成员试图读取控制终端时才抛EIO,对写终端未做规定。

猜你喜欢

转载自blog.csdn.net/qq_32095699/article/details/106686042