进程间关系下

进程间的关系 下

需要用一种方法知道哪一个进程组是前台进程,这样伪终端程序就知道将终端输入和终端产生的信号发送到何处。

#include <unistd.h>
pid_t tcgetpgrp(int fd);
int tcsetpgrp(int fd, pid_t pgrpid);

函数tcgetpgrp返回前台进程组的id,他和fd上打开终端相关联。

给出控制tty的文件描述符,通过tcgetsid函数,应用程序能获得首进程的进程组id。

#include <termios.h>

pid_t tcpgetsid(int fd);

需要管理控制终端的应用程序k恶意调用tcgetsid函数识别出控制终端首进程的会话id。

demo:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>

static void judge(void){
    pid_t pid;
    pid = tcgetpgrp(STDIN_FILENO);
    if(pid == -1){
        perror("tcgetpgrp");
        return;
    }else if(pid == getpgrp()){
        printf("foreground\n");
    }else{
        printf("background\n");
    }
}

int main(void){
    printf("tcgetsid:%d,pgrp=%d,sid=%d\n",tcgetsid(STDIN_FILENO),getpgrp(),getsid(getpid()));
    signal(SIGTTOU,SIG_IGN);
    judge();
    int result;
    result = tcsetpgrp(STDIN_FILENO,getpgrp());
    if(result == -1){
        perror("tcsetpgrp");
        return -1;
    }
    judge();
    return 0;
}

9.8作业控制

作业控制是1980年新增的一个特征。他允许下在一个终端上启动多个作业(进程组)。它控制哪一个作业可以访问该终端以及哪些作业在后台运行。作业控制有以下三个形式的支持。

1)支持作业控制的shell

2)内核中的终端驱动必须支持作业控制。

3)内核必须提供某些作业控制的信号支持

一个作业是几个进程的合集,通常是一个进程管道。例如

vi main.c

前台进程和后台进程的切换

在Linux终端运行命令的时候,在命令末尾加上 & 符号,就可以让程序在后台运行

[root@Ubuntu$] java Main &

如果程序正在前台运行,可以使用 Ctrl+z 选项把程序暂停,然后用jobs -l查看刚才暂停的程序的number(工作号),然后使用bg %[number] 命令让这个暂停的程序在后台继续运行,jobs命令查看当前终端后台运行的任务,同时也能看到该任务的运行状态。

ps 和 jobs。区别在于 jobs 只能查看当前终端后台执行的任务,换了终端就看不见了。而ps命令适用于查看瞬时进程的动态,可以看到别的终端的任务。

对于所有运行的程序,我们可以用jobs –l 指令查看

[root@Ubuntu$] jobs -l

也可以用 fg %[number] 指令把一个程序调到前台,也就是把刚才后台运行的进程调到前台

二、fg、bg、jobs、&、nohup、ctrl+z、ctrl+c 命令

1、& 加在一个命令的最后,可以把这个命令放到后台执行,如 watch -n 10 sh test.sh & #每10s在后台执行一次test.sh脚本

2、ctrl + z 可以将一个正在前台执行的命令放到后台,并且处于暂停状态。 3、jobs 查看当前有多少在后台运行的命令 jobs -l选项可显示所有任务的PID,jobs的状态可以是running, stopped, Terminated。但是如果任务被终止了(kill),shell 从当前的shell环境已知的列表中删除任务的进程标识。 4、fg 将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用fg %jobnumber(是命令编号,不是进程号)将选中的命令调出。 5、bg 将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用bg %jobnumber将选中的命令调出。 6、kill 法子1:通过jobs命令查看job号(假设为num),然后执行kill %num 法子2:通过ps命令查看job的进程号(PID,假设为pid),然后执行kill pid 前台进程的终止:Ctrl+c 7、nohup 如果让程序始终在后台执行,即使关闭当前的终端也执行(之前的&做不到,我们用ssh连接的服务器,如果我们断开连接,当前终端上运行的后台任务也就随之关闭,因为我们在当前终端上所运行的所有命令,其父进程都是当前的终端,而一旦父进程退出,则会发送hangup信号给所有子进程,子进程收到hangup以后也会退出。如果我们要在退出shell的时候继续运行进程,则需要使用nohup忽略hangup信号,或者setsid将将父进程设为init进程(进程号为1)),这时候需要nohup,使用nohup之后,如果我们退出终端,我们运行的程序的父进程就会变成1。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。关闭中断后,在另一个终端jobs已经无法看到后台跑的程序了,因为jobs只能看到当前与当前终端进程有关的程序,此时利用ps可以查看到指定的进程。

但是需要注意的是nohup会关闭我们运行的程序的标准输入流,也就是我们不能只用标准输入流了。默认会重置输出流到nohup.out文件,当然也可以自定义输出文件。

9.9 shell执行程序

我们检查一下shell是如何运行的,以及这于进程组、进程终端和会话概念的关系,再次执行ps命令。

ps -o pid,ppid,pgid,sid,common

zhanglei@zhanglei-virtual-machine:~$ ps -o pid,ppid,pgid,sid,comm
    PID    PPID    PGID     SID COMMAND
   4463    4453    4463    4463 bash
   4504    4463    4504    4463 ps

ps的 父进程是shell,这正是我们期望看到的。不支持作业控制的进程组为前台进程组。

管道中最后一个进程是shell的子进程,该管道中的第一个进程则是最后一个进程的子进程。从中可以看出 shell fork了自身的副本,然后再为副本中每一条命令fork一个进程。

如果在后台执行这个管道

当有作业控制时候,后台作业被放到后台进程组,如果后台作业试图读取控制终端,则会产生SIGTTIN.在没有作业控制时候,其处理方法是:如果这个进程没有重定向输入输出,则会将标准输入、输出重定向到/dev/null。读/dev/null则产生一个文件结束。这就意味着后台进程cat读取到末尾立即正常结束。

9.10孤儿进程组

孤儿进程一般由init收养,一般在父进程已经挂掉了的时候由init收养

父进程沉睡5秒

子进程为挂断信号建立信号处理器(SIGHUP),这样就能观察到SIGHUP是否已经发送给子进程

子进程用kill,给自己发送SIGTSTP,让自身停止

父进程终止以后子进程变为了孤儿进程,父进程变为了init进程。

现在子进程变为了孤儿进程组的成员。将孤儿进程组定义为:该组中每个成员的父进程要么是改组的一个成员,要么不是该组所属会话的成员。对孤儿进程的另一个描述是

父进程停止后进程组包含一个停止的进程,进程组成为孤儿进程组,posix要求向孤儿进程发送sighup信号,接着发送sigcont信号

猜你喜欢

转载自blog.csdn.net/qq_32783703/article/details/107294931