设定线程运行栈:pthread_attr_setstack()

概述

  • 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!\n");
        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\n");
        msSleep(300);
        DBG_PRINT("End\n");
    }
}

#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\n", 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\n", 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\n", 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\n", getstackaddr, getstackSize);

    ret = pthread_create(&thread1ID, &attr, testThead1, NULL);
    if(ret != 0)
    {
        DBG_PRINT("pthread_create failed! errno:%s\n", 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;

你可能感兴趣的:(linux环境开发)