daemon和pthread_create之间的问题(线程阻塞)

背景

最近完成工具链和uClibc库升级的任务,升级之后出现了一个bug,就是大软件起来之后,原先ps能看到6个线程的进程,现在只能看到三个,导致出现了一些功能上的问题。

排查问题

有了问题,当然要处理啦。毕竟是自己埋下的坑,含着泪也要把它填起来。刚开始在代码里面加上打印,发现都没有显示,觉得特别奇怪。心想是不是阻塞在哪个函数了。经过从main函数开始打印,慢慢的定位到daemon这个函数。心想这个函数时做什么的?为什么会阻塞呢?

上网百度之后,发现daemon函数的作用就是将进程放到后台执行。所以我的打印就看不到了,并不是阻塞在这里。

但是奇怪的现象是当我把这个函数注释掉之后,编译出来的程序又是完好如初了。(ps:可以看到6个线程)

这就让我把问题定位在了daemon函数上。后续再进行排查。发现程序在进行第一个pthread_create函数之后,就不再继续放下执行了。(打开daemon函数)

最终我将问题定位在daemon函数和pthread_create两个之间

问题解决

问题解决的过程我就不再详细说明了。因为过程也花了好几天,和老大慢慢确认问题,一起总结。也了解到了很多新的知识点。

首先你要了解以下几点内容:

  1. main函数不是程序最初的入口
    首先第一点,我们之前一直听过一句话,main函数是程序的入口,其实并不是的。你仔细想想就知道肯定不是这样的。因为你并没有初始化堆栈,stdin,stdout,stderr等参数,你是怎么使用的呢?这肯定是在main函数之前就已经设置好的了。

  2. GNU C attribute 设置属性
    GNU C attribute 是GCC的一大特色,他可以为函数,变量,类型赋予属性。

其中

attribute((constructor)) // 构造函数在main函数被调用之前调用

attribute((destructor)) // 析构函数在main函数被调用之后调

  1. 不同的工具链,编译的效果是不同的
    根据背景可知,我升级了工具链。现在用的是463以前用的是342。用他们编译相同的文件,会发现效果不一样,463在main函数之前会执行.gnu_adtribute。而342不会。如下部分代码(汇编):
    342编译器:
 1     .file   1 "uttsn.c"                                        
 2     .section .mdebug.abi32                                     
 3     .previous                                                  
 4     .abicalls
 5     .section    .rodata.str1.4,"aMS",@progbits,1               
 6     .align  2   
 7 $LC0:
 8     .ascii  "son thread %d\n\000"                              
 9     .align  2

463编译器:

 1     .file   1 "uttsn.c"
  2     .section .mdebug.abi32
  3     .previous
  4     .gnu_attribute 4, 3
  5     .abicalls
  6     .section    .rodata.str1.4,"aMS",@progbits,1
  7     .align  2
  8 $LC0:
  9     .ascii  "son thread %d\012\000"

所以463会在main函数之前初始化gnu_attribute相关的东西。

进入正题:
pthread_initialize函数是被设置为构造函数的,在main函数之前执行。该函数的作用是记录主线程的pid

static void pthread_initialize(void) __attribute__((constructor));

daemon函数的源码:

35 int daemon( int nochdir, int noclose )
 36 {
 37     int fd;
 38 
 39     switch (fork()) {
 40         case -1:
 41             return(-1);
 42         case 0:
 43             break;
 44         default:
 45             _exit(0);
 46     }
 47 
 48     if (setsid() == -1)
 49         return(-1);
 50 
 51     /* Make certain we are not a session leader, or else we
 52      * might reacquire a controlling terminal */
 53     if (fork())
 54         _exit(0);
 55 
 56     if (!nochdir)
 57         chdir("/");
 58 
 59     if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
 60         dup2(fd, STDIN_FILENO);
 61         dup2(fd, STDOUT_FILENO);
 62         dup2(fd, STDERR_FILENO);
 63         if (fd > 2)
 64             close(fd);
 65     }
 66     return(0);
 67 }
 68 

综上所述:
用了新的工具链之后出现问题,主要的原因是由于,463支持在main函数之前初始构造函数。记录主线程的pid。而342是不具有这个操作的。

而daemon函数时将主线程exit(0)。这样就导致,463编译的软件,调用之后daemon之后,主线程就直接退出了,当我们调用pthread_create函数之后,会发唤醒消息给pthread_initialize记录的pid线程时,让他继续执行。(其实在这里就已经错误了,因为pthread_initialize记录的是父进程的pid.而调用pthread_create的进程是子进程)。这样就导致子进程没有收到唤醒。一直处于阻塞。

一般如果没有在main函数之前进行pthread_initialize。在pthread_create调用时,也会再次调用。所以342之前一直没有问题。

因此,处理该问题的方式有两种:

1:修改daemon函数。不调用fork函数.(其实我很奇怪这个fork的目的何在)

2: 将pthread_initialize函数attribute((constructor))属性去除。

你可能感兴趣的:(linux,上班日志,C语言)