深入理解linux下进程和线程的空间分配,进程栈和线程栈的空间分配
来源:互联网 发布:直播录播软件 编辑:程序博客网 时间:2024/05/29 13:08
最近学习了下linux下进程和线程空间的分配原理,觉得有必要坐下总结,
关于进程栈和线程栈总结:
(1)进程栈大小时执行时确定的,与编译链接无关
(2)进程栈大小是随机确认的,至少比线程栈要大,但不会超过2倍
(3)线程栈是固定大小的,可以使用ulimit -a 查看,使用ulimit -s 修改
(4)一般默认情况下,线程栈是在进程的堆中分配栈空间,每个线程拥有独立的栈空间,为了避免线程之间的栈空间踩踏,线程栈之间还会有以小块guardsize用来隔离保护各自的栈空间,一旦另一个线程踏入到这个隔离区,就会引发段错误。
下面是一个比较简单的多线程程序。程序如下,
1234567891011121314151617181920212223
int main(){ int ret = 0; int i = 0; /* int stack_size = 128 * 1024; */ pthread_attr_t attr; /* pthread_attr_setstacksize(&attr, stack_size); */ for (i = 0; i < 3; i++) { ret = pthread_create(&thread[i], NULL, thread_func, NULL); if (ret) { perror("create"); return -1; } } while (1) { ; } return 0;}
上图是我的测试程序,我创建了3个线程。程序运行以后,我们可以通过/ proc/PID/task来看该程序有多少线程在运行:
然后我们来看一下进程的地址空间。 /proc/PID/maps就是进程的地址空间。如下所示:
可以看出,进程的地址空间从低到高依次是:进程代码段(标志含有x)、只读数据段、可读写数据段、堆、mmap区(文件映射和匿名映射,其中有文件名的行是文件映射),栈。
线程18438的栈:(0xb7570000-0xb6d70000)的值恰好是8M,线程栈默认大小是8M。(0xb6d70000-0xb6d6f000)的值是4K,这4K是保护页。
为什么这三个线程的栈都是8M?可以从ulimit命令来得出,这是进程的资源限制:
使用ulimit -a命令可以看出,进程资源限制中栈大小的限制是8194K,即8M.
那么,这个8M大小是不是可以更改的?以及后面会什么会有一个4K大小的保护页?这可以从glibc代码里面来获取答案:
12345678910111213141516171819
1.__pthread_create_2_1 /* 这里分配线程栈 */ ALLOCATE_STACK2./* allocate_stack就是具体的分配线程栈的函数: */allocate_stack /*如果没有设置线程栈大小,就使用默认值*/ size = attr > stacksize ?: __default_stacksize; /* ... */ /* Try to get a stack from the cache. */ pd = get_cached_stack (&size, &mem); /* 如果没有从cache申请到,就要mmap申请一块内存 */ if (pd == NULL){ /* MAP_PRIVATE | MAP_ANONYMOUS: 私有匿名映射 */ mmap (NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); /* 接着设置一个保护区,该区域的页表属性是PROT_NONE(Page can not be accessed) */ mprotect(guard, guardsize, PROT_NONE);
对于设置为PROT_NONE的页,是不能访问的,那么访问到这个保护区时就出现错误,linux是靠这种机制来实现栈溢出保护的。
下面我们来调整线程栈:
- 设置pthread_attr属性
可以看到此时的线程栈大小是: (0xb758f000-0xb756f000) = 128K.
测试进程栈和线程栈大小:
查看线程栈大小:
可以看到默认情况下线程栈大小为8192(8MB),可以使用ulimit -s xxx修改线程默认栈大小
(1)检查线程栈默认大小(8KB)
线程执行2030次之后,出现段错误(2030*4K=8120K)
(2)修改栈大小,使用pthread_attr_setstack()
如上修改栈大小为16MB,其中线程栈的空间从堆中进行分配
程序执行4063次后出现段错误(4063*4KB)
(3)创建两个线程,使用默认栈大小执行
创建两个线程,默认单个线程栈大小为8M
执行结果1:程序执行4009次之后段错误(4009*4KB)
执行结果2:程序执行3380次之后段错误(3380*4KB)
总结:
两个线程时,两个线程栈的总和不是固定值,也不是线程栈的2倍
(3)不使用任何线程
执行结果1:程序执行2538次后段错误(2538*4KB)
执行结果2:程序执行2537次后段错误(2537*4KB)
总结:
进程的栈大小不是固定的,而是比线程栈大一些
(4)线程栈从进程栈中分配
执行结果1: 程序执行2536次后段错误(2536*4KB>8M)
执行结果2:程序执行2537次后段错误(2537*4KB>8M)
总结:
线程从进程栈分配空间,大小并不是固定的,如果分配空间大于进程栈空间,那么直接运行时出现段错误。
- 深入理解linux下进程和线程的空间分配,进程栈和线程栈的空间分配
- Linux进程空间分配
- Linux进程空间分配
- Linux中进程空间的分配
- 堆和栈的空间分配
- 进程的虚拟地址空间分配概述
- 深入理解进程和线程
- 进程和线程的理解
- 进程和线程的理解
- 进程的内存分配(堆和栈)
- 《深入理解Linux内核》-3.1. 进程,轻量级进程,和线程
- linux下进程和线程的区别
- linux 下 进程和线程的区别
- linux 下 进程和线程的区别
- linux 下 进程和线程的区别
- linux下进程和线程的区别
- linux 下 进程和线程的区别
- linux 下 进程和线程的区别
- String Date Calendar之间的转换
- Java8-Stream-No.06
- springMVC项目中JSP页面不显示图片
- ubuntu14.04安装Pytorch 和 torchvision
- Ubuntu学习整理
- 深入理解linux下进程和线程的空间分配,进程栈和线程栈的空间分配
- Linux CentOS 7 安装PostgreSQL 9.5(源码编译)
- 【JS】用js动态设置元素标签之间的换行
- MySQL Installer is running in Community mode 的解决办法
- 调用webService的几种方式
- Java8-Stream-No.07
- java的long类型转为json格式后,js中精度丢失问题
- sharding-jdbc结合mybatis实现分库分表功能
- 手动输入数据完成链表的 头插、尾插