daemon和pthread

xiaoxiao2021-02-28  37

背景

最近完成工具链和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 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))属性去除。

转载请注明原文地址: https://www.6miu.com/read-2626830.html

最新回复(0)