进程组、会话、控制终端和守护进程

1、进程组

每个进程除了有一个进程ID之外,还有一个进程组。进程组是一个或多个进程的集合。每个进程组都有唯一的进程组ID。函数getpgrp()可以得到进程的进程组ID

1 #include

 2 #include

 3 #include

 4

 5 int main() {

     pid_t pid;

 7

     if ((pid=fork())<0) {

         printf("fork error!");

10     }else if (pid==0) {

11         printf("The child process PID is %d.\n",getpid());

12         printf("The Group ID is %d.\n",getpgrp());

13         printf("The Group ID is %d.\n",getpgid(0));

14         printf("The Group ID is %d.\n",getpgid(getpid()));

15         exit(0);

16     }

17

18     sleep(3);

19     printf("The parent process PID is %d.\n",getpid());

20     printf("The Group ID is %d.\n",getpgrp());

21

22     return 0;

23 }

注:getpgrp()==getpgid(),见1213行。

 

2、会话(此处参考《unix高级编程》)

会话(session)是一个或多个进程组的集合。

开始于用户登录

终止与用户退出

此期间所有进程都属于这个会话期

进程通过getsid()获得会话id,例:

1 #include

 2 #include

 3 #include

 4

 5 int main() {

     pid_t pid;

 7

     if ((pid=fork())<0) {

         printf("fork error!");

10         exit(1);

11     }else if (pid==0) {

12         printf("The child process PID is %d.\n",getpid());

13         printf("The Group ID of child is %d.\n",getpgid(0));

14         printf("The Session ID of child is %d.\n",getsid(0));

15         sleep(10);

16         setsid(); // 子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程

17         printf("Changed:\n");

18         printf("The child process PID is %d.\n",getpid());

19         printf("The Group ID of child is %d.\n",getpgid(0));

20         printf("The Session ID of child is %d.\n",getsid(0));

21         sleep(20);

22         exit(0);

23     }

24

25     return 0;

26 }

进程调用setsid函数建立新会话。

进程组、会话、控制终端和守护进程

pid_t setsid(void);

该调用进程是组长进程,则出错返回

该调用进程不是组长进程,则创建一个新会话

该进程变成新会话首进程(session header)

该进程成为一个新进程组的组长进程。

该进程没有控制终端,如果之前有,则会被中断

 组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程

在子进程中调用setsid()后,子进程成为新会话首进程,且成为一个组长进程,其进程组id等于会话id

进程可以通过setpgid()来加入已存在的进程组或创建一个新的进程组。例:

1 #include

 2 #include

 3 #include

 4

 5 int main() {

     pid_t pid;

 7

     if ((pid=fork())<0) {

         printf("fork error!");

10         exit(1);

11     }else if (pid==0) {

12         printf("The child process PID is %d.\n",getpid());

13         printf("The Group ID of child is %d.\n",getpgid(0)); // 返回组id

14         sleep(5);

15         printf("The Group ID of child is changed to %d.\n",getpgid(0));

16         exit(0);

17     }

18

19     sleep(1);

20     setpgid(pid,pid); // 改变子进程的组id为子进程本身

21    

22     sleep(5);

23     printf("The parent process PID is %d.\n",getpid());

24     printf("The parent of parent process PID is %d.\n",getppid());

25     printf("The Group ID of parent is %d.\n",getpgid(0));

26     setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程

27     printf("The Group ID of parent is changed to %d.\n",getpgid(0));

28

29     return 0;

30 }

 

3、控制终端

会话和进程组有一些特性:

1). 一个会话可以有一个控制终端(controlling terminal)

2). 建立与控制终端连接的会话首进程被称为控制进程(controlling process)

3). 一个会话中的几个进程组可被分成一个前台进程组(forkground process group)和几个后台进程组(background process group)

4). 如果一个会话有一个控制终端,则它有一个前台进程组。

5). 无论何时键入终端的中断键(DELETECtrl+C),就会将中断信号发送给前台进程组的所有进程。

6). 无论何时键入终端的退出键(Ctrl+\),就会将退出信号发送给前台进程组的所有进程。

7). 如果终端检测到调制解调器(或网络)已经断开连接,则将挂断信号发送给控制进程(会话首进程)

现在,需要有一种方法通知哪个进程组是前台进程组,这样终端设备驱动程序就能了解将终端输入和终端产生的信号送到何处。

 

:这里需要理解何谓前台进程何谓后台进程,前台进程即为可由终端控制的进程,前台进程执行时,终端丧失控制权,前台进程结束后,将控制权交还给终端。后台进程则为独立于终端的进程,例如守护进程即为一个永久性的后台进程。

 

4、守护进程

Linux大多数服务都是通过守护进程实现的,完成许多系统任务

0: 调度进程,称为交换进程(swapper),内核一部分,系统进程

1: init进程, 内核调用,负责内核启动后启动Linux系统

没有终端限制 

让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程

进程组、会话、控制终端和守护进程

:但是我个人在参考《linux c编程实战 》一书时,发现其在setsid()之后又加了一步fork()创建子进程exit()使父进程退出,原因是,此时的子进程已经成为会话组的组长,有权利再调出一个终端,如果出现此情况,则未达到完全脱离终端的目的,此时再调用fork并退出父进程,使得此时的子进程成为完全的后台进程,独立于任何的终端。我觉得这点非常重要,也建议大家在尝试创建守护进程时加上这一步。

 

 #include

  #include

  #include

  #include

  #include

  #include

  #include

 

  int main() {

     pid_t pid;

     int i,fd;

     char *buf="This is a daemon program.\n";

 

     if ((pid=fork())<0) {

         printf("fork error!");

         exit(1);

     }else if (pid>0)  // fork且退出父进程

         exit(0);

    

     setsid();    // 在子进程中创建新会话。

    if ((pid=fork())<0) {  //再次调用fork()并退出父进程

         printf("fork error!");

         exit(1);

     }else if (pid>0)  // fork且退出父进程

         exit(0);

     chdir("/");  // 设置工作目录为根

     umask(0);    // 设置权限掩码

     for(i=0;i返回子进程文件描述符表的项数

         close(i);                // 关闭这些不将用到的文件描述符

 

     while(1) {// 死循环表征它将一直运行

 // 以读写方式打开"/tmp/daemon.log",返回的文件描述符赋给fd

         if ((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0)   

{

             printf("Open file error!\n");

             exit(1);

         }

         // buf写到fd

         write(fd,buf,strlen(buf)+1);

         close(fd);

         sleep(10);

         printf("Never output!\n");

     }

     return 0;

 }

 

http://blog.sina.com.cn/s/blog_6642cd020101g3tl.html
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值