APUE学习:第九章 进程关系

本文详细解释了进程组的管理,包括获取和更改进程组ID、控制终端的关联、会话的定义以及作业控制的原理。讨论了孤儿进程组的概念,以及在父进程终止时子进程的行为。还探讨了FreeBSD中会话和tty结构的应用。
摘要由CSDN通过智能技术生成

进程组

获取调用进程的进程组ID——getpgrp

tips:

 1.每个进程组有一个组长进程,组长进程的进程组ID等于其进程ID

2.进程组中有进程,进程组就存在,这与组长进程是否终止无关

3.进程组的生命周期:从进程组的创建到最后一个进程离开

更改进程组ID

tips:

1.一个进程只能为它自己或它的子进程设置进程组ID

2.在子进程调用了exec后,不能再更改该子进程的进程组ID

3.一般在fork之后,子进程和父进程都调用setpgid,使子进程加入父进程的进程组

why?这样不会产生冗余操作吗?

这样保证了父进程、子进程在执行各自代码之前,子进程保证位于父进程的进程组。

控制终端

tips:

1.一个会话可以有一个控制终端。

控制终端通常为终端设备(终端登陆情况)或者伪终端设备(网络登录情况)

2.控制进程——建立于控制终端连接的会话首进程

3.会话中的几个进程组可被分为一个前台进程组,以及一个或多个后台进程组

保证程序能与控制终端对话的方法:open文件/dev/tty

会话

定义:一个或多个进程组的集合。

用shell的管道命令将几个进程编成一组:

图9-6就是:

proc1 | proc2 &

proc3 | proc4 | proc5 

会话的建立——setsid函数

如果调用进程已经是一个进程组的组长,则此函数返回出错

tips:

1.通常先调用fork,然后使父进程终止,子进程继续。

子进程继承了父进程的进程组ID,子进程的ID是新分配的,两者不可能相等,保证了setsid函数不出错

2.会话首进程ID视为会话ID

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

通知内核哪一个进程组是前台进程组——tcgetpgrp、tcsetpgrp和tcgetsid函数

作业控制

 

tips:

只有前台作业接收终端输入。后台作业也可以试图读终端,终端驱动程序会检测这种情况,并向后台作业发送一个SIGTTIN信号

该信号会停止此后台作业,并向shell有关用户发出通知,然后用户可以用shell命令将此作业转为前台运行,这样该作业就能读终端了。

shell示例:

stty指令允许/禁止后台作业输出至控制终端

代码示例: 

shell执行程序

不支持作业控制的Solaris上shell执行程序:

在前台执行

可获取这些信息:

1.ps的父进程为sh(ps的ppid == sh的pid)

2.ps和ps位于同一前台进程组和同一会话组

在后台执行:

观察可知:

只有ps进程的PID变了,其他都没变,也就是说在不支持作业控制的情况下,ps仍然在前台进程组中

在Bourne shell中执行命令:

可以得到这些信息:

管道中最后一个指令(cat1)为shell的子进程,管道中第一个指令(ps)为cat1的子进程

推测流程如下:

shell fork一个自身的副本(用来执行cat1),然后此副本再为管道中的每条命令各fork一个进程(所以ps的父进程为cat1)。

如下图所示:

a->b    则b是a的父进程

如果后台执行程序:

也只是进程ID发生了改,因为Bourne shell同样不支持作业控制

在没有作业控制下,如果一个后台进程试图读其控制终端会发生什么?

在Bourne shell中执行三个进程:

管道中的最后一个进程(cat2)为shell的子进程,其他命令的进程为最后进程(cat2)的子进程

带作业控制的Shell 

可以观察到:

1.bash和ps位于不同的进程组,这里ps命令在前台运行,ps命令是前台进程组(5796)的组长进程/

2.登录的shell在执行ps指令时是后台进程组

3.两个进程处于同一会话(SID相等)

后台执行此程序:

可以观察到ps和cat1位于5799进程组,5799为前台进程组 而bash位于后台进程组

因为指令是在前台执行的,所以ps、cat1都在前台进程组,所以bash就在后台进程组

 

孤儿进程组

孤儿进程定义:

父进程已经终止的进程称为孤儿进程组,由init进程收养。

反定义:

一个进程组不是孤儿进程组的条件是——该组中有一个进程,其父进程在属于同一会话的另一个组中。

对于书上那句话:

如果进程组不是孤儿进程组,那么在属于同一会话的另一个组中的父进程就有机会重新启动该组中停止的进程。

为什么范围要限定在一个会话内?

在同一个会话内,不同进程组的父进程可以通过会话ID找到这些进程组,从而有机会重新启动这些停止的进程。如果不在同一个会话内,父进程就无法直接访问其他会话中的进程组,也就无法重新启动这些进程。

因此,为了确保父进程能够管理和控制属于同一个会话的进程组,需要限制在同一个会话内进行这样的操作。

对于书上的问题:

"父进程终止时,如果该子进程停止(用作业控制)又将如何呢?子进程如何继续,以及子进程是否知道它已经是孤儿进程?"

如上图所示,子进程停止,父进程则将退出。构成此情形的代码如下:

#include"apue.h"
#include<errno.h>
static void sig_hup(int signo)
{
    printf("SIGHUP received,pid = %ld\n",(long)getpid());
}

static void pr_ids(char * name)/*用于输出id信息*/
{
    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()
{
    char c;
    pid_t pid;
    pr_ids("parent");
    if((pid=fork())<0)
        err_sys("fork error");
    else if(pid > 0)
    {
        sleep(5);/*父进程睡觉5s,使得子进程在父进程终止前运行*/
    }
    else{
        pr_ids("child");
        signal(SIGHUP,sig_hup);/*为SIGHUP信号添加处理函数*/
        kill(getpid(),SIGTSTP);/*给自己发送停止信号*/
        pr_ids("child"); /*受到内和发送的继续信号后继续*/
        if((read(STDIN_FILENO,&c,1))!=1)
            printf("read error %d ont controlling TTY\n",errno);

    }
    exit(0);
}

输出结果:

该程序的几个特性:

 内核向新孤儿进程组中的每一个进程发送挂断信号(SIGHUP),接着又向其发送继续信号(SIGCONT)

FreeBSD实现

每个会话都分配一个session结构

进程调用setsid时,内核中分配一个session结构。

tty结构:

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值