免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3779 | 回复: 8
打印 上一主题 下一主题

诡异的守护进程中的文件描述符,标题要长长长长长。。 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-08-31 20:34 |只看该作者 |倒序浏览
本帖最后由 ChiyuT 于 2011-08-31 20:36 编辑

今天被这个诡异的问题弄得茶不思来饭不想, 哪位达人能够拯救我!!

问题如下:

主程序是一段很简单的代码,我放到最前边。里边的 daemon_init 和各种打印函数都是直接照抄的《UNIX网络编程》第一卷中的附录D,我放到后边,大家有兴趣的可以自己研究。
  1. #include <stdio.h>
  2. #include <syslog.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <string.h>
  6. #include <fcntl.h>
  7. #include <stdarg.h>
  8. #include <syslog.h>
  9. #include <stdlib.h>
  10. #include <errno.h>

  11. int main(int argc, char * argv[])
  12. {

  13.         daemon_init(argv[0], 0);
  14.        
  15.         int fd = open("tmp1", O_RDWR | O_CREAT | O_TRUNC, 0666);
  16.         err_ret("fd = %d\n", fd);

  17.         write(fd, "hello world!", strlen("hello world!"));

  18.         fd = open("tmp2", O_RDWR | O_CREAT | O_TRUNC, 0666);
  19.         err_ret("fd = %d\n", fd);

  20.         return 0;
  21. }
复制代码
然后我检查log,记录如下:
  1. 7444 Aug 31 20:23:44 localhost ./daemon[5901]: fd = 3: Bad file descriptor
  2. 7445 Aug 31 20:23:44 localhost ./daemon[5901]: fd = 5: Bad file descriptor
复制代码
问题一:   

为什么文件描述符不是连续的? 4 跑到哪儿去了????Bad file descriptor该怎么理解? 我确实在tmp1里边看到了我写入的内容。

继续,如果我将 daemon_init 中下边代码注释掉,即不关闭所继承的文件描述符。
  1.         /*        Close off file descriptors        */
  2.         for (i = 0; i < MAXFD; i++) {
  3.                 close(i);
  4.         }
复制代码
运行,再次检查log,记录如下:
  1. 7448 Aug 31 20:29:27 localhost ./daemon[6100]: fd = 6: Success
  2. 7449 Aug 31 20:29:27 localhost ./daemon[6100]: fd = 8: Success
复制代码
问题二:

为什么文件描述符不是连续的?为什么是从6开始?3、4、5都跑哪儿去了? 7 又跑到哪儿去了???? 这次为什么是Success ??


