微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

堆栈分配给一个线程

我一直在试图拼凑栈内存如何分配给线程。 我还没有把整件事整理在一起。 我试着去看代码,但我更困惑,所以我要求你的帮助。

刚才我问了这个问题 。 所以假设特定的程序(因此,所有的线程都在同一个进程内)。 如果我为每个堆栈指针的起始处写入printf ,然后为它们分配了多less,那么在这个消息的结尾处我得到类似于表的东西,其中第一列是time_t usec ,第二列没有关系,第三个是线程的tid,第四个是防护大小,然后是堆栈的开始,堆栈的结束(按堆栈的开始sorting),最后一个是分配的堆栈(认为8Mggs),最后一列是第一个分配的堆栈的结束和下一个堆栈的开始之间的区别。

这意味着(我认为),如果为0,那么堆栈是连续的,如果是正的,因为堆栈在内存中增长下来,那么意味着在一个tid和下一个之间存在许多Mbs的“自由空间”记忆)。 如果是否定的,这意味着内存正在被重用。 所以这可能意味着在创build这个线程之前已经释放了堆栈空间。

我的问题是:给线程分配堆栈空间的algorithm究竟是什么(在比代码更高的层次上),为什么我有时会得到连续的堆栈,有时甚至没有,有时在最后一列得到像7.94140625和0.0625这样的值?

为什么在同一个程序的不同运行中堆栈大小不同?

打电话给_freea真的有必要?

为什么堆栈低于内存中的文本?

linux内核如何强制实现堆栈大小限制?

gcc cc1:内存不足分配

这是所有的Linux 2.6,C和pthreads。

这可能是一个问题,我们必须重复才能做到这一点,对此我表示歉意,但我现在正在告诉你我所知道的。 随意要求澄清。

谢谢你。 表格如下。

52815 14 14786 4096 92549120 100941824 8392704 0 52481 14 14784 4096 100941824 109334528 8392704 0 51700 14 14777 4096 109334528 117727232 8392704 0 70747 14 14806 4096 117727232 126119936 8392704 8.00390625 75813 14 14824 4096 117727232 126119936 8392704 0 51464 14 14776 4096 126119936 134512640 8392704 8.00390625 76679 14 14833 4096 126119936 134512640 8392704 -4.51953125 53799 14 14791 4096 139251712 147644416 8392704 -4.90234375 52708 14 14785 4096 152784896 161177600 8392704 0 50912 14 14773 4096 161177600 169570304 8392704 0 51617 14 14775 4096 169570304 177963008 8392704 0 70028 14 14793 4096 177963008 186355712 8392704 0 51048 14 14774 4096 186355712 194748416 8392704 0 50596 14 14771 4096 194748416 203141120 8392704 8.00390625

使堆栈指针指向一个mmap返回的指针。 (Linux,32位虚拟机)

什么是“双栈错误”?

什么是存储在线程的堆栈?

我如何find最大堆栈大小?

在堆栈上分配一个页面大小的缓冲区会损坏内存?

首先,通过启动一个简单的启动单个线程的测试程序,我们可以看到它用于创建一个新线程的系统调用。 这是一个简单的测试程序:

#include <pthread.h> #include <stdio.h> void *test(void *x) { } int main() { pthread_t thr; printf("startn"); pthread_create(&thr,NULL,test,NULL); pthread_join(thr,NULL); printf("endn"); return 0; }

其strace输出的相关部分:

write(1,"startn",6start ) = 6 mmap2(NULL,8392704,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK,-1,0) = 0xf6e32000 brk(0) = 0x8915000 brk(0x8936000) = 0x8936000 mprotect(0xf6e32000,4096,PROT_NONE) = 0 clone(child_stack=0xf7632494,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,parent_tidptr=0xf7632bd8,{entry_number:12,base_addr:0xf7632b70,limit:1048575,seg_32bit:1,contents:0,read_exec_only:0,limit_in_pages:1,seg_not_present:0,useable:1},child_tidptr=0xf7632bd8) = 9181 futex(0xf7632bd8,FUTEX_WAIT,9181,NULL) = -1 EAGAIN (Resource temporarily unavailable) write(1,"endn",4end ) = 4 exit_group(0) = ?

我们可以看到它使用PROT_READ|PROT_WRITE保护和MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK标志从mmap获得堆栈。 然后,它保护堆栈的第一个(即最低)页面,以检测堆栈溢出。 其余的电话与手头的讨论无关。

那么, mmap如何分配栈呢? 那么,让我们从Linux内核的mmap_pgoff开始; 现代mmap2系统调用的入口点。 它在锁定之后委托给do_mmap_pgoff 。 然后调用get_unmapped_area来查找适当范围的未映射页面

不幸的是,这会调用在vma中定义的函数指针 – 这可能使得32位和64位进程可以有不同的想法来映射哪些地址。 在x86的情况下,这是在arch_pick_mmap_layout定义的,它根据是否使用32位或64位体系结构进行切换。

那么我们来看一下arch_get_unmapped_area的实现。 它首先从find_start_end获得一些合理的认值,然后测试是否传递的地址提示是有效的(对于线程堆栈,没有提示通过)。 然后它开始扫描虚拟内存映射,从一个缓存地址开始,直到找到一个洞。 它保存了下一个搜索所用的孔的末端,然后返回这个孔的位置。 如果到达地址空间的末端,它将从头再次开始,给它一次机会找到空地。

正如你所看到的,通常情况下,它将以增加的方式分配堆栈(对于x86; x86-64使用arch_get_unmapped_area_topdown并且可能将其分配递减)。 但是,它也保留了从何处开始搜索的缓存,因此可能会根据何时释放区域而留下间隙。 特别是当一个mmaped区域被释放时,它可能会更新free-address-search-cache,所以你也可能会看到乱序分配。

这就是说,这是一个实现细节。 不要依赖你的程序中的任何一个 。 只要拿什么地址mmap出来,开心:)

glibc在nptl / allocatestack.c中处理这个问题。

重点是:

mem = mmap (NULL,size,prot,MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,0);

所以它只是要求内核提供一些匿名内存,与malloc对于大块的做法不同。 它实际得到哪个块取决于内核…

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