⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 51.htm

📁 unix高级编程原吗
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>123</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center">               ● UNIX网络编程                       (BM: clown)                </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p   align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="20.htm">上一层</a>][<a href="52.htm">下一篇</a>]
<hr><p align="left"><small>发信人: fist (星仔迷), 信区: LINUX <br>

标  题: Linux网络编程入门教程(2) <br>

发信站: 武汉白云黄鹤站 (Tue Feb 20 11:56:27 2001) , 转信 <br>

◆daemonize()函数创建守护进程 <br>

  在对main()函数进行介绍的时候我就提到过,一般服务器程序在接收客户机连接请 <br>

  <br>

求之前,都要创建一个守护进程。守护进程是linux/Unix编程中一个非常重要的概念, <br>

  <br>

因为在创建一个守护进程的时候,我们要接触到子进程、进程组、会晤期、信号机制以 <br>

  <br>

及文件、目录、控制终端等多个概念,因此详细地讨论一下守护进程,对初学者学习进 <br>

  <br>

程间关系是非常有帮助的。下面就是例程中的daemonize()函数: <br>

----------------------------------------------------------------- <br>

/**************************************************************** <br>

function:   daemonize <br>

description: detach the server process from the current context, creating a <br>

  <br>

 pristine, predictable        environment in which it will execute. <br>

arguments:  servfd file descriptor in use by server. <br>

return value: none. <br>

calls:    none. <br>

globals:   none. <br>



****************************************************************/ <br>

void daemonize (servfd) <br>

int servfd; <br>

{ <br>

  int childpid, fd, fdtablesize; <br>

  /* ignore terminal I/O, stop signals */ <br>

   signal(SIGTTOU,SIG_IGN); <br>

   signal(SIGTTIN,SIG_IGN); <br>

   signal(SIGTSTP,SIG_IGN); <br>

  /* fork to put us in the background (whether or not the user <br>

   specified '&' on the command line */ <br>

  if ((childpid = fork()) < 0) { <br>

    fputs("failed to fork first child\r\n",stderr); <br>

    exit(1); <br>

   } <br>

  else if (childpid > 0) <br>

   exit(0); /* terminate parent, continue in child */ <br>

   /* dissociate from process group */ <br>

  if (setpgrp(0,getpid())<0) { <br>

    fputs("failed to become process group leader\r\n",stderr); <br>

    exit(1); <br>

  } <br>

  } <br>

  /* lose controlling terminal */ <br>

  if ((fd = open("/dev/tty",O_RDWR)) >= 0) { <br>

    ioctl(fd,TIOCNOTTY,NULL); <br>

    close(fd); <br>

  } <br>

  /* close any open file descriptors */ <br>

  for (fd = 0, fdtablesize = getdtablesize(); fd < fdtablesize; fd++) <br>

  if (fd != servfd) <br>

close(fd); <br>

  5.调用函数chdir("/")将当前工作目录更改为根目录。这是为了保证我们的进程不 <br>

使 <br>

用 <br>

任何目录。否则我们的守护进程将一直占用某个目录,这可能会造成超级用户不 <br>

能卸载一个 <br>

文件系统。 <br>

  6.调用函数umask(0)将文件方式创建屏蔽字设置为"0"。这是因为由继承得来的文 <br>

件创 <br>

建方式屏蔽字可能会禁止某些许可权。例如我们的守护进程需要创建一组可读可写的文 <br>

件, <br>

而此守护进程从父进程那里继承来的文件创建方式屏蔽字却有可能屏蔽掉了这两种许可 <br>

权, <br>

则新创建的一组文件其读或写操作就不能生效。因此要将文件方式创建屏蔽字设置为"0 <br>



"。 <br>

  在daemonize()函数的最后,我们可以看到这样的信号捕捉处理语句: <br>

   signal(SIGCLD,(Sigfunc *)reap_status); <br>

  这不是创建守护进程过程中必须的一步,它的作用是调用我们自定义的reap_statu <br>

s() <br>

函 <br>

数来处理僵死进程。reap_status()在例程中的定义为: <br>

----------------------------------------------------------------- <br>

/**************************************************************** <br>

function:    reap_status <br>

description:   handle a SIGCLD signal by reaping the exit status of the per <br>

ishe <br>

d child, and            discarding it. <br>

arguments:    none. <br>

return value:  none. <br>

calls:      none. <br>

globals:     none. <br>

****************************************************************/ <br>

void reap_status() <br>

{ <br>

  int pid; <br>

  union wait status; <br>



  while ((pid = wait3(&status,WNOHANG,NULL)) > 0) <br>

  ; /* loop while there are more dead children */ <br>

} <br>

----------------------------------------------------------------- <br>

  上面信号捕捉语句的原文为: <br>

   signal(SIGCLD, reap_status); <br>

  我们刚才说过,signal()函数的第二个参数一定要有有一个整型参数但是没有返回 <br>

值。 <br>

而reap_status()是没有参数的,所以原来的语句在编译时无法通过。所以我在预编译部 <br>

分 <br>

加 <br>

入了对Sigfunc()的类型定义,在这里用做对reap_status进行强制类型转换。而 <br>

且在BSD系 <br>

统 <br>

中通常都使用SIGCHLD信号来处理子进程终止的有关信息,SIGCLD是Syst <br>

em V中定义的一个 <br>

信 <br>

号名,如果将SIGCLD信号的处理方式设定为捕捉,那么内核将马 <br>

上检查系统中是否存在已经 <br>

