linux守护进程的创建

简介:

这个文档演示了如何使用gcc在linux中写一个精灵进程,使用这篇文档的前提是有Linux的知识和熟悉C语言。这篇文档的版权者是Devin Watson,使用BSD协议。

1.引言:什么是一个精灵进程

  一个精灵进程(或服务)是一个后台进程,它被设计用来自己运行,并且很少或没有用户的干预。Apache服务器http精灵进程(httpd)就是精灵进程的一个例子,它在后台中等待,监听特定的端口,根据请求的类型提供页面或处理脚本。

  在Linux中创建一个精灵进程使用了一个有序的规则集。知道它们如何工作,将会帮助你理解精灵进程不但可以在Linux 用户态中的工作,也会和内核调用一起运行。事实上,一些精灵进程和内核模块的接口,会和硬件设备一起工作,例如全局控制板,打印机以及PDA。它们是Linux的基本构建组件之一,这使得Linux有着难以置信的灵活性和力量。

  通过这篇文档,将会使用C语言构建一个非常简单的精灵进程。我们将会一步一步的向里面添加代码,展示了设置和运行一个daemon的合适的顺序。

2.开始

首先,你需要使用下面的两个软件安装在你的Linux机器上,来开发精灵进程:

(1)GCC 3.2.2 或者更高的版本

(2)Linux 开发头文件和库

如果你的系统还没有安装这两个软件,那么你将会需要安装它们来开发这个文档中的例子。检测你的GCC的版本,可以使用命令:

gcc --version

3.计划你的精灵进程

3.1 精灵进程都干了写什么

  一个精灵进程应该做一件事情,并且把它做好。这个事情可能会像管理多个域名上的成百个发件箱一样复杂,或者像写一个报告并且调用发送邮件程序将报告发送出去一样简单。

  无论如何,关于这个精灵进程做什么,你都应该有一个好的计划。如果它要和其他进程协作(这些进程可能写了或者还没有写),那么也需要考虑其他的进程。

3.2 如何交流

  精灵进程绝不应该通过一个终端和用户交流。事实上,一个精灵进程完全不应该和一个用户直接交流。所有的交流都应该通过一些接口(你可能写了或者没有写),它可能会像GTK+GUI一样复杂,也可能像信号集一样简单。

4.基本的精灵进程结构

  当一个精灵进程启动之后,它必须做一些底层次的工作,让它自己准备好来做它真正的工作。这包括了下面几个步骤:


(1)创建子进程,退出父进程

(2)改变文件的掩码

(3)打开日志文件,以便向里面写入执行信息

(4)创建唯一的会话ID(SID)

(5)改变当前的工作路径到一个安全的地方

(6)关闭标准文件描述符

(7)编写实际的精灵进程代码   

4.1 创建子进程

  一个精灵进程可以通过由系统自己启动,或者由用户通过终端或者脚本启动。当它启动后,这个进程就像系统中的其它可执行文件一样。为了让它真正的有自主权,必须创建一个子进程,这个子进程用来执行实际的代码。我们使用fork来创建子进程:

pid_t pid;

/* Fork off the parent process */

pid = fork();

if (pid < 0) {

exit(EXIT_FAILURE);

}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

  注意在调用fork函数之后要立即进行出错检查,当写一个精灵进程的时候,你需要使你的代码尽可能的健壮。事实上,一个精灵进程的代码中相当多的部分就是出错检查。fork函数或者返回子进程的ID(大于0),或者出错返回-1。如果进程不能fork一个子进程,那么精灵进程应该就在这里结束。

  如果fork成功的话,父进程必须结束。这对那些没有见过的人来说可能有些奇怪,但是通过fork,子进程从这里继续执行。

4.2 改变文件掩码

  为了写那些被精灵进程创建的文件(包括日志文件),文件掩码必须改变来保证它们能够被正确的写或者读。这和在命令行运行umask命令有些相似。但是我们在这里使用编程的方式修改。我们可以使用umask()函数来完成这些:


pid_t pid, sid;

 

/* Fork off the parent process */

pid = fork();

