Linux环境编程之进程(七):守护进程

守护进程也是一种进程,它由如下特性:

1、生存期较长,在系统自举时启动,仅在系统关闭时终止。

2、没有控制终端,在后台运行。

系统中有很多守护进程,它们执行日常事务活动。如日志进程syslogd、web服务器httpd、邮件服务器sendmail和数据块服务器mysqld等。大多数守护进程都是以超级用户(用户ID为0)特权运行。没有一个守护进程具有控制终端,其终端设置为问号(?),终端前台进程组ID设置为-1。内核守护进程以无控制终端方式启动。用户层守护进程缺少控制终端可能是守护进程调用了setsid的结果。所有用户层守护进程都是进程组长进程以及会话的首进程,而且是这些进程组和会话中的唯一进程。最后,大多数守护进程的父进程是init进程。

(一)编程规则

1、调用umask将文件模式创建屏蔽字设置为0。

2、调用fork,然后使父进程退出(exit)。

3、调用setsid以创建一个新会话。使调用进程:a、成为新会话的首进程;b、成为一个新进程组的组长进程;c、没有控制终端。

4、将当前工作目录更改为根目录。

5、关闭不需要的文件描述符。

6、某些守护进程打开/dev/null使其具有文件描述符0、1和2。

(二)出错记录

因为守护进程没有控制终端,所以不能只是简单地写到标准出错上。那么该如何处理出错消息呢?我们不希望所有守护进程都写到控制台设备上,也不希望每个守护进程将它自己的出错消息写到一个单独的文件中,因为要关心哪一个守护进程写到哪一个记录文件中,并定期地检查这些文件,会很麻烦。所以,需要有一个集中的守护进程出错记录设施。

如上图所示,有3种方法产生日志消息:

1、内核例程可以调用log函数。任何一个用户进程通过打开(open)然后读(read)/dev/klog设备就可以读取这些消息。

2、大多数用户进程(守护进程)调用syslog函数以产生日志消息。//下面给出例子

3、在此主机上的一个用户进程,或通过TCP/IP网络连接到此主机的其他主机上的一个用户进程可将日志消息发向UDP端口514。

#include <syslog.h>

void openlog(const char *ident, int option, int facility);

void syslog(int priority, const char *format, ...);

void closelog(void);

参数:

identity一般是程序的名称。

option参数是指定许多选项的位屏蔽。

priority参数是facility和level的组合。

(三)示例程序

/*
 *File Name : daemo.c
 *Autho     : libing
 *Mail      : [email protected]
 */
#include "stdio.h"
#include "stdlib.h"
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/resource.h>

int daemonize(void)
{
	int 	i, fd0, fd1, fd2;
	pid_t	pid;
	struct rlimit rl;

	/*
	 *调用umask将文件模式创建屏蔽字设置为0
	 */
	umask(0);

	/*
	 *调用fork,然后使父进程退出(exit)
	 */
	if((pid = fork()) < 0){
		printf("can‘t fork.");
		return -1;
	} else if(pid != 0) /*parent*/
		exit(0);
	setsid();//调用setsid以创建一个新会话

	/*
	 *将当前工作目录更改为根目录
	 */
	if(chdir("/") < 0)
		printf("can‘t change directory to/");

	/*
	 *Get maximum number of file descriptors.
	 */
	if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
		printf("can‘t get file limit.\n");
	/*
	 *关闭不需要的文件描述符.
	 */
	if(rl.rlim_max == RLIM_INFINITY)
		rl.rlim_max = 1024;
	for(i = 0; i < rl.rlim_max; i++)
		close(i);

	/*
	 *守护进程打开/dev/null使其具有文件描述符0、1和2。
	 */
	fd0 = open("/dev/null", O_RDWR);
	fd1 = dup(0);
	fd2 = dup(0);

	return 0;
}

int
main(void)
{
	daemonize();
	openlog("daemotest", LOG_PID, LOG_USER);
	syslog(LOG_INFO, "program started.");
	while(1){
		sleep(1);
	}
	return 0;
}

运行测试结果:

编译:gcc daemo.c -o daemotest
运行:./daemotest
测试:ps axj | grep daemotest
1  6565  6565  6565 ?           -1 Ss    1000   0:00 ./daemotest

可以看出daemotest进程的父进程是1,没有终端控制(TTY选项为“?”)。其记录在/var/log/messages文件中,可以在该文件中找到"program started"字样的记录。

Linux环境编程之进程(七):守护进程

时间: 2024-08-06 07:55:40

Linux环境编程之进程(七):守护进程的相关文章

Linux环境编程之进程(一):main函数调用、进程终止以及命令行参数和环境表