终止等待处理的子进程,如果有,则立即调用信号捕捉处理程序。 <br>

  一般在信号捕捉处理程序中都要调用wait()、waitpid()、wait3()或是wait4()来返 <br>

回 <br>

回 <br>

子 <br>

进程的终止状态。这些"等待"函数的区别是,当要求函数"等待"的子进程还没有 <br>

终止时,wa <br>

it()将使其调用者阻塞;而在waitpid()的参数中可以设定使调用者不发生阻塞,wait( <br>

)函 <br>

数 <br>

不被设置为等待哪个具体的子进程,它等待调用者所有子进程中首先终止的那个 <br>

,而在调用 <br>

waitpid()时却必须在参数中设定被等待的子进程ID。而wait3()和wait4()的参数分别比 <br>

wai <br>

t()和waitpid()还要多一个"rusage"。例程中的reap_status()就调用了函数wait3(), <br>

这个 <br>

函数是BSD系统支持的,我们把它和wait4()的定义一起列出来: <br>

----------------------------------------------------------------- <br>

#include <sys/types.h> <br>

#include <sys/wait.h> <br>

#include <sys/time.h> <br>

#include <sys/resource.h> <br>

pid_t wait3(int *statloc, int options, struct rusage *rusage); <br>

pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); <br>

----------------------------------------------------------------- <br>

  其中指针statloc如果不为"NULL",那么它将指向返回的子进程终止状态。参数pid <br>



是我 <br>

们指定的被等待的子进程的进程ID。参数options是我们的控制选择项,一般为WNOHANG <br>

或是 <br>

WUNTRACED。例程中使用了选项WNOHANG,意即如果不能立即返回子进程的终止状态(譬 <br>

如由 <br>

于子进程还未结束),那么等待函数不阻塞,此时返回"0"。      WUNTRACED选项 <br>

的 <br>

意 <br>

思是如果系统支持作业控制,如果要等待的子进程的状态已经暂停,而且其状态 <br>

自从暂停以 <br>

来还从未报告过,则返回其状态。参数rusage如果不为"NULL",则它将指向内核返回的 <br>

由终 <br>

止进程及其所有子进程使用的资源摘要,该摘要包括用户CPU时间总量、缺页次数、接收 <br>

到 <br>

信 <br>

号的次数等。 <br>

◆代理服务程序do_proxy() <br>

  在例程main()函数快要结束时,我们看到,在服务器接受了客户机的连接请求后, <br>

将为 <br>

其创建子进程,并在子进程中执行代理服务程序do_proxy()。 <br>

-----------------------------------------------------------------/********** <br>

**** <br>

**** <br>

************************************************** <br>

function:    do_proxy <br>

description:  does the actual work of virtually connecting a client to the <br>

teln <br>

et service on the          isolated host. <br>

arguments:   usersockfd socket to which the client is connected. return va <br>

lue: <br>

 none. <br>

calls:     none. <br>

globals:     reads hostaddr. <br>

****************************************************************/ <br>

void do_proxy (usersockfd) <br>

int usersockfd; <br>

{ <br>

  int isosockfd; <br>

  fd_set rdfdset; <br>

  int connstat; <br>

  int iolen; <br>

  char buf[2048]; <br>

  /* open a socket to connect to the isolated host */ <br>

  if ((isosockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) <br>

  errorout("failed to create socket to host"); <br>



  /* attempt a connection */ <br>

  connstat = connect(isosockfd,(struct sockaddr *) &hostaddr, sizeof(hosta <br>

ddr) <br>

); <br>

  switch (connstat) { <br>

  case 0: <br>

  break; <br>

  case ETIMEDOUT: <br>

  case ECONNREFUSED: <br>

  case ENETUNREACH: <br>

  strcpy(buf,sys_myerrlist[errno]); <br>

  strcat(buf,"\r\n"); <br>

  write(usersockfd,buf,strlen(buf)); <br>

  close(usersockfd); <br>

  exit(1); <br>

  /* die peacefully if we can't establish a connection */ <br>

  break; <br>

  default: <br>

  errorout("failed to connect to host"); <br>

  } <br>

  /* now we're connected, serve fall into the data echo loop */ <br>

  while (1) { <br>



    /* Select for readability on either of our two sockets */ <br>

    FD_ZERO(&rdfdset); <br>

    FD_SET(usersockfd,&rdfdset); <br>

    FD_SET(isosockfd,&rdfdset); <br>

  if (select(FD_SETSIZE,&rdfdset,NULL,NULL,NULL) < 0) <br>

   errorout("select failed"); <br>

   /* is the client sending data? */ <br>

  if (FD_ISSET(usersockfd,&rdfdset)) { <br>

    if ((iolen = read(usersockfd,buf,sizeof(buf))) <= 0) <br>

     break; /* zero length means the client disconnected */ <br>

     rite(isosockfd,buf,iolen); <br>

     /* copy to host -- blocking semantics */ <br>

   } <br>

  /* is the host sending data? */ <br>

  if (FD_ISSET(isosockfd,&rdfdset)) { <br>

    f ((iolen = read(isosockfd,buf,sizeof(buf))) <= 0) <br>

      break; /* zero length means the host disconnected */ <br>

      rite(usersockfd,buf,iolen); <br>

      /* copy to client -- blocking semantics */ <br>

    } <br>

   } <br>

   /* we're done with the sockets */ <br>



   close(isosockfd); <br>

   lose(usersockfd); <br>

  } <br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -