zoukankan      html  css  js  c++  java
  • 终端&作业控制&会话启动过程

    进程组

    每个进程除了有个进程id外,还属于一个进程组。进程组是一个或者多个进程的集合。通常他们与同一个作业相关联,可以接受来自同一终端的信号。进程组id等于其进程组长id。进程组的终止与进程组长是否存在无关,只要有一个成员进程存在,那么这个进程组就存在。

    作业

    shell分前后台进行控制的不是进程而是作业或者进程组。一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成。shell可以运行一个前台作业和多个后台作业,这称为作业控制。

    作业与进程组的区别:如果一个作业中的某个进程创建了一个子进程,那么这个子进程不属于作业,但属于进程组。

    会话

    会话是一个或者多个进程组的集合。

    一个会话可以有一个控制终端。建立与控制终端连接的会话首进程被称为控制进程。

    注:通过管道方式实现一个作业任务。

    前两行:对比可以看出,两个sleep命令的PPID(父进程id:bash),PGID(进程组ID),SID(会话ID),TTY(终端)都是相同的。

        PID是不同的,他们是一个进程组中的两个不同进程。

    将之与第三行对比,发现,各个项都是不同的,特别是SID,TTY,TPGID(top PGID(前台进程组ID))都是不同的。

    ps -e 或 ps -o pid,ppid,session,tpgid, comm (其中session显示的sessionid, tpgid显示前台进程组id, comm显示命令名称)

    同时,我们可以验证,SID会话ID是由每个终端进行确定的。

    由这个图我们更可以清晰的理解到,对于每一个连续的命令执行流,其进程组/作业id是相同的。会话id则是终端相关的。

    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char const *argv[])
    {
        printf("fd: %d -> %s
    ",0,ttyname(0) );
        printf("fd: %d -> %s
    ",1,ttyname(1) );
        printf("fd: %d -> %s
    ",2,ttyname(2) );
        return 0;
    }

    现在我们来看终端登录的过程:

    1、系统启动时,init进程根据配置文件/etc/inittab确定需要打开哪些终端。例如配置文件中有
    这样一行:
    1:2345:respawn:/sbin/getty 9600 tty1

    和/etc/passwd类似,每个字段用用:号隔开。开头的1是这一一行行配置的id,通常要和tty的后缀一一致,配置tty2的那一一行行id就应该是2。第二二个字段2345表示示运行行级别2~5都执行行这个配置。最后一个字 段/sbin/getty 9600 tty1是init进程要fork/exec的命令,打开终端/dev/tty1,波特率 是9600(波特率只对串又口口和Modem终端有意义),然后提示示用用户输入入帐号。中间的respawn字段表示示init进程会监视getty进程的运行行状态,一一旦该进程终止止,init会再次fork/exec这个命令,所以 我们从终端退出登录后会再次提示示输入入帐号。

    2、getty根据命令行行参数打开终端设备作为它的控制终端,把文文件描述符0、1、2都指向控制终 端,然后提示示用用户输入入帐号。用用户输入入帐号之后,getty的任务就完成了,它再执行行login程序:
    execle("/bin/login", "login", "-p", username, NULL, envp);

    3、如果密码不正确,login进程终止止,init会重新fork/exec一一个getty进程。如果密码正确,login程 序设置一一些环境变量,设置当前工工作目目录为该用用户的主目目录,然后执行行Shell:

    execl("/bin/bash", "-bash", NULL);

    从getty开始exec到login,再exec到bash,其实都是同一一个进程,因此控制终 端没变,文文件描述符0、1、2也仍然指向控制终端。由于fork会复制PCB信息,所以由Shell启动的 其它进程也都是如此。

    从会话角度讲 终端开启过程是:

    1。getty或者telnetd调用setsid,设置会话id,在这个id的基础上开启终端,建立会话。同时创建一个进程组。且该进程是此终端下所有会话的控制终端。

    2。登录过程中,进行exec替换。变为login,然后变为shell

    3。当shell调用fork创建子进程进行工作时,调用setpgid将作业中的某个进程设置伪新的组进程,形成本会话内的一个作业流。

    注:在作业中,如果某个进程创建新的子进程了,该子进程并不会作为作业内容而被等待。当真是作业完成时,作业就会退出。(进程组和作业的区别)。

    关于作业的命令:

    jobs查看当前系统执行作业

    process & 将process作为后台作业执行。

    bg 将作业改为后台执行

    fg 将作业改为前台执行

    参数的话, %1 表示作业号为1

    lang@liang:~/linux/thread$ cat fsdf &
    [2] 6470
    lang@liang:~/linux/thread$ fsdf

    给一个需要写入终端的命令设置后台执行时,并不能成功。

    这时我们需要设置stty tostop 禁止后台进程写。然后才能成功

  • 相关阅读:
    Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)
    无向图求点割集的算法
    hdu 2121无根最小树形图要建一个虚拟节点
    hdu 1576扩展欧几里得算法
    欧几里德算法的扩展-求解不定方程
    hdu 3072 强连通+缩点+最小树形图思想
    1352 集合计数 扩展欧几里德算法
    1247 可能的路径 逆向思维
    Atcoder B
    C. Timofey and a tree 观察题 + dfs模拟
  • 原文地址:https://www.cnblogs.com/lang5230/p/5693745.html
Copyright © 2011-2022 走看看