说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《Linux网络编程》、《linux系统编程》、《Unix环境高级编程》P408
什么是守护进程?
守护进程又叫精灵进程,是一种生存期较长的进程,用于执行日常事务活动。
常在系统自举时启动,仅在系统关闭时才终止,不与任何控制终端相关联(tty = ?
),故在后台运行。
它们一般以root用户运行或者其他特殊的用户(例如 apache和postfix),并处理一些系统级的任务。习惯上守护进程的名字通常以d结尾(就像crond 和 sshd),但这不是必须的,甚至不是通用的。
名字来源
来源于麦克斯韦妖(Maxwell’s demon),它是物理学家JamesMaxwell在1867年进行的一个思想实验。Daemon这个词也是希腊神话中的鬼怪,它存在与人类和神之间,拥有占卜的能力。与Judeo-Christian神话中的daemon不同,希腊神话中的daemon不是邪恶的。实际上,神话中的daemon是神的助手,做一些奥林匹斯山的居民自己不愿做的事——很像Unix中的守护进程,完成前台用户不愿做的事。
unix系统中一些守护进程
1.init进程 = 系统守护进程,负责启动各执行层次特定系统服务。(这些服务在各自拥有守护进程帮助下实现)
2.keventd守护进程 ,内核中运行计划执行的函数提供进程上下文。
3.kapmd守护进程,对高级电源管理提供支持。
4.kswapd守护进程 = 页面调出守护进程,将脏页面以低速写到磁盘上是的这些页面在需要时仍可回收使用。
5.bdflush和kupdated守护进程,将高速缓存中的数据冲洗到磁盘上。
6.inetd守护进程,侦听系统网络接口,获取网络服务进程的请求。
守护进程的两个基本要求:
①必须是init进程的子进程。
②不与任何控制终端相关联。
守护进程的编程规则:
1.调用umask()
将文本模式创建屏蔽字设置为0。
由继承得来的文件模式创建屏蔽字可能会拒绝设置某些文件。
2.调用fork()创建新的进程。
该进程会是将来的守护进程。
3.父进程中调用exit()
。
保证祖父进程(守护进程的祖父进程)确认父进程已结束。还保证了父进程不再继续运行,守护进程不是进程组的组长进程。最后一点是顺利完成以下步骤的前提。
4.调用setsid()
使得守护进程有一个新进程组和新会话,两者都把它作为首进程。
保证它不会与控制终端相关联(因为进程刚刚创建了新的会话,同时也就不会为其关联一个控制终端)。
5.调用chdir()
将当前工作目录改为根目录。
因为前面调用fork()
创建新进程,它所继承的当前工作目录可能在文件系统中任何地方。而守护进程通常在系统启动时运行,同时不希望一些随机目录保持打开状态,也就阻止了管理员卸载守护进程工作目录所在的那个文件系统。
6.关闭所有的文件描述符。
不需要继承任何打开的文件描述符,对于无法确认的文件描述符,让它们继续处于打开状态。
可使用open_max()
或getrlimit()
判定最高文件描述符值,并关闭直到该值的所有描述符。
7.某些守护进程打开/dev/null
使其具有文件描述符0、1、2。
这样,任何一个试图读标准输入,写标准输出或标准出错的库示例不会产生任何效果。因为守护进程并不与终端设备相关联,故不能在终端设备上显示其输出,也无处从交互式用户处接收输入。
初始化守护进程的代码:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char *argv[])
{
/* STEP 0:变量声明 */
int i;
pid_t pid;
struct rlimit rl;
/* STEP 1 :清除文件模式创建屏蔽字为设置为0 */
umask(0);
/* STEP 2 :创建新进程 */
pid = fork();
if ( pid < 0) //创建出错
fprintf(stderr, "can't fork");
/* STEP 3 :结束父进程 */
if (pid > 0) //父进程
return -1;
/* STEP 4 :创建新会话 */
if (setsid() == -1)
return -1;
/* STEP 5 :将当前的工作目录更改为根目录,这样我们就不会阻止文件系统被卸载 */
if (chdir("/") < 0)
fprintf(stderr, "can't change directory to / ");
/* STEP 6 :关闭所有的文件描述符 */
if (getrlimit(RLIMIT_NOFILE,&rl) < 0) //获取文件描述符的最大数量
fprintf(stderr, "can't get file limit");
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);
/* STEP 7 :文件描述符0、1和2附加到/dev/null*/
open("/dev/null", O_RDWR); //stdin
dup(0); //stdout
dup(0); //stderror
/* do its daemon thing.. . */
return 0;
}
许多Unix系统在它们的C函数库中提供了daemon()
函数来完成这些工作,将繁琐的工作简化了很多:
#include <unistd.h>
int daemon (int nochdir,int noclose) ;
若nochdir
非零,就不会将工作目录改为根目录。
若noclose
非零,就不关闭所有打开的文件描述符。
若父进程设置了守护进程的这些属性,那么这些参数是很有用的。通常都会把这些参数设为0。
成功时,返回0。失败时返回-1。errno设置为fork()或者setsid()的错误代码之一。