Linux 作业控制
Posted by jiayi | Posted in APUE | Posted on 07-01-2009
3
话说一个支持作业控制的 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
/* 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);
}
}
#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 ,说明上一个终端的 前台进程组ID 为 7031 ,但是我们并没有找到这个 进程组 。./job_control _1 7161 在执行setpgid(0,new_gpig) 函数之前曾是 7031 进程组的 Leader ,但随后将自己放进了 7019 进程组,7031 进程组变空,随后消失。但终端还认为前台进程组ID 仍是 7031 ,所以 SIGINT 和 SIGQUIT 都被送递到一个不存在的 进程组,看起来好像是被阻塞了。。。
setpgid() 还真是不好对付,以后用起时一定小心。

