文章随机晒最新文章关照最多的

jiayi Rss

Linux 作业控制

| Posted in APUE |

18

话说一个支持作业控制的 Linux 终端下有 作业1,我们把他放到后台,终端执行 kill %1 ,恩,我们知道它把 作业1 杀掉了。但它只杀掉了 作业1 吗?那可不一定……
首先,作业(job) 指的是 进程组(process group),一个作业就是一个进程组。进程组 是什么,A process group is a collection of one or more processes, usually associated with the same job,that can receive signals from the same terminal。明白了两者各自的定义,很容易看到他们是相通的。把进程组抽象成作业,一个主要目的是便于操控。因此作业控制可以看作进程组控制,只不过更简便了,但作业控制有时却不能按照我们的意愿工作。

下面给出一段程序加以说明
第一个程序命名 job_control.c,第二个程序命名 job_control_1.c

CODE

/* job_control.c */
#include<sys/types.h>
#include<unistd.h>

int main()
{
    if((pid=fork())==0) {
        /* setpgid(0,0); */
        while(1) {
        }
    } else {
        /* setpgid(pid,0); */
        while(1);
    }  
}

CODE

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>

int main(int argc,char **argv)
{
    if(argc!=2) {
        printf("Usage: ./job_control_1 <pgid>
"
);
        exit(1);
    }  

    pid_t new_gpid;
    sscanf(argv[1],"%d",&new_gpid);

    if(setpgid(0,new_gpid)!=0) {
        perror("setpgid() error");
        exit(2);
    }  
  
    while(1) {
    }  
}

第一个程序只是简单的fork()一个子进程,第二个程序会将自己的 Group ID 设成 argv[1]

后台分别运行 job_control  job_control _1

jiayi:/home/jiayi/apue # ./job_control &
[1] 7161
jiayi:/home/jiayi/apue # ps -o pid,ppid,pgrp,sid,tpgid,command
  PID  PPID  PGRP   SID TPGID COMMAND
 6631  4167  6631  6631  7163 bash
 7161  6631  7161  6631  7163 ./job_control
 7162  7161  7161  6631  7163 ./job_control
 7163  6631  7163  6631  7163 ps -o pid,ppid,pgrp,sid,tpgid,command
jiayi:/home/jiayi/apue # ./job_control_1 7161 &
[2] 7165
jiayi:/home/jiayi/apue # ps -o pid,ppid,pgrp,sid,tpgid,command
  PID  PPID  PGRP   SID TPGID COMMAND
 6631  4167  6631  6631  7167 bash
 7161  6631  7161  6631  7167 ./job_control
 7162  7161  7161  6631  7167 ./job_control
 7165  6631  7161  6631  7167 ./job_control_1 7161
 7167  6631  7167  6631  7167 ps -o pid,ppid,pgrp,sid,tpgid,command
jiayi:/home/jiayi/apue # kill %1
jiayi:/home/jiayi/apue #
[1]-  已终止               ./job_control
[2]+  已终止               ./job_control_1 7161

从上面的输出可以看出
(1) 终端将它的子进程放进不同的 进程组,而不是放到终端的 进程组
(2) ./job_control 被标记为 job[1]job_control_1 7161 被标记为 job[2] ,但他们的 进程组 同为 7161
(3) kill %1 不仅杀掉 job[1],同时将 job[2] 杀掉

估计没哪个程序会这样写,但从中可以看出 作业控制 确实没有将 进程组 规划好。正确的做法应该是,将 job_control_1 7161 归入 job[1],并给用户打印一条说明。

在此折腾过程中,还发现一个有意思的现象,将 ./job_control_1 7161 放到前台来执行,你将失去对终端的控制权,面对终端,无法操作!
下面演示了这个过程

jiayi:/home/jiayi/apue # ./job_control &
[1] 7019
jiayi:/home/jiayi/apue # ps -o pid,ppid,pgrp,sid,tpgid,command
  PID  PPID  PGRP   SID TPGID COMMAND
 6631  4167  6631  6631  7029 bash
 7019  6631  7019  6631  7029 ./job_control
 7020  7019  7019  6631  7029 ./job_control
 7029  6631  7029  6631  7029 ps -o pid,ppid,pgrp,sid,tpgid,command
jiayi:/home/jiayi/apue # ./job_control_1 7019
^C^C^C^C^C^C^^^^^^

呵呵, Control-C  Control- 发送的信号都没被响应,只在是打印转义字符… 除了看着终端,束手无策。。。

另开一个并键入 ps 命令

jiayi:/home/jiayi/apue # ps -e -o pid,ppid,pgrp,sid,tpgid,command | tail -6
 6918  2836  2836  2836    -1 pickup -l -t fifo -u
 7019  6631  7019  6631  7031 ./job_control
 7020  7019  7019  6631  7031 ./job_control
 7031  6631  7019  6631  7031 ./job_control_1 7019
 7037  6576  7037  6576  7037 ps -e -o pid,ppid,pgrp,sid,tpgid,command
 7038  6576  7037  6576  7037 tail -6

TPGID 一栏都是 7031 ,说明上一个终端的 前台进程组ID7031 ,但是我们并没有找到这个 进程组./job_control _1 7161 在执行setpgid(0,new_gpig) 函数之前曾是 7031 进程组的 Leader ,但随后将自己放进了 7019 进程组,7031 进程组变空,随后消失。但终端还认为前台进程组ID 仍是 7031 ,所以 SIGINTSIGQUIT 都被送递到一个不存在的 进程组,看起来好像是被阻塞了。。。

setpgid() 还真是不好对付,以后用起时一定小心。

Comments (18)

技术性文章!!!

两个进程同时被kill是因为第二个程序修改了自己的pgid造成的,每个后台任务都有自己的进程组,所以如果第二个进程不修改pgid的话,是不会和第一个进程在一个组里的,所以我觉得这不应该是shell的问题~~

最近自己写个Shell玩,看了下apue,发现自己写的一团糟…..

jiayi Reply:

@JackalDire,
跟信号的time window相像,虽然出现的几率很小,但总有可能出现,本身设计上有疏忽。。。子进程在setpgid()时发送一个类似SIGCHLD的信号因该可以解决,不过这样的话系统调用要修改,shell也要修改…
你很厉害啦,研究生上完apue才要求写shell,加油~
ps:我也在复习apue,忘了个一塌糊涂。。。

Write a comment

You must be logged in to post a comment.