(一)main函数调用 main函数作为程序运行时的入口函数,它是如何被调用的呢?首先必须清楚一点,main函数也是一个函数,它只有被调用才能够执行.其实,在执行可执行程序时,在调用main函数之前,内核会先调用一个特殊的启动例程,将此启动例程作为可执行程序的起始地址.启动例程是如何作为可执行程序的起始地址的?这是由链接编译器设置的,而链接编译器则是由C编译器(如gcc编译器)调用的.启动例程作为可执行程序的起始地址主要做哪些工作呢?启动例程从内核取得命令行参数和环境变量值,以此来为main函数

Linux环境编程之进程(六):进程组

进程组 每个进程除了有一个进程ID之外,还属于一个进程组.进程组是一个或多个进程的集合.每个进程组有一个唯一的进程组ID.进程组ID类似于进程ID--它是一个整数,并可存放在pid_t数据类型中.函数getpgrp返回调用进程的进程组ID. 每个进程组都可以有一个组长进程.组长进程的标识是,其进程组ID等于其进程ID.组长进程可以创建一个进程组,创建该组中的进程,然后终止.只要在某个进程组中有一个进程存在,则进程组就存在,这与其组长进程是否终止无关.从进程组创建开始到其中最后一个进程离开为止的时

Linux环境编程之进程(二):程序的存储空间布局

引言: 一个写好的程序一般要存放在存储器中,那么程序中的代码.数据等各部分,是如何有规律的存放在存储器中的呢? (一) 一个存储的程序可分为五部分:正文段.初始化数据段.非初始化数据段.栈.堆.其典型的存储安排如下图: 正文段:这是由CPU执行的机器指令的部分.通常,正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需要一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其自身的指令. 初始化数据段:通常称为数据段,它包含了程序中需要明确地赋初值的变量. 非初始化数据段:通常称为bs

Linux环境编程之进程(四):创建新进程、执行程序和进程终止

引言: 对于每个进程,都有一个非负整数表示的唯一进程ID.虽然进程的ID是唯一的,但却是可重用的.系统中有一些专用的进程.如ID为0的进程通常是调度进程,也成交换进程或系统进程(它是内核进程).进程ID为1通常是init进程,它是一个普通的用户进程.一些与进程ID有关的函数: #include <unistd.h> pid_t getpid(void);   //返回值:调用进程的进程ID pit_t getppid(void); //返回值:调用进程的父进程ID uid_t getuid(v

Linux环境编程之进程(五):竞争条件以及exec函数

(一) 当多个进程企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,就认为它们发生了竞争关系.避免竞争的条件,给出apue上的一个代码吧: #include "apue.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); /*set things up for TELL_XXX & WAIT_XXX*/ if((pid == fork()) < 0){ e

Linux环境编程之进程(三):函数间跳转

引言:在编写程序时,经常在函数内部使用goto语句来跳转,从而进行出错处理,那么如果想要在函数之间进行跳转该怎么做呢?使用setjmp和longjmp函数. 给出示例程序: #include <stdio.h> #include <stdlib.h> #include <setjmp.h> static void f1(int, int, int, int); static void f2(void); static jmp_buf jmpbuffer; static

Linux系统编程——特殊进程之守护进程

什么是守护进程? 守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件. 守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示.由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都

Linux进程学习(孤儿进程和守护进程)

孤儿进程和守护进程 通过前面的学习我们了解了如何通过fork()函数和vfork()函数来创建一个进程.现在 我们继续深入来学习两个特殊的进程:孤儿进程和守护进程 一.孤儿进程 1.什么是 孤儿进程如果一个子进程的父进程先于子进程 结束, 子进程就成为一个孤儿进程,它由 init 进程收养,成为 init 进程的子进程.2.那么如何让一个进程变为一个孤儿进程呢?我们可以先创建一个进程,然后杀死其父进程,则其就变成了孤儿进程.pid =  fork();if(pid > 0) {         

Linux进程学习 - 孤儿进程和守护进程

孤儿进程和守护进程 通过前面的学习我们了解了如何通过fork()函数和vfork()函数来创建一个进程.现在 我们继续深入来学习两个特殊的进程:孤儿进程和守护进程 一.孤儿进程 1.什么是 孤儿进程如果一个子进程的父进程先于子进程 结束, 子进程就成为一个孤儿进程,它由 init 进程收养,成为 init 进程的子进程.2.那么如何让一个进程变为一个孤儿进程呢?我们可以先创建一个进程,然后杀死其父进程,则其就变成了孤儿进程.pid =  fork();if(pid > 0) {