最近完成工具链和uClibc库升级的任务,升级之后出现了一个bug,就是大软件起来之后,原先ps能看到6个线程的进程,现在只能看到三个,导致出现了一些功能上的问题。
有了问题,当然要处理啦。毕竟是自己埋下的坑,含着泪也要把它填起来。刚开始在代码里面加上打印,发现都没有显示,觉得特别奇怪。心想是不是阻塞在哪个函数了。经过从main函数开始打印,慢慢的定位到daemon这个函数。心想这个函数时做什么的?为什么会阻塞呢?
上网百度之后,发现daemon函数的作用就是将进程放到后台执行。所以我的打印就看不到了,并不是阻塞在这里。
但是奇怪的现象是当我把这个函数注释掉之后,编译出来的程序又是完好如初了。(ps:可以看到6个线程)
这就让我把问题定位在了daemon函数上。后续再进行排查。发现程序在进行第一个pthread_create函数之后,就不再继续放下执行了。(打开daemon函数)
最终我将问题定位在daemon函数和pthread_create两个之间
问题解决的过程我就不再详细说明了。因为过程也花了好几天,和老大慢慢确认问题,一起总结。也了解到了很多新的知识点。
首先你要了解以下几点内容:
main函数不是程序最初的入口 首先第一点,我们之前一直听过一句话,main函数是程序的入口,其实并不是的。你仔细想想就知道肯定不是这样的。因为你并没有初始化堆栈,stdin,stdout,stderr等参数,你是怎么使用的呢?这肯定是在main函数之前就已经设置好的了。
GNU C attribute 设置属性 GNU C attribute 是GCC的一大特色,他可以为函数,变量,类型赋予属性。
其中 attribute((constructor)) // 构造函数在main函数被调用之前调用 attribute((destructor)) // 析构函数在main函数被调用之后调
不同的工具链,编译的效果是不同的 根据背景可知,我升级了工具链。现在用的是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 2463编译器:
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))属性去除。