相关代码:
  1. #define MAXFD 64
  2. #define MAXLINE 4096

  3. int daemon_proc;

  4. static void err_doit(int, int, const char *, va_list);

  5. /*
  6. *        Nonfatal error related to a system call,
  7. *        print a message and return.
  8. */
  9. void err_ret(const char * fmt, ...)
  10. {
  11.         va_list ap;
  12.        
  13.         va_start(ap, fmt);
  14.         err_doit(1, LOG_INFO, fmt, ap);
  15.         va_end(ap);

  16.         return;
  17. }

  18. /*
  19. *        Fatal error related to a system call,
  20. *        print a message and terminate.
  21. */
  22. void err_sys(const char * fmt, ...)
  23. {
  24.         va_list ap;
  25.        
  26.         va_start(ap, fmt);
  27.         err_doit(1, LOG_ERR, fmt, ap);
  28.         va_end(ap);

  29.         exit(1);
  30. }

  31. /*
  32. *        Fatal error related to a system call,
  33. *        print a message, dump core, and terminate.
  34. */
  35. void err_dump(const char * fmt, ...)
  36. {
  37.         va_list ap;
  38.        
  39.         va_start(ap, fmt);
  40.         err_doit(1, LOG_ERR, fmt, ap);
  41.         va_end(ap);
  42.         abort();
  43.         exit(1);
  44. }

  45. /*
  46. *        Nonfatal error unrelated to a system call,
  47. *        print a message and return
  48. */
  49. void err_msg(const char * fmt, ...)
  50. {
  51.         va_list ap;
  52.        
  53.         va_start(ap, fmt);
  54.         err_doit(0, LOG_INFO, fmt, ap);
  55.         va_end(ap);

  56.         return;
  57. }

  58. /*
  59. *        Fatal error unrelated to a system call,
  60. *        print a message and terminate.
  61. */
  62. void err_quit(const char * fmt, ...)
  63. {
  64.         va_list ap;
  65.        
  66.         va_start(ap, fmt);
  67.         err_doit(0, LOG_ERR, fmt, ap);
  68.         va_end(ap);

  69.         exit(1);
  70. }

  71. /*
  72. *        Print a message and return to caller,
  73. *        caller specifies "errnoflag" and "level".
  74. */
  75. static void err_doit(int errnoflag, int level, const char * fmt, va_list ap)
  76. {
  77.         int errno_save, n;
  78.         char buf[MAXLINE + 1];

  79.         errno_save = errno;                                        /*        Value caller might want printed.        */
  80. #ifdef HAVE_VSNPRINTF
  81.         vsnprintf(buf, MAXLINE, fmt, ap);
  82. #else
  83.         vsprintf(buf, fmt, ap);                                /*        This is not safe        */
  84. #endif
  85.         n = strlen(buf);
  86.         if (errnoflag) {
  87.                 snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
  88.         }
  89.         strcat(buf, "\n");

  90.         if (daemon_proc) {
  91.                 syslog(level, buf);
  92.         } else {
  93.                 fflush(stdout);                                        /*        In case stdout and stderr are the same        */
  94.                 fputs(buf, stderr);
  95.                 fflush(stderr);
  96.         }

  97.         return;
  98. }

  99. int daemon_init(const char * name, int facility)
  100. {
  101.         int                i;
  102.         pid_t        pid;
  103.         struct sigaction sa_dae;

  104.         if ((pid = fork()) < 0) {
  105.                 return -1;
  106.         } else if (pid) {
  107.                 _exit(0);                                                        /*        Parent terminates        */
  108.         }

  109.         /*        Child 1 continue...        */
  110.         if (setsid() < 0) {                                                /*        Become session leader        */
  111.                 return -1;
  112.         }
  113.        
  114.         sa_dae.sa_handler = SIG_IGN;
  115.         if ((sigaction(SIGHUP, &sa_dae, NULL)) < 0) {
  116.                 perror("sigaction");
  117.                 return -1;
  118.         }

  119.         if ((pid = fork()) < 0) {
  120.                 return -1;
  121.         } else if (pid) {
  122.                 _exit(0);                                                        /*        Child 1 terminates        */
  123.         }

  124.         /*        Child 2 continues...        */
  125.         daemon_proc = 1;

  126. //        chdir("/");                                                                /*        Change working directory        */

  127.         /*        Close off file descriptors        */
  128.         for (i = 0; i < MAXFD; i++) {
  129.                 close(i);
  130.         }

  131.         /*        Redirect stdin, stdout, and stderr to /dev/null        */
  132.         open("/dev/null", O_RDONLY);
  133.         open("/dev/null", O_RDWR);
  134.         open("/dev/null", O_RDWR);

  135.         openlog(name, LOG_PID, facility);

  136.         return 0;                                                                /*        Success        */
  137. }
  138.                
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-08-31 21:49 |只看该作者
本帖最后由 雨过白鹭洲 于 2011-08-31 21:53 编辑

一个一个问题来哈


问题一:   

为什么文件描述符不是连续的? 4 跑到哪儿去了????Bad file descriptor该怎么理解? 我确实在tmp1里边看到了我写入的内容。

4跑哪里去了,我也不知道

Bad file descriptor不是因为你open出错了,而是前面daemon_init里面的代码出错设置了errno,而系统调用成功时是不会修改errno的,这样你看到的就是之前的errno错误码
  1. /*        Close off file descriptors        */
  2.         for (i = 0; i < MAXFD; i++) {
  3.                 close(i);
  4.         }

  5.                 errno = 0;
复制代码
很明显是close(i)的时候关闭了并未打开的文件描述符,因此close设置了errno为Bad file descriptor

像上面这样重置一下errno,就不会再看到log中的Bad file descriptor啦

两个文件都是打开成功的,因此你向tmp1写入数据,就能看到里面的内容

论坛徽章:
0
3 [报告]
发表于 2011-08-31 21:55 |只看该作者
本帖最后由 雨过白鹭洲 于 2011-08-31 22:00 编辑

问题二:

为什么文件描述符不是连续的?为什么是从6开始?3、4、5都跑哪儿去了? 7 又跑到哪儿去了???? 这次为什么是Success ??

为什么不连续,我同样不知道

从6开始是因为系统默认为你打开了0,1,2三个文件描述符,你把daemon_init中关闭描述符的代码注释了,然后里面又打开了
        /*        Redirect stdin, stdout, and stderr to /dev/null        */
        open("/dev/null", O_RDONLY);
        open("/dev/null", O_RDWR);
        open("/dev/null", O_RDWR);

三个文件描述符,这就是3,4,5

因此你下次在main函数里open自然就是从6开始了。

7跑哪里了?god knows!

为什么是Success,参考问题一,你没有调用close关闭未打开的文件描述符,因此就没有出错并设置errno为bad file descriptor啦

论坛徽章:
0
4 [报告]
发表于 2011-08-31 22:02 |只看该作者
回复 3# 雨过白鹭洲

多谢多谢!恍然大悟。  

现在就剩下那个不连续的问题了。

论坛徽章:
0
5 [报告]
发表于 2011-08-31 22:04 |只看该作者
本帖最后由 ChiyuT 于 2011-08-31 22:06 编辑

我在怀疑是不是syslog本身要占用一个文件描述符。{:3_194:} 因为两个打开文件的操作中间,都调用了一次syslog,估计得研究一下它的实现。

论坛徽章:
0
6 [报告]
发表于 2011-08-31 22:09 |只看该作者
回复 3# 雨过白鹭洲

果然如此,我改了一下代码:
  1.         int fd1 = open("tmp1", O_RDWR | O_CREAT | O_TRUNC, 0666);
  2.        
  3. //        write(fd, "hello world!", strlen("hello world!"));

  4.         int fd2 = open("tmp2", O_RDWR | O_CREAT | O_TRUNC, 0666);
  5.         err_ret("fd1 = %d, fd2 = %d", fd1, fd2);
复制代码
这回就连续了,汗一个:
  1. 8431 Aug 31 22:08:35 localhost ./daemon[3494]: fd1 = 3, fd2 = 4: Bad file descriptor
复制代码

论坛徽章:
0
7 [报告]
发表于 2011-08-31 22:22 |只看该作者
本帖最后由 雨过白鹭洲 于 2011-08-31 22:28 编辑

补充一下为什么4或者7文件描述符不见了

原因是err_ret调用了syslog,而syslog打开并占用了一个文件描述符,标准里面有描述:
The openlog() and syslog() functions  may  allocate  a  file descriptor.   It is not necessary to call openlog() prior to calling syslog().


看下面代码
  1. int fd = open("tmp1", O_RDWR | O_CREAT | O_TRUNC, 0666);
  2. // err_ret("fd = %d\n", fd);

  3. fd = open("tmp2", O_RDWR | O_CREAT | O_TRUNC, 0666);
  4. // err_ret("fd = %d\n", fd);
  5.         
  6. fd = open("tmp3", O_RDWR | O_CREAT | O_TRUNC, 0666);
  7. // err_ret("fd = %d\n", fd);
复制代码
开放第一个err_ret,文件描述符是0,1,2,3,5,6             err_ret使用了4
开放第二个err_ret,文件描述符是0,1,2,3,4,6             err_ret使用了5
开放第三个err_ret,文件描述符是0,1,2,3,4,5             err_ret使用了6


从上面的试验来看,是syslog()而不是openlog()打开了文件描述符,而且重复调用syslog()只会打开一个描述符

论坛徽章:
0
8 [报告]
发表于 2011-08-31 22:53 |只看该作者
回复 7# 雨过白鹭洲

多谢多谢!都如你如言。{:3_192:} 咱可以安心睡个好觉了。

论坛徽章:
0
9 [报告]
发表于 2011-09-06 23:58 |只看该作者
楼主难道没有用过strace吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP