Linux下的网络编程分为两部分:服务器编程和客户机编程。一般服务器程式在接收客户机连接请求之前,都要创建一个守护进程。守护进程是linux/Unix编程中一个很重要的概念,因为在创建一个守护进程的时候,我们要接触到子进程、进程组、会晤期、信号机制连同文档、目录、控制终端等多个概念,因此周详地讨论一下守护进程,对初学者学习进程间关系是很有帮助的。


首先看一段将普通进程转换为守护进程的代码:

---------------------------

/****************************************************************
function:   daemonize
description: detach the server process from the current context, creating a pristine, predictable        environment in which it will execute.
arguments:  servfd file descriptor in use by server.
return value: none.
calls:    none.
globals:   none.
****************************************************************/
void daemonize (servfd)
int servfd;
{
  int childpid, fd, fdtablesize;
  /* ignore terminal I/O, stop signals */
   signal(SIGTTOU,SIG_IGN);
   signal(SIGTTIN,SIG_IGN);
   signal(SIGTSTP,SIG_IGN);
  /* fork to put us in the background (whether or not the user
   specified '&' on the command line */
  if ((childpid = fork()) < 0) {
    fputs("failed to fork first childrn",stderr);
    exit(1);
   }
  else if (childpid > 0)
   exit(0); /* terminate parent, continue in child */
   /* dissociate from process group */
  if (setpgrp(0,getpid())<0) {
    fputs("failed to become process group leaderrn",stderr);
    exit(1);
  }
  /* lose controlling terminal */
  if ((fd = open("/dev/tty",O_RDWR)) >= 0) {
    ioctl(fd,TIOCNOTTY,NULL);
    close(fd);
  }
  /* close any open file descriptors */
  for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd )
  if (fd != servfd)
   close(fd);
   /* set working directory to allow filesystems to be unmounted */
   chdir("/");
   /* clear the inherited umask */
   umask(0);
   /* setup zombie prevention */
   signal(SIGCLD,(Sigfunc *)reap_status);
  }

---------------------------

在linux系统中,假如要将一个普通进程转换为守护进程,需要执行的步骤如下:

1、调用fork()函数创建子进程,然后中止父进程,保留子进程继续运行。因为,当一个进程是以前台进程的方式由shell启动时,假如中止了父进程,子进程就会自动转为后台进程。另外,在下一步时,我们需要创建一个新的会晤期,这就需要创建会晤期的进程不是个进程组的组长进程。当父进程中止,子进程继续运行时,就确保了进程组的组ID和子进程的进程ID不会相等。
fork()函数的定义为:
----------------------
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
----------------------
fork()函数被调用一次,会返回两次值。这两次返回的值分别是子进程的返回值和父进程的返回值,子进程的返回值为“0”,父进程的返回值为子进程的进程ID。假如出错返回“-1”。

1、确保进程不会获得任何控制终端。这是为了避免在关闭某些终端时会显示有程式正在运行而无法关闭的情况。这一步通常的做法是:调用函数setsid()创建一个新的会晤期。
setsid()函数的定义为:
----------------------
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
----------------------
在第一步里我们已确保了调用此函数的进程不是进程组的组长,那么调用此函数将创建一个新的会晤。其结果是:首先,此进程编程该会晤期的首进程(session leader,系统默认会晤期的首进程是创建该会晤期的进程)。而且,此进程是该会晤期中的唯一进程。然后,此进程将成为一个新的进程组的组长,新进程组的组ID就是该进程的进程ID。最后,确保此进程没有控制终端,即使在调用setsid()之前此进程拥有控制终端,在创建会晤期后这种联系也将被解除。假如调用该函数的进程是个进程组的组长,那么函数将返回出错信息“-1”。

更有一个方法能够让进程无法获得控制终端,如下:
----------------------
if((fd = fopen("/dev/tty",0_RDWR)) >= 0){
ioctl(fd,TIOCNOTTY,NULL);
close(fd);
}
----------------------
其中/dev/tty是个流设备,也是我们的终端映射。调用close()函数将终端关闭。

3、信号处理。一般要忽略掉某些信号。信号相当于软件中断,Linux/Unix下的信号机制提供了一种处理异步事件的方法,终端用户键入引发中断的键,或是系统发出信号,这都会通过信号处理机制终止一个或多个程式的运行。

不同情况下引发的信号不同,任何的信号都有自己的名字,这些名字都是以“SIG”开头的,只是后面有所不同。我们能够通过这些名字了解到系统中到底发生了什么事。当信号出现时,我们能够需要系统进行一下三种操作:

a、忽略信号。大多数信号都采用这种处理方法,但是对SIGKILL和SIGSTOP信号不能做忽略处理。

b、捕获信号。这是一种最为灵活的操作方式。这种处理方式的意思就是:当某种信号发生时,我们能够调用一个函数对这种情况进行响应的处理。最常见的情况是:假如捕获到SIGCHID信号,则表示子进程已终止,然后可作此信号的捕获函数中调用waitpid()函数取得该子进程的进程ID已他的终止状态。假如进程创建了临时文档,那么就要为进程终止信号SIGTERM编写一个信号捕获函数来清除这些临时文档。

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!