守护进程代码过程概要

来源:互联网 发布:32位ubuntu镜像下载 编辑:程序博客网 时间:2024/06/05 04:39

(一)、守护进程的特征

   #ps  -axj  其中:-a 显示所有用户所拥有的进程的状态

                              -x 显示没有控制终端的进程状态

                              -j 显示与作业相关的的信息:会话ID、进程组ID、控制终端以及终端进程组ID

(二)守护进程的编程规则

(1)、编程规则

1.调用umask()将文件模式创建屏蔽字设置为0.(文件权限——不要变更程序的初始文件权限mode

    由继承提来的文件模式创建屏蔽字可能会拒绝设置某些权限。例如,若守护进程要创建一个组可读、写的文件,而继承的文件模式创建屏蔽字可能屏蔽了这两种权限,于是所要求的组可读、写就不能起作用。

例:umask(0);mode与上umask反码

2.调用fork(),产生一个子进程,然后使父进程退出(exit),子进程变为孤儿进程(init进程的子进程)

这样可以实现:

  1)、如果该守护进程是作为一条简单shell命令启动的,那么父进程终止使得shell认为这条命令已经执行完毕(骗了一下os)

  2)、子进程继承了父进程组ID,但具有一个新的进程ID,这就保证了子进程不是一个进程组的组长进程

if ((pid = fork()) < 0)   perror("%s: can't fork");

else if (pid != 0)       exit(0);           /* parent */

3. 调用setsid()以创建一个新会话(因为父进程退出了,父进程的会话也退出了)

这样可以实现:  1)、成为新会话的首进程

                2)、成为一个新进程组的组长进程

                3)、没有终端控制

例:setsid();

4.将当前的工作目录更改为根目录

从父进程处继承过来的当前工作目录可能在一个装配文件系统中。因为守护进程通常在系统再引导之前是一直存在的,所以如果守护进程的当前工作目录在一个装配文件系统中,那么该文件系统就不能被umount,这与装配文件系统的原意不符

 if (chdir("/")< 0)     perror("can't changedirectory to /");

5、关闭不再需要的文件描述符(守护进程无输入输出,文件描述符继承自父进程,守护进程不需要,全部关闭

这使守护进程不再持有从其父进程继承过来的某些文件描述符(父进程可能是shell进程,或某个其它进程),可以使用getrlimit()函数来判定最高文件描述符值,并关闭直到该值的所有描述符

Shell指令:#ulimit  -a

 

if (rl.rlim_max == RLIM_INFINITY)        rl.rlim_max = 1024;//os只允许一个进程打开1024个文件

for (i = 0; i < rl.rlim_max; i++)    close(i);//系统调用

 

补充:资源限制

系统资源的限制可由getrlimit读取,由setrlimit设置

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit*r_limit);

int setrlimit(int resource, const structrlimit *r_limit);

其中:struct rlimit定义于sys/resource.h,至少包括下列两个成员

      {  rlim_t  rlim_cur;    当前的软限制(当前文件打开数>1024时,发出警告)

        rlim_t rlim_max;    硬限制(当前文件打开数>1024时,由OS杀死进程)

}

rlim_t是一个整数类型用于描述资源级别(typdefunsigned int rlim_t)

通常soft limit是一建议限制,意味着不应超越,如果超越会引起库函数返回错误

    Hard limit是硬性限制,若超越意味着将导致系统发送信号以终结程序的运行

例:若超越CPU时间,将导致内核发送SIGXCPU

    若超越数据大小限制,将导致内核发送SIGSEGV

一个程序可以设置软限制,一般软限制小于hard limit

仅有具有superuser privileges的程序可以增大hard limit

 

可以被限制的资源被定义于sys/resource.h中,包括:

 

CPU使用量

数据段大小

文件大小(<4G)

打开文件数(一般1024)

每个进程用的堆栈大小

每个进程用的地   址空间,一般小于4G


返回值:Successful   0

              error       -1

6、某些守护进程打开/dev/null(空洞设备)使其具有文件描述符0stdin)、1stdout)和2stderr

这样任何一个试图读标准输入、写标准输出或标准出错的库例程都不会产生任何效果

因为守护进程并不与终端设备相关联,所以不能在终端设备上显示其输出,也无处从交互用户那里接收输入。

 fd0= open("/dev/null", O_RDWR);// 0stdin)、1stdout)和2stderr)全部放进/dev/null(空洞设备)中

       fd1 = dup(0);

       fd2 = dup(0);

(2)、syslog设施(Linux/UNIX:消息写入syslog设施——写入log文件中)

守护进程存在的一个问题是如何处理出错消息。因为它本就不应该有控制终端,所以不能只是简单地写到标准错误上。也不希望所有守护进程都写到控制台(console)设备上,因为在很多工作站上控制台设备都运行着一个窗口系统。也不希望每个守护进程将它自己的出错消息写到一个单独的文件中。所以,需要有一个集中的守护进程出错记录设施。BSD的syslog设施得到了广泛的应用。大多数守护进程都使用这一设施。

有3种产生日志消息的方法:

1)内核例程可以调用log函数。

    任何一个用户进程都可以通过打开(open)并读取(read)  /dev/klog设备来读取这些消息。

2)大多数用户进程(守护进程)调用syslog(3)函数来产生日志消息,这使消息被发送至UNIX域数据报套接字/dev/log

3)无论一个用户进程是在此主机上,还是在通过TCP/IP网络连接到此主机的其它主机上,都可以将日志消息发向UDP 514端口。注意syslog函数从不产生这些UDP数据报,它们要求产生此日志消息的进程进行显式的网络编程。

通常,syslogd守护进程读取所有3种格式的日志消息。此守护进程在启动时读一个配置文件---/etc/syslog.conf,该文件决定了不同种类的消息应送向何处

2、syslog设施的接口syslog函数

#include <syslog.h>

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

voidsyslog(int priority,  const char *format, ...);              必须

void closelog(void);                                      可选

说明:调用openlog是可选的。如果不调用openlog,则在第一次调用syslog时,自动调用openlog

      调用closelog也是可选的,因为它只是关闭曾被用于与syslogd守护进程进行通信的描述符

 

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

作用:产生一个日志消息,默认打开/dev/log设备

参数:1)priority参数(级别)是facility和level的组合,它们可选取的值分别列于facility和level中 level值按优先级从最高到最低依次排列

      2)format参数(自定义的格式):

将format参数以及其它所有参数传到vsprintf函数以便进行格式化。在format中,每个出现的%m字符都先被代换成与errno值对应的出错消息字符串(strerror)


(四)、实现函数---daemon()

作用:使调用程序run in the background,进程成为守护进程

函数:#include <unistd.h>//系统调用

           intdaemon(int nochdir, int noclose);

参数:

nochdir  若为0,daemon()将调用进程的当前目录更改为“/ ”

            否则,当前进程的工作目录无变化

noclose  若为0,(关)daemon()redirects standard input, standard output and standard error to /dev/null;

            否则 no changes are made to these file descriptors.

返回值  //成功与否的标志

On success daemon() returns 0.

If an error occurs, daemon() returns -1 andsets errno to any of the errors specified for the  fork  and setsid