守护进程是在后台运行不受终端控制的进程(如输入、输出等),一般的网络服务都是以守护进程的方式运行。守护进程脱离终端的主要原因有两点:(1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。(2)(如其他用户登录该终端后,以前的守护进程的错误信息不应出现)由终端上的一些键所产生的信号(如中断信号),不应对以前从该终端上启动的任何守护进程造成影响。要注意守护进程与后台运行程序(即加&启动的程序)的区别。

 创建守护进程的过程:  

1. 调用fork创建子进程。父进程终止,让子进程在后台继续执行。
2. 子进程调用setsid产生新会话期并失去控制终端调用setsid()使子进程进程成为新会话组长和新的进程组长,同时失去控制终端。
3. 忽略SIGHUP信号。会话组长进程终止会向其他进程发该信号,造成其他进程终止。
4. 调用fork再创建子进程。子进程终止,子子进程继续执行,由于子子进程不再是会话组长,从而禁止进程重新打开控制终端。
5. 改变当前工作目录为根目录。一般将工作目录改变到根目录,这样进程的启动目录也可以被卸掉。
6. 关闭打开的文件描述符,打开一个空设备,并复制到标准输出和标准错误上。 避免调用的一些库函数依然向屏幕输出信息。
7. 重设文件创建掩码清除从父进程那里继承来的文件创建掩码,设为0。
8. 用openlog函数建立与syslogd的连接。

创建守护进程的例子如下程序所示:

void daemon_init(const char* pname,int facility)
{
int i;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/* 清除文件模式创建掩码,使新文件的权限位不受原先文件模式创建掩码的权限位的影响*/
umask(0);
if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
{
perror("getrlimit() error");
exit(-1);
}
if((pid = fork()) < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0) /*父进程终止 */
exit(0);
setsid(); /* 子进程成为会话首进程*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP,&sa,NULL) < 0)
{
perror("sigaction() error");
exit(-1);
}
if((pid = fork()) < 0)
{
perror("fork() error");
exit(-1);
}
else if(pid > 0)
exit(0); /* 第一个子程进终止,保证后面操作不会分配终端 */
if(chdir("/")<0) /* 改变工作目录 */
{
perror("chdir() error");
exit(-1);
}
if(rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for(i=0;i<rl.rlim_max;++i) /*关闭所有打开的文件描述字*/
close(i);
openlog(pname, LOG_PID, facility); /*用syslogd处理错误*/
}

现在要用守护进程实现一个时间服务器,呈现的功能是:服务器运行后自动成为守护进程,返回shell;客户端运行后收到服务器发来的当前时间。

时间服务器程序(timeserver.c)如下:

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <fcntl.h>
8 #include <signal.h>
9 #include <syslog.h>
10 #include <sys/resource.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <time.h>
15
16 #define MAXLINE 100
17
18 void daemon_init(const char* pname,int facility)
19 {
20 int i;
21 pid_t pid;
22 struct rlimit rl;
23 struct sigaction sa;
24 /* 清除文件模式创建掩码,使新文件的权限位不受原先文件模式创建掩码的权限位的影响*/
25 umask(0);
26 if(getrlimit(RLIMIT_NOFILE,&rl) < 0)
27 {
28 perror("getrlimit() error");
29 exit(-1);
30 }
31 if((pid = fork()) < 0)
32 {
33 perror("fork() error");
34 exit(-1);
35 }
36 else if(pid > 0) /*父进程终止 */
37 exit(0);
38 setsid(); /* 子进程成为会话首进程*/
39 sa.sa_handler = SIG_IGN;
40 sigemptyset(&sa.sa_mask);
41 sa.sa_flags = 0;
42 if(sigaction(SIGHUP,&sa,NULL) < 0)
43 {
44 perror("sigaction() error");
45 exit(-1);
46 }
47 if((pid = fork()) < 0)
48 {
49 perror("fork() error");
50 exit(-1);
51 }
52 else if(pid > 0)
53 exit(0); /* 第一个子程进终止,保证后面操作不会分配终端 */
54 if(chdir("/")<0) /* 改变工作目录 */
55 {
56 perror("chdir() error");
57 exit(-1);
58 }
59 if(rl.rlim_max == RLIM_INFINITY)
60 rl.rlim_max = 1024;
61 for(i=0;i<rl.rlim_max;++i) /*关闭所有打开的文件描述字*/
62 close(i);
63 openlog(pname, LOG_PID, facility); /*用syslogd处理错误*/
64 }
65
66 int main(int argc,char *argv[])
67 {
68 int listenfd, connfd;
69 socklen_t addrlen, len;
70 struct sockaddr cliaddr;
71 struct sockaddr_in server;
72 char buff[MAXLINE];
73 time_t ticks;
74 int n;
75 bzero(&server, sizeof(server));
76 bzero(&cliaddr,sizeof(cliaddr));
77 server.sin_family = AF_INET;
78 server.sin_port = htons(5050);
79 server.sin_addr.s_addr = htonl(INADDR_ANY);
80 daemon_init(argv[0], 0);
81 if((listenfd=socket(AF_INET, SOCK_STREAM, 0))==-1)
82 {
83 syslog(LOG_NOTICE|LOG_LOCAL0,"socket error");
84 exit(-1);
85 }
86 if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr))==-1)
87 {
88 syslog(LOG_NOTICE|LOG_LOCAL0,"socket error");
89 exit(-1);
90 }
91 if(listen(listenfd,5)==-1)
92 {
93 syslog(LOG_NOTICE|LOG_LOCAL0,"listen error");
94 exit(-1);
95 }
96 for ( ; ; )
97 {
98 len = sizeof(cliaddr);
99 connfd = accept(listenfd,&cliaddr, &len);
100 ticks = time(NULL);
101 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
102 if((n= write(connfd, buff, strlen(buff)))==-1)
103 syslog(LOG_NOTICE|LOG_LOCAL0,"write error");
104 close(connfd);
105 }
106 }

客户端程序(timeclient.c)如下:

 1 #include <unistd.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <netdb.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #define PORT 5050
9 #define MAXDATASIZE 100
10
11 int main(int argc, char *argv[])
12 {
13 int fd, numbytes;
14 char buf[MAXDATASIZE];
15 struct sockaddr_in server;
16 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
17 {
18 perror("Create socket failed.");
19 exit(-1);
20 }
21 bzero(&server, sizeof(server));
22 server.sin_family = AF_INET;
23 server.sin_port = htons(PORT);
24 server.sin_addr.s_addr = htonl(INADDR_ANY);
25 if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)
26 {
27 perror("connect failed.");
28 exit(-1);
29 }
30 if( ((numbytes = recv(fd, buf, MAXDATASIZE, 0)) == -1))
31 {
32 perror("recv error.");
33 exit(-1);
34 }
35 buf[numbytes] ='\0';
36 printf("Server Message: %s\n",buf);
37 close(fd);
38 }

程序运行过程: 先运行时间服务器程序,再在运行客户端程序。

运行时间服务器程序结果如下:

此时timeserver是个守护进程,已经在运行,通过ps -axj命令可以查看该进程,查看结果如下:

 运行客户端程序结果如下:

客户端收到服务器发送的当前时间。

Unix环境高级编程(十四)守护进程实现时间服务器的更多相关文章

  1. UNIX环境高级编程——初始化一个守护进程

    #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h&g ...

  2. Unix环境高级编程(十八)高级进程间通信

    本章主要介绍了基于STREAM的管道和UNIX域套接字,这些IPC可以在进程间传送打开文件描述符.服务进程可以使用它们的打开文件描述符与指定的名字相关联,客户进程可以使用这些名字与服务器进程通信. 1 ...

  3. UNIX环境高级编程 第8章 进程控制

    本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...

  4. UNIX环境高级编程 第7章 进程环境

    本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...

  5. Unix环境高级编程(十二)线程控制

    本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制: Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询 ...

  6. UNIX环境高级编程 第9章 进程关系

    在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...

  7. Unix环境高级编程(十五)高级I/O

    1.非阻塞I/O 对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况:(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道.终端设备和网络设备).(2)如果数据不能立 ...

  8. Unix环境高级编程(十)信号续

    1.signal函数 Unix系统的信号机制最简单的接口是signal函数,函数原型如下: #include <signal.h> typedef void (*sighandler_t) ...

  9. unix环境高级编程第四章笔记

    文件和目录 start fstart lstart函数 一旦给出pathname, start函数就返回了与此命名文件有关的信息结构 #include <sys/start> int st ...

随机推荐

  1. 锐浪报表 导出 PDF ANSI码 乱码 问题解决

    锐浪 报表 导出PDF时如果 ANSI 码 打勾了会乱码,能将这个选项默认不打勾吗 //在报表导出事件脚本里写脚本,可实现导出控制Sender.AsE2PDFOption.AnsiTextMode=0 ...

  2. Oracle 错误收集

    Oracle 错误大全 ORA-00001: 违反唯一约束条件 (.)ORA-00017: 请求会话以设置跟踪事件ORA-00018: 超出最大会话数ORA-00019: 超出最大会话许可数ORA-0 ...

  3. [NPM] Execute Code from a Remote GitHub Branch with npx

    We will see how you can use npx to pull and execute code from a GitHub repository. If you need even ...

  4. Python编程-基础知识-List

    Negative Indexes(负索引) >>> spam = ['cat', 'bat', 'rat', 'elephant'] >>> spam[-1] 'e ...

  5. Android Bundle存储数据类型

    曾经被问到这样一个问题:Bundle能存哪些数据类型,不能存哪些数据类型? 当时那个汗啊,因为,平常使用Bundle,要么使用基本数据类型,要么序列化自定义的Class,那到底能存哪些类型,不能存哪些 ...

  6. leetcode Wildcard Matching greedy algrithm

    The recursive program will result in TLE like this: class Solution { public: bool isMatch(const char ...

  7. iPhone调用ffmpeg2.0.2解码h264视频的示例代码

    iPhone调用ffmpeg2.0.2解码h264视频的示例代码 h264demo.zip 关于怎么在MAC下编译iOS下的ffmpeg请看 编译最新ffmpeg2.0.1(ffmpeg2.0.2)到 ...

  8. PyQt5教程——组件(7)

    PyQt5中的组件(widgets) 组件(widgets)是构建一个应用的基础模块.PyQt5有广泛的各式各样的组件,包含按钮,复选按钮,滑块条,和列表框.在这个部分的教程中,我们将学习几种有用的组 ...

  9. 算法笔记_127:蓝桥杯2017模拟赛-本科组习题解答(Java)

     目录 1 算年龄 2 猜算式 3 排列序数 4 字符串比较 5 还款计算 6 滑动解锁 7 风险度量   PS:以下代码部分仅供参考,若有不当之处,还请路过同学指出哦~ 1 算年龄 标题:算年龄 英 ...

  10. webpack配置上线地址

    webpack配置上线地址主要使用output配置项下的publicPath. webpack.config.js配置文件为: var htmlWebpackPlugin = require('htm ...