码迷,mamicode.com
首页 > 系统相关 > 详细

经典C/S服务器模型之守护进程

时间:2016-01-11 21:48:21      阅读:243      评论:0      收藏:0      [点我收藏+]

标签:

linux编程-守护进程编写

 

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

Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。

守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同 Unix环境下守护进程的编程规则并不一致。

    需要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。

    

 

基本概念及特性

进程:

系统进行资源分配和CPU调度的单位.函数getpid可以得到进程的进程ID:pid_t getpid(void);函数getppid可以得到进程的父进程ID:pid_t getppid(void);

① 每个进程都有一个父进程

② 当子进程终止时,父进程会得到通知并能取得子进程的退出状态.

 

进程组:

进程组是一个或多个进程的集合。它们与同一作业相关联,可以接受来自同一终端的各种信号。每个进程组都有唯一的进程组ID。函数getpgrp可以得到进程的进程组ID

pid_t getpgrp(void);

每个进程组都可以有一个组长进程。组长进程的标识是,其进程组ID等于进程ID

① 每个进程也属于一个进程组。

② 每个进程主都有一个进程组号,该号等于该进程组组长的PID

③ 一个进程只能为它自己或子进程设置进程组ID

 

会话期:

对话期(session)是一个或多个进程组的集合。函数getsid返回会话首进程的进程组ID。此函数是Single UNIX SpecificationXSI扩展。pid_t getsid(pid_t pid);

如果pid0,返回调用进程的会话首进程的进程组ID。如果pid并不属于调用者所在的会话,那么调用者就不能得到该会话首进程的进程组ID

① setsid()函数可以建立一个对话期:

② 如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。

    (1)此进程变成该新的对话期的首进程

    (2)此进程变成一个新进程组的组长进程。

    (3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。如果该进程是一个进程组的组长,此函数返回错误。

    (4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能是新会话的进程组的PID

控制终端:
  linux是一个多用户多任务的分时操作系统,必须要支持多个用户同时登陆同一个操作系统,当一个用户登陆一次终端时就会产生一个会话,
  每个会话有一个会话首进程,即创建会话的进程,建立与终端连接的就是这个会话首进程,也被称为控制进程。

pid_t tcgetpgrp(int filedes);

函数tcgetpgrp返回前台进程组的进程组ID,该前台进程组与在filedes上打开的终端相关联;如果进程有一个控制终端,则该进程可以调用tcsetpgrp将前台进程组ID设置为pgrpidpgrpid的值应该是在同一会话中的一个进程组的IDfiledes必须引用该会话的控制终端。

下图可以表示以上四者的基本关系:

技术分享

技术分享

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

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

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

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

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

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

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

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

下边就以守护进程的实际代码运行,辅助理解。

 1 #include<unistd.h>
 2 #include<signal.h>
 3 #include<stdio.h>
 4 #include<stdlib.h>
 5 #include<sys/param.h>
 6 #include<sys/types.h>
 7 #include<sys/stat.h>
 8 #include<time.h>
 9 
10 void init_daemon()
11 {
12         int pid;
13         int i;
14 
15 //       for(i=0;i<NOFILE;i++)
16   //              close(i);     
17 
18         printf("parent\n");
19         printf("pid[%d]\n",getpid());
20         printf("ppid[%d]\n",getppid());
21         printf("gid[%d]\n",getpgrp());
22         printf("sid[%d]\n",getsid(0));
23         printf("tcid[%d]\n",tcgetpgrp(0));
24 
25         printf("\n\n");
26         pid=fork();
27 
28         if(pid<0)
29                 exit(1);//使得子进程一定不是进程组组长,这样才能调用setsid,建立新的进程组和会话组
30         else if(pid>0)
31                 exit(0);
32 
33         printf("child 1\n");
34         printf("pid[%d]\n",getpid());
35         printf("ppid[%d]\n",getppid());
36         printf("gid[%d]\n",getpgrp());
37         printf("sid[%d]\n",getsid(0));
38         printf("tcid[%d]\n",tcgetpgrp(0));
39         printf("\n\n");
40         else if(pid>0)
41                 exit(0);
42 
43         printf("child 1\n");
44         printf("pid[%d]\n",getpid());
45         printf("ppid[%d]\n",getppid());
46         printf("gid[%d]\n",getpgrp());
47         printf("sid[%d]\n",getsid(0));
48         printf("tcid[%d]\n",tcgetpgrp(0));
49         printf("\n\n");
50         setsid(); //建立新的进程组和会话组,并成为新的进程组的组长,和回话组的组长
51         printf("setsid child 1\n");
52         printf("pid[%d]\n",getpid());
53         printf("ppid[%d]\n",getppid());
54         printf("gid[%d]\n",getpgrp());
55         printf("sid[%d]\n",getsid(0));
56         printf("tcid[%d]\n",tcgetpgrp(0));
57         printf("\n\n");
58         pid=fork();
59         if(pid<0)
60                 exit(1);
61         else if(pid>0)
62                 exit(0);//使得孙子进程不在是进程组的组长,即没有权限建立新的与回话组绑定的控制终端
63 
64         printf("child 2\n");
65         printf("pid[%d]\n",getpid());
66         printf("ppid[%d]\n",getppid());
67         printf("gid[%d]\n",getpgrp());
68         printf("sid[%d]\n",getsid(0));
69         printf("tcid[%d]\n",tcgetpgrp(0));
70         printf("\n\n");
71         //关闭文件描述符,这样进程不在与文件描述符传递数据,比如printf打印的数据不在现在是终端界面中
72         for(i=0;i<NOFILE;i++)
73                 close(i);
74 
75         printf("close fd\n");
76         chdir("/home/cz/Desktop/mcs/");  //切换工作目录
77         printf("cd\n");
78         umask(0);//清除文件掩膜
79         printf("umask\n");
80 }
81 
82 void main()
83 {
84         FILE *fp;
85         time_t t;
86 
87         printf("%s\n","start");
88         init_daemon();
89 }
cz@ubuntu:~/Desktop/mcs$ ./demaontest 
start
parent
pid[2724]
ppid[1821]
gid[2724]
sid[1821]
tcid[2724]


cz@ubuntu:~/Desktop/mcs$ child 1
pid[2725]
ppid[1]
gid[2724]
sid[1821]
tcid[1821]


setsid child 1
pid[2725]
ppid[1]
gid[2725]
sid[2725]
tcid[-1]


child 2
pid[2726]
ppid[1]
gid[2725]
sid[2725]
tcid[-1]

 

 由以上实验结果就可以清晰的明白,守护进程化过程中,每一步的作用。

经典C/S服务器模型之守护进程

标签:

原文地址:http://www.cnblogs.com/cz-blog/p/5114184.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!