一、进程组,作业,会话的概念

    1、进程组:是一个或多个进程的集合。通常,与同一作业相关联,可以接收来自同一终端的各种信号。每个进程都有一个唯一的进程组ID。每个进程组都可以有一个组长进程。组长进程的标识是,其进程组ID等于其进程ID。组长进程可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,这与组长进程是否终止无关。

    2、作业:Shell分前后台来控制的不是进程而是作业(job)或者进程组。一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成,shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。

      作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业。 

一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。

    3、会话:是一个或多个进程组的集合。一个会话可以有一个控制终端。建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。

****************(进程组就相当于一个班级,组长进程就像当于班长。会话就相当于是一个年级。一个年级有一个教务处。(控制终端))。
二、终端

    1、终端的概念:用户通过终端登录系统后得到一个Shell进程,这个终端称为Shell进程的控制终端,

      每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终 端。事实上每个终端设备

都对应一个不同的设备文件,/dev/tty提供了一个通用的接口,一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访问。ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备而不能是任意文件。

****************查看一下各种不同的终端所对应的设备文件名。

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     printf("fd: %d -> %s\n",0,ttyname(0));
  6     printf("fd: %d -> %s\n",1,ttyname(1));
  7     printf("fd: %d -> %s\n",2,ttyname(2));
  8 }

    2、终端登录过程:      

    一台PC通常只有一套键盘和显示器,也就是只有一套终端设备,但是可以通过Ctrl-Alt-F1~Ctrl-Alt- F6切换到6个字符终端,相当于有6套虚拟的终端设备,它们共用同一套物理终端设备,对应的设备 文件分别是/dev/tty1~/dev/tty6,所以称为虚拟终端(Virtual Terminal)。设备文件/dev/tty0表 示当前虚拟终端,比如切换到Ctrl-Alt-F1的字符终端时/dev/tty0就表示/dev/tty1,切换到Ctrl-Alt- F2的字符终端时/dev/tty0就表示/dev/tty2,就像/dev/tty一样也是一个通用的接口,但它不能表示图形终端窗口所对应的终端。

    a、系统启动时,init进程根据配置文件/etc/inittab确定需要打开哪些终端。

    b、getty根据命令行参数打开终端设备作为它的控制终端,把文件描述符0、1、2都指向控制

终端,然后提示用户输入帐号。用户输入帐号之后,getty的任务就完成了,它再执行login程序:

    execle("/bin/login", "login", "-p", username, NULL, envp);

    c、login程序提示用户输入密码(输入密码期间关闭终端的回显),然后验证帐号密码的正确性。 如果密码不正确,login进程终止,init会重新fork/exec一个getty进程。如果密码正确,login程序设置一些环境变量,设置当前工作目录为该用户的主目录,然后执行Shell:

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

三、守护进程  

   1、 守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

   2、用ps axj | grep -E 'd$'查看守护进程

    参数a表示不仅列当前用户的进程,也列出所有其他用户的进程,参数x表示不仅列有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息。

   3、创建守护进程

        调用函数setsid函数创建一个新的Session,并成为Session Leader(会话首进程),调用成功返回新创建的Session id,出错返回-1;

    a、调用umask将文件模式创建屏蔽字设置为0.

    b、调用fork,父进程退出(exit)。原因:

        1)如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为该命令已经执行完毕。

        2)保证子进程不是一个进程组的组长进程。

    c、调用setsid创建一个新会话。setsid会导致:

        1)调用进程成为新会话的首进程。 

        2)调用进程成为一个进程组的组长进程 。

        3)调用进程没有控制终端。(再次fork一次,保证daemon进程,之后不会打开tty设备)

    d、将当前工作目录更改为根目录。

    e、关闭不在需要的文件描述符。

    f、其他:忽略SIGCHLD信号。

 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 void mydeamon(void)
  5 {
  6     pid_t  id=fork();
  7     umask(0);//将文件模式创建屏蔽字设置为0.
  8     if(id>0) 
  9     {
 10         exit(0);//调用fork,父进程退出(exit)
 11     }
 12     setsid();//调用setsid创建一个新会话
 13     chdir("/");//将当前工作目录更改为根目录。
 14     close(0);//关闭不在需要的文件描述符。
 15     close(1);
 16     close(2);
 17 }
 18 int main()
 19 {
 20     mydeamon();
 21     while(1);
 22     return 0;
 23 }

用ps axj |grep "文件名"查看刚才创建的守护进程

wKiom1c1xFuQHoSVAADj3ZRrWDc376.jpg