天天看点

Linux---进程间关系和守护进程进程间关系

进程间关系

进程组

  1. 进程组是一个或多个进程的集合
  2. 每个进程除了有一个进程ID之外,还属于一个进程组
  3. 这些进程通常与同一作业相关联,可以接受来自同一终端的各种信号
  4. 每个进程组有唯一进程组ID,每个进程组可以有一个组长进程,其进程ID和进程组ID一致
  5. 组长进程可以创建一个进程组,创建该组中进程,然后终止
  6. 只要某个进程组中有一个进程存在,该进程组就存在,与组长进程是否终止无关
  7. 从进程组的创建到最后一个进程离开的时间区成为进程组的生命周期
    Linux---进程间关系和守护进程进程间关系
    他们的ppid是一样的,属于同一控制终端,pid和pgid一样的是进程组组长

作业

  1. 进程组在某种意义上可称之为作业,在我们所用的shell中,shell对于前后台的控制的是作业或进程组
  2. shell对作业的控制,可以 运行一个前台作业和多个后台作业
  3. 在前台新起作业,shell是无法运行,因为它被提到了后台,但是如果前台进程退出,shell就又被提到了前台,所以可以继续接受用户输入
  4. 进程组和作业的唯一区别:进程组的进程创建出来的子进程还是属于进程组,但是作业中的进程创建出来的子进程不属于这个作业
1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     pid_t pid=fork();
  6     if(pid<0)
  7     {
  8         return 1;
  9     }
 10     else if(pid==0)
 11     {
 12         while(1)
 13         {
 14             printf("child%d###\n",getpid());
 15             sleep(1);
 16         }
 17     }
 18     else
 19     {
 20         int i=3;
 21         while(i)
 22         {
 23             printf("parent%d#####\n",getpid());
 24             --i;
 25             sleep(1);
 26         }
 27     }
 28     return 0;
 29 }

           

fork创建出子进程,默认在前台运行,此时输入命令是不会起任何作用的,3秒过后,父进程退出,此时命令又可以读取运行了,这是因为当父进程退出后,子进程被1号进程托管,自动成为后台作业,此时shell又被提到前台,就可以正常运行了

会话

  1. 会话是一个或多个进程组的集合
  2. 一个会话有一个控制终端,这通常是登录到其上的终端设备(在终端登录情况下)或伪终端设备(在网络登录情况下)
  3. 建立与控制终端连接的会话首进程被称为控制进程
  4. 一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组
  5. 所以一个会话中,应该包括控制进程,一个前台进程组和任意后台进程组
//创建后台作业
进程 &    sleep 100 &
//查看后台作业
jobs
//将后台作业提到前台
fg num  
num 是jobs里第几个后台进程
//使用ctrl+z 将其放在后台,并且状态为stopped暂停状态
//将进程放在后台运行
bg
           

守护进程

守护进程也叫精灵进程,是在后台运行的一种特殊进程,它独立于控制终端并且周期性的执行某种人物或等待处理某些发生的时间

  1. 是系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互
  2. 除守护进程外其他进程都是在用户登录或运行程序时创建,在系统注销时终止,但系统服务程序不受用户登录注销的影响(以为和终端没有关系)
  3. 自成会话,自成进程组
  4. 可以用ps axj命令查看系统中的进程,参数a表示不仅当前用户的进程,也列出所有其他用户的进程,参数x表示不仅有控制终端的进程,也列出所有无控制终端的进程,参数j表示列出与作业控制相关的信息
Linux---进程间关系和守护进程进程间关系
  1. 凡是TPGID写着-1的都是没有控制终端的进程,也就是守护进程
  2. 在COMMAND一列用[ ]括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以K开头的名字,表示Kernel
  3. udevd负责维护/dev目录下的设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件可以看出,守护进程通常采用以d结尾的名字,表示Daemon

守护进程存在的原因:控制终端因为某些原因会发送一些信号,接受到信号的进程去执行这些信号的默认处理动作会导致进程退出,这就使得进程不能正常的处理某些任务,所以就需要像征收户进程这样接受不到信号的进程。让进程独立于控制终端,执行某些任务或处理某些事件

创建守护进程的几个重要部分

fork()两次

第一次fork是为了调用setsid,因为调用setsid函数的进程一定不是进程组的组长,让父进程创建出子进程,然后退出,此时子进程一定不是进程组的组长

调用setsid

这是创建守护进程最重要的一步,调用setsid创建一个会话,让调用此函数的进程成为会话首进程

#include<unsitd.h>

id_t setsid(void);

该函数调用成功时返回新创建的Session的id,也就是当前进程的id,出错返回-1

如果调用这个函数的进程是进程组的组长,那么setsid()就会出错,返回-1

要保证当前进程不是进程组的组长也很容易,只要先fork()在调用setsid就行了

fork创建的子进程和父进程在同一个进程组中,进程组的组长必然是该组的第一个进程

所以子进程不可能是该组的第一个进程,也就不可能会使组长,在子进程中调用setsid就不会有问题了

成功调用该函数的结果是:

当前进程称为会话首进程 控制进程,当前进程的id就是会话的id

创建一个新的进程组,当前进程成为进程组的组长,当前进程的id就是进程组的id

如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程

所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了

将当前工作目录改成根目录

守护进程会保护当前目录下的文件或目录下不被删除或卸载

那么如果某一时刻需要对该目录下的文件进行删除操作,就不执行

为什么不能删除或卸载守护进程当前目录下的文件或目录

因为避免出现因为删除或卸载当前目录下的文件或目录而导致守护进程运行失败

系统提供的daemon函数

#include<unsitd.h>

int daemon(int nochdir, int noclose);

当nochdir为0时,当前目录变为根目录,否则不变

当noclose为0时,标准输入,标准输出和错误输出中定向为/del/null,也就是不输出任何信息,否则照样输出

返回值

deamon()调用了fork()

如果fork成功,那么父进程就调用_exit(2)退出

所以看到的错误信息 全部是子进程产生的

如果成功函数返回0,否则返回-1

1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 #include<fcntl.h>
  6 #include<stdlib.h>
  7 #include<sys/stat.h>
  8 void mydaemon(void)
  9 {
 10     int i;
 11     int fd0;
 12     pid_t pid;
 13     struct sigaction sa;
 14     umask(0);
 15     if(pid==fork()<0)
 16         perror("fork");
 17     else if(pid>0)
 18         exit(0);
 19     setsid();
 20     sa.sa_handler=SIG_IGN;
 21     sidemptyset(&sa.sa_mask);
 22     sa.sa_flags=0;
 23     if(sigaction(SIGCHLD,&sa,NULL)<0)
 24         return ;
 25     if(pid=fork()<0)
 26     {
 27         perror("fork");
 28         return ;
 29     }
 30     else if(pid!=0)
 31         exit(0);
 32     if(chdir("/")<0)
 33     {
 34         printf("chdir dir error\n");
 35         return ;
 36     }
 37     close(0);
 38     fd0=open("/dev/null",O_RDWR);
 39     dup2(fd0,1);
 40     dup2(fd0,2);
 41 }
 42 int main()
 43 {
 44    // daemon(0,0);
 45    mydaemon();
 46     while(1)
 47     {
 48         sleep(1);
 49     }
 50     return 0;
 51 }

           

继续阅读