PTHREAD_STACK_MIN宏指示一个线程堆栈的最小字节。当调用pthread_attr_setstacksize()函数设定线程堆栈时,设定的线程堆栈大小值必须大于等于PTHREAD_STACK_MIN宏定义的值,否则返回错误。
不同的处理器和操作系统,PTHREAD_STACK_MIN宏具有不同值,这也是该宏存在的意义。它一般定义在limits.h头文件中。不过,也有例外。
问题现象
在博通(broadcom)MIPS平台上开发多线程程序时就发现了这个例外。只要线程一旦开始运行就会出现段错误(segment fault)。经过分析,估计是设置的线程堆栈太小导致的。问题是,程序里设置线程堆栈大小值为PTHREAD_STACK_MIN,这怎么可能出错呢?
再仔细分析发现,原来程序里对这个宏有个额外的定义,如下代码所示:
#include <limits.h> #ifdef _CYGWIN__ #ifndef PTHREAD_STACK_MIN #warning "!Undefine PTHREAD_STACK_MIN, use the custom value 8192!" #define PTHREAD_STACK_MIN 8192 #endif #else #ifndef PTHREAD_STACK_MIN #warning "!Undefine PTHREAD_STACK_MIN, use the custom value 16384!" #define PTHREAD_STACK_MIN 16384 #endif #endif
然后,在程序里,把PTHREAD_STACK_MIN宏值打印出来,结果为16384。也就是说,在limits.h头文件中没有该宏的定义。
这是怎么回事?
问题分析
首先,我检索下交叉编译器里倒底有木有宏PTHREAD_STACK_MIN的定义,如下所示:
$ grep -r “PTHREAD_STACK_MIN” ./
匹配到二进制文件 ./mipsel-unknown-linux-gnu/bin/getconf
./mipsel-unknown-linux-gnu/include/bits/local_lim.h:#define PTHREAD_STACK_MIN 131072
grep: 警告: ./mipsel-unknown-linux-gnu/include/include: 嵌套目录循环
./mipsel-unknown-linux-gnu/include/pthread.h: minimal size of the block must be PTHREAD_STACK_MIN. */
./mipsel-unknown-linux-gnu/include/pthread.h: to be started. This size must never be less than PTHREAD_STACK_MIN
./mipsel-unknown-linux-gnu/usr/include/bits/local_lim.h:#define PTHREAD_STACK_MIN 131072
grep: 警告: ./mipsel-unknown-linux-gnu/usr/include/include: 嵌套目录循环
./mipsel-unknown-linux-gnu/usr/include/pthread.h: minimal size of the block must be PTHREAD_STACK_MIN. */
./mipsel-unknown-linux-gnu/usr/include/pthread.h: to be started. This size must never be less than PTHREAD_STACK_MIN
./usr/include/bits/local_lim.h:#define PTHREAD_STACK_MIN 131072
grep: 警告: ./usr/include/include: 嵌套目录循环
./usr/include/pthread.h: minimal size of the block must be PTHREAD_STACK_MIN. */
./usr/include/pthread.h: to be started. This size must never be less than PTHREAD_STACK_MIN
经过检索发现,mips交叉编译器里有宏PTHREAD_STACK_MIN的定义(打红色的地方),不过,它不是位于limits.h头文件中,而是位于bits/local_lim.h头文件下。
现在的问题是:limits.h头文件中有没有直接或间接包含bits/local_lim.h头文件?
再次检索发现,mips交叉编译器的多个路径下都有limits.h头文件,如下所示:
$ find -name limits.h
./lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include-fixed/limits.h
./lib/gcc/mipsel-unknown-linux-gnu/4.4.6/install-tools/include/limits.h
./lib/gcc/mipsel-unknown-linux-gnu/4.5.3/include-fixed/limits.h
./lib/gcc/mipsel-unknown-linux-gnu/4.5.3/install-tools/include/limits.h
./mipsel-unknown-linux-gnu/include/c++/4.4.6/tr1/limits.h
./mipsel-unknown-linux-gnu/include/c++/4.5.3/tr1/limits.h
./mipsel-unknown-linux-gnu/include/limits.h
./mipsel-unknown-linux-gnu/include/linux/limits.h
使用命令:mipsel-unknown-linux-gnu-gcc –v,可查看到交叉编译器使用的版本为4.4.6,所以可以排除4.5.3路径下的limits.h头文件。
深入分析后,发现只有./lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include-fixed/limits.h头文件间接包含了bits/local_lim.h头文件。
分析到这里,问题的根本应该是:交叉编译应用程序时,使用的是哪个limits.h头文件了。这也是本文要重点说明的内容。
头文件路径
我们知道,在应用程序里,可以使用-I选项来指定包含目录的路径。实际上,编译程序时,编译器会指定默认的头文件搜索路径的。可使用命令:“arm-linux-gcc -E -v -”来查看。如下所示。
$ mipsel-unknown-linux-gnu-gcc -E -v –
Using built-in specs.
Target: mipsel-unknown-linux-gnu
Configured with: ../configure –target=mipsel-unknown-linux-gnu –prefix=/cross-root –enable-clocale=gnu –enable-threads=posix –enable-__cxa_atexit –enable-c99 –enable-long-long –disable-libstdcxx-pch –disable-libunwind-excaptions –disable-nls –enable-shared –disable-multilib –enable-languages=c,c++ –with-gmp=/build-temp –with-mpfr=/build-temp –with-mpc=/build-temp
Thread model: posix
gcc version 4.4.6 (GCC)
COLLECT_GCC_OPTIONS=’-E’ ‘-v’ ‘-mllsc’ ‘-mno-shared’
/cross-root/libexec/gcc/mipsel-unknown-linux-gnu/4.4.6/cc1.exe -E -quiet -v – -mllsc -mno-shared
ignoring nonexistent directory “/cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/../../../../mipsel-unknown-linux-gnu/sys-include”
#include “…” search starts here:
#include <…> search starts here:
/cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include
/cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include-fixed /cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/../../../../mipsel-unknown-linux-gnu/include
End of search list.
其中,
#include “…” search starts here:
#include <…> search starts here:
/cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include
/cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/include-fixed /cross-root/lib/gcc/mipsel-unknown-linux-gnu/4.4.6/../../../../mipsel-unknown-linux-gnu/include
End of search list.
这一段指定了编译器默认的头文件搜索路径,共有3个搜索路径,具有不同的搜索顺序。具体来说是:当使用#include <filename.h>包含头文件时,首先会去上面的第一个路径搜索,如果找到则停止搜索,如果没找到,则去第二路径搜索。如果第三个搜索路径下依旧没有找到头文件,编译器则会报错。
使用类似方法,可以查看其它(交叉)编译器默认的头文件搜索路径。
#include_next
在分析PTHREAD_STACK_MIN宏定义所在位置时,在syslimits.h文件中发现了如下的头文件包含:
#define _GCC_NEXT_LIMITS_H /* tell gcc's limits.h to recurse */ #include_next <limits.h> #undef _GCC_NEXT_LIMITS_H
#include_next,这是什么?
#include_next是GNU的一个扩展,不是标准C中的指令。个人对它的理解是:去下一个头文件搜索路径下搜索头文件。
参考资料
- gcc:预处理语句--#include和#include_next:http://blog.csdn.net/fjb2080/article/details/5247494
- 关于头文件的问题:http://blog.chinaunix.net/uid-29145190-id-3867605.html