概述
- 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)
{
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;
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;