设定线程运行栈:pthread_attr_setstack()

2019-07-13 09:27发布

概述

  • linux在创建线程时,如果使用默认的栈,默认栈的大小通常为8MB,这对内存比较紧张的嵌入式平台来说,是无法接受的巨量内存浪费;
  • pthread_attr_setstack()可以设定线程栈的地址和大小,设定的栈地址必须以linux页面大小对齐,所以这里使用posix_memalign()分配页面对齐的内存;该内存中不使用时也是使用free()释放;
  • 线程的最小栈大小为16KB,小于这个数值pthread_attr_setstack会设定失败;
  • 在线程中的局部变量是直接从线程栈中分配的,静态局部变量不在线程栈中;所以如果函数调用过多,导致局部变量占用空间超过栈空间,就会引起coredump;
  • 如果使用默认栈大小,还可以调用pthread_attr_setguardsize()设定线程栈的溢出保护大小,当栈溢出时,会发SIGSEGV 信号到该线程;
  • 如果不想设定线程栈的地址,可以使用pthread_attr_setstacksize来只设定栈大小,让kernel自动分配对应大小的栈,栈最小为16KB,并且是页面(通常为4KB)对齐;这时也可以调用pthread_attr_setguardsize设定栈边界保护区大小(至少为一页),防止爆栈后直接coredump;

pthread_attr_setstack示例

#include #include #include #include #include #include #include #include #include #define DBG_PRINT(fmt, args...) {printf("%s %d ", __FUNCTION__, __LINE__);printf(fmt,##args);} /** * [msSleep 用nanosleep实现的毫秒级线程休眠] * @param msTime [description] */ void msSleep(unsigned msTime) { struct timespec reqTime; struct timespec remTime; int ret = 0; if(msTime == 0) { DBG_PRINT("invalid argument! "); return; } else if(msTime>=1000) { reqTime.tv_sec = msTime/1000; reqTime.tv_nsec = (unsigned long)((msTime%1000)*1000000UL); } else { reqTime.tv_sec = 0; reqTime.tv_nsec = (unsigned long)(msTime*1000000UL); } do { /** * 由于nanosleep在休眠线程过程中,线程可能会被中断唤醒,并且唤醒后,剩余的休眠时间会保存在 * remTime中,所以使用remTime中的时间继续休眠线程; */ ret = nanosleep(&reqTime, &remTime); if(-1 == ret) { switch(errno) { case EINTR: reqTime.tv_sec = remTime.tv_sec; reqTime.tv_nsec = remTime.tv_nsec; continue; default: break; } } break; }while(1); } #define BUFFER_LEN 0x3000 void *testThead1(void* arg) { //设定线程名为zoobiTask1 prctl(PR_SET_NAME, "zoobiTask1"); char buffer[BUFFER_LEN]; while(1) { DBG_PRINT("Start "); msSleep(300); DBG_PRINT("End "); } } #define THREAD_STACK_LEN 0x4001 int main(int argc, const char* argv[]) { pthread_t thread1ID; pthread_attr_t attr; int ret = 0; void *stackAddr = NULL; //获取linux的页大小 int paseSize = getpagesize(); DBG_PRINT("The linux page size:0x%x ", paseSize); pthread_attr_init(&attr); /** * 申请内存,并且内存以页大小对齐,需要申请的内存大小必须是2的整数次幂; * 经常用的malloc申请的内存只会默认使用8bytes或者16bytes对齐(依赖平台是32位还是64位); */ ret = posix_memalign(&stackAddr, paseSize, THREAD_STACK_LEN); if(0 != ret) { DBG_PRINT("posix_memalign failed, errno:%s ", strerror(ret)); return -1; } #if 1 /** * 设定线程运行栈地址和大小,栈大小最小为16KB,并且栈地址以页面对齐; */ ret = pthread_attr_setstack(&attr, stackAddr, THREAD_STACK_LEN); if(0 != ret) { DBG_PRINT("pthread_attr_setstack failed, errno:%s ", strerror(ret)); return -1; } #endif void *getstackaddr = NULL; size_t getstackSize = 0; pthread_attr_getstack(&attr, &getstackaddr, &getstackSize); DBG_PRINT("getstackaddr:%p, getstackSize:0x%x ", getstackaddr, getstackSize); ret = pthread_create(&thread1ID, &attr, testThead1, NULL); if(ret != 0) { DBG_PRINT("pthread_create failed! errno:%s ", strerror(ret)); return -1; } pthread_detach(thread1ID); while(1) { msSleep(10000); } return 0; } 上面例程编译运行后打印如下: [zoobi@localhost linux_env]$ g++ thread_stack.cpp -o thread_stack -lpthread [zoobi@localhost linux_env]$ ./thread_stack main 90 The linux page size:0x1000 main 117 getstackaddr:0x85f000, getstackSize:0x4001 testThead1 73 Start testThead1 75 End testThead1 73 Start 如果将BUFFER_LEN更改为0x4000,如下打印: [zoobi@localhost linux_env]$ ./thread_stack main 90 The linux page size:0x1000 main 117 getstackaddr:0x24d1000, getstackSize:0x4001 @ @ @ @C 打印都是乱码,说明局部变量过大导致栈已经溢出,线程运行情况是不确定的;
将pthread_attr_setstack()注释调,然后更改BUFFER_LEN,最大可以更改到0x7fe000;在我的平台测试,改到0x7ff000线程就会coredump,说明在测试平台上默认栈大小为0x800000;