Linux多线程详解(2)
5.线程的更多细节
5.1线程栈
线程栈大小是固定的,也就是ulimit -a 显示的值
在创建线程的时候,可以通过pthread_attr_t来初始化线程的属性,包括线程的栈布局信息,如栈起始地址stackaddr,栈大小stacksize。 具体需要通过方法
函数原型:
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
参数说明:
attr:线程属性指针
stackaddr:设置栈的起始地址
返回值:0代表成功,或返回大于0的错误码 // 注:stackaddr 指向为该线程开辟的空间,该空间可以使用malloc或者mmap来开辟,而不能来自进程的栈区。开辟的stackaddr所指向的动态空间需要自己负责释放。
当然也可将线程栈的空间管理交给系统,如果想只改变系统默认的栈大小8MB,可以通过
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); // 注:stacksize最小值为16384,单位为字节 |
由上面的API接口,可以得到,线程栈的stacksize是保存在pthread_attr_t中的,可以通过人为的指定,也可以通过在创建线程的时候读取系统的配置文件来初始化stacksize,当初始化完栈的起始地址,和大小后,便可以通过
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); |
来初始化线程栈末尾之后用以避免栈溢出的缓冲区的大小,如果应用程序溢出到此缓冲区中,这个错误可能会导致 SIGSEGV 信号被发送给该线程, 从而造成段错误,缓冲区默认设置为PAGESIZE个字节。因为线程的mm->start_stack和所属进程相同,所以线程栈的起始地址并没有存放在task_struct中,应该只是使用attr中的stackaddr,来初始化task_struct->thread-> sp(sp指向struct pt_regs对象,该结构体用于保存用户进程或者线程的寄存器现场)。
总结:线程栈的空间开辟在所属进程的堆区,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。线程栈的起始地址和大小存放在pthread_attr_t 中,栈的大小并不是用来判断栈是否越界,而是用来初始化避免栈溢出的缓冲区的大小(或者说安全间隙的大小)