if (pid < 0) {

/* Log failure (use syslog if possible) */

exit(EXIT_FAILURE);

}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

 

/* Change the file mode mask */

umask(0);

  通过设置umask为0,我们将会对精灵进程产生的文件有足够的权限。尽管你可能不需要使用任何文件,在这里设置umask是一个不错的注意,就是为了防止你可能会访问文件系统中的文件。


4.3 打开日志文件

  这部分是可选的,但是推荐你打开一个系统中的日志文件来写日志信息。这可能是你可以查看你的精灵进程调试信息的唯一的一个地方。

4.4 创建一个唯一的会话期ID

  从这里开始,子进程必须从内核得到一个唯一的SID来进行运作。否则,子进程在系统中,将会成为一个孤儿进程。

pid_t pid, sid;

 

 /* Fork off the parent process */

pid = fork();

if (pid < 0) {

exit(EXIT_FAILURE);

}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

 

 /* Change the file mode mask */

umask(0);

 

 /* Open any logs here */

 

 /* Create a new SID for the child process */

sid = setsid();

if (sid < 0) {

/* Log any failure */

exit(EXIT_FAILURE);

}

 

setsid函数和fork函数的返回类型相同,我们可以使用同样的出错检查。


4.5 改变工作路径

  当前的工作路径应该改变到一个总是存在的地方。因为许多Linux发行版本并没有完全遵守Linux文件系统结构标准,唯一的一个确定目录就是根目录,我们可以使用chdir函数:

pid_t pid, sid;

 

 /* Fork off the parent process */

pid = fork();

if (pid < 0) {

exit(EXIT_FAILURE);

}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

 

 /* Change the file mode mask */

umask(0);

 

 /* Open any logs here */

 

 /* Create a new SID for the child process */

sid = setsid();

if (sid < 0) {/* Log any failure here */

exit(EXIT_FAILURE);

}

 

/* Change the current working directory */

if ((chdir("/")) < 0) {

/* Log any failure here */

exit(EXIT_FAILURE);

}

再一次的,你可以看到检测出错的代码。chdir函数在失败时会返回-1,所以一定要在改变目录后进行检查。

4.6 关闭文件描述符

  设置精灵进程的最后一步是关闭标准的文件描述符(STDOUT,STDIN,STDERR),因为一个金陵进程必须不能使用终端,这些文件描述符就是多余的,并且是一个潜在的危险。close函数可以处理这些:

pid_t pid, sid;

 

/* Fork off the parent process */

pid = fork();

if (pid < 0) {

exit(EXIT_FAILURE);

}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

 

/* Change the file mode mask */

umask(0);

 

/* Open any logs here */

 

/* Create a new SID for the child process */

sid = setsid();

if (sid < 0) {

/* Log any failure here */

exit(EXIT_FAILURE);

}

 

/* Change the current working directory */

if ((chdir("/")) < 0) {

/* Log any failure here */

exit(EXIT_FAILURE);

}

 

/* Close out the standard file descriptors */

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

  把常数文件描述符和定义的文件描述符结合起来是一个好主意,这就使得程序能够在不同的系统版本之间具有良好的可移植性。


5.编写精灵进程代码

5.1 初始化

  到了这一步,你一经向Linux系统说明这个程序就是一个精灵进程,所以现在就是开始编写Linux精灵进程的代码了。初始化是第一步,因为这里可能会调用大量不同的函数,来设置你的精灵进程的任务,我将不会太过深入:

5.2 大循环

  一个精灵进程的主要代码实在一个无限循环之中,从技术上说,它不是一个无限循环,但是它的结构如下:

pid_t pid, sid;

 

/* Fork off the parent process */

pid = fork();

if (pid < 0) {

exit(EXIT_FAILURE);}

/* If we got a good PID, then

we can exit the parent process. */

if (pid > 0) {

exit(EXIT_SUCCESS);

}

 

/* Change the file mode mask */

umask(0);

 

/* Open any logs here */

 

/* Create a new SID for the child process */

sid = setsid();

if (sid < 0) {

/* Log any failures here */

exit(EXIT_FAILURE);

}

 

/* Change the current working directory */

if ((chdir("/")) < 0) {

/* Log any failures here */

exit(EXIT_FAILURE);

}

 

/* Close out the standard file descriptors */

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

 

/* Daemon-specific initialization goes here */

 

/* The Big Loop */

while (1) {

/* Do some task here ... */

sleep(30); /* wait 30 seconds */

}

  这个典型的循环使用了while语句,带有一个无限循环的条件,在它的内部调用sleep函数使得它以一定的时间间隔来运行。


  可以把它想象成为心跳一样:当你的心在跳动时,它就执行了一些工作,然后就等待下一次跳动。许多的精灵进程都遵从了相同的方法。

6.一个完整的程序

  下面列出了一个完整的精灵程序示例代码,它展示了设置和执行必须的所有步骤。可以使用gcc编译,在命令行终端模式下运行。要终止的话,可以在找到它的pid之后使用kill命令。

  我也包括了操作系统日志文件的头文件,推荐(至少)应该记录程序开始,停止,暂停,死亡等日志记录,此外使用fopen,fwrite,fclose来写你自己的日志文件。

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <fcntl.h>

#include <errno.h>

#include <syslog.h>

#include <string.h>

 

int main(void)

{

pid_t pid,sid;

/*fork*/

pid = fork();

if(pid<0)

{

exit(EXIT_FAILURE);

}

else if(pid>0)

{

exit(EXIT_SUCCESS);

}

 

/*change the file mode mask*/

umask(0);

 

/*open logs here*/

/*create new SID for the child process*/

sid = setsid();

if(sid<0)

{

exit(EXIT_FAILURE);

}

 

/*change the current working directory*/

if(chdir("/")<0)

{

exit(EXIT_FAILURE);

}

 

/*close the file standard file descriptors*/

close(STDIN_FILENO);

close(STDOUT_FILENO);

close(STDERR_FILENO);

 

/*deamon-specific initialization here*/

/*the big loop*/

while(1)

{

/*do some task here*/

sleep(30);//wait 30 seconds

}

 

exit(EXIT_SUCCESS);

 

return 0;

}

原文:http://m.blog.csdn.net/article/details?id=21277759



本文转自 Linux_woniu 51CTO博客,原文链接:http://blog.51cto.com/linuxcgi/1965314

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2天前
|
NoSQL Linux 程序员
【linux进程信号(一)】信号的概念以及产生信号的方式
【linux进程信号(一)】信号的概念以及产生信号的方式
|
2天前
|
Linux
【linux进程间通信(一)】匿名管道和命名管道
【linux进程间通信(一)】匿名管道和命名管道
|
2天前
|
Java Shell Linux
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
【linux进程控制(三)】进程程序替换--如何自己实现一个bash解释器?
|
2天前
|
算法 Linux Shell
【linux进程(二)】如何创建子进程?--fork函数深度剖析
【linux进程(二)】如何创建子进程?--fork函数深度剖析
|
2天前
|
存储 Linux Shell
【linux进程(一)】深入理解进程概念--什么是进程?PCB的底层是什么?
【linux进程(一)】深入理解进程概念--什么是进程?PCB的底层是什么?
|
3天前
|
消息中间件 Unix Linux
Linux的学习之路:17、进程间通信(1)
Linux的学习之路:17、进程间通信(1)
18 1
|
3天前
|
存储 安全 Linux
Linux的学习之路:9、冯诺依曼与进程(1)
Linux的学习之路:9、冯诺依曼与进程(1)
18 0
|
8天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
10天前
|
监控 Linux
linux监控指定进程
请注意,以上步骤提供了一种基本的方式来监控指定进程。根据你的需求,你可以选择使用不同的工具和参数来获取更详细的进程信息。
14 0
|
11天前
|
消息中间件 监控 Linux
Linux进程和计划任务管理
通过这些命令和工具,你可以有效地管理Linux系统中的进程和计划任务,监控系统的运行状态并保持系统的稳定和可靠性。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
102 2