大家好,本人被下面这个问题困扰了一段时间,最近似乎找到了答案。
这里和大家分享一下,可能对有相同困惑的同学有点帮助,同时也请各位帮忙看看错漏的地方。

1================问题:
在使用pthread库创建两个线程时clone()被调用了两次,可以用strace 看到:

int
main()
{
...
        err=pthread_create(&tid, NULL, job, NULL);
        err=pthread_create(&tid1, NULL, job, NULL);
...
}

strace:

clone(Thread is running.
child_stack=0xb7efb4b4,
flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
parent_tidptr=0xb7efbbd8, {entry_number:6, base_addr:0xb7efbb90,
limit:1048575, seg_32bit:1, contents:0, read_exec_only:0,
limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb7efbbd = 5104

clone(child_stack=0xb76fa4b4,
flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
parent_tidptr=0xb76fabd8, {entry_number:6, base_addr:0xb76fab90,
limit:1048575, seg_32bit:1, contents:0, read_exec_only:0,
limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb76fabd = 5105

大家都知道clone()可以产生一个所谓的“轻量级进程”,也就是有独立的task_struct的,独立调度的东西。
再看看
flag:CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
这里对这些flag就不多说了对于我这个问题,最为重要的是CLONE_VM(共享内存描述符mm_struct和所有的页表)。
这点可以在copy_mm()里面看得到(在clone()中被调用):

static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
...
        if (clone_flags & CLONE_VM) {
                atomic_inc(&oldmm->mm_users);
                mm = oldmm;
                goto good_mm;
        }
...
}

这样我这里两个pthread和原来的main进程就共享同一个mm_struct了,如图1:

pthread线程之间的栈必定是独立的,不然不可能被独立调度,我的问题就在这里,3个task_struct 共用一个mm_struct,那么他们的栈应该怎么办?难道在一个栈里面?

2======================解答:
问题主要在两个函数上面,一个是clone()系统调用,一个是pthread_create()
man clone,看他的参数。参数列表中最为显眼的就是第二个参数void *child_stack,man里面是这样形容它的:
The  child_stack argument specifies the location of the stack used by the child process.  Since the child
and calling process may share memory, it is not possible for the child process to  execute  in  the  same
stack as the calling process.
调用clone()的时候是要自己提供子task的栈空间的,这个系统调用实在是太特别了。
在看看pthread的库是怎样给这个参数赋值的:
函数pthread_handle_create() (manager.c里面) 被调用来创建新线程,在这个函数里面调用了pthread_allocate_stack()
来分配栈空间:

static int pthread_allocate_stack(const pthread_attr_t *attr,
                                  pthread_descr default_new_thread,
                                  int pagesize,
                                  char ** out_new_thread,
                                  char ** out_new_thread_bottom,
                                  char ** out_guardaddr,
                                  size_t * out_guardsize,
                                  size_t * out_stacksize)
{
...
      map_addr = mmap(NULL, stacksize + guardsize,
                      PROT_READ | PROT_WRITE | PROT_EXEC,
                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
      if (map_addr == MAP_FAILED)
        /* No more memory available.  */
        return -1;
...
}

可以看到pthread库是通过调用mmap()来为新的线程创建栈空间的,可以在细看一下它的参数,它使用了flag
MAP_ANONYMOUS和MAP_PRIVATE,而且fd参数中用了-1,这样调用的结果是在进程空间中创建一个匿名的
线性区,这样就有了栈空间,而且这个空间也在原来的mm_struct里面,如图2:

光有空间不行,还要看看内核是怎样使用,我们来到copy_thread(),它在copy_process()中被调用(它们都在clone()里面被调用的):

static struct task_struct *copy_process(unsigned long clone_flags,
                                        unsigned long stack_start,
                                        struct pt_regs *regs,
                                        unsigned long stack_size,
                                        int __user *child_tidptr,
                                        struct pid *pid)
{
...
        retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
...
}
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
        unsigned long unused,
        struct task_struct * p, struct pt_regs * regs)
{
        struct pt_regs * childregs;
        struct task_struct *tsk;
        int err;

childregs = task_pt_regs(p);
        *childregs = *regs;
        childregs->ax = 0;
        childregs->sp = sp;
...
}

这里可以清楚的看到,内核为这个用户线程初始化他未来的sp寄存器值,就是刚才mmap()返回的那个地址。所以结果
如图3:

附上pthread库,有兴趣的同学可以研究一下,好像还比较复杂。

分享地址:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2018590

分享一个关于pthread线程栈在mm_struct里面的分布问题的更多相关文章

  1. 分享一个Python脚本--统计redis key类型数据大小分布

    概述 今天主要介绍怎么统计redis key类型数据大小分布. 原理:使用redis命令: scan.pipline.type 和 debug object 来得到 redis key 信息. 脚本 ...

  2. Java性能分析之线程栈详解与性能分析

    Java性能分析之线程栈详解 Java性能分析迈不过去的一个关键点是线程栈,新的性能班级也讲到了JVM这一块,所以本篇文章对线程栈进行基础知识普及以及如何对线程栈进行性能分析. 基本概念 线程堆栈也称 ...

  3. 分享一个自制的 .net线程池

    扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...

  4. thread线程栈size及局部变量最大可分配size【转】

    转自:http://blog.csdn.net/sunny04/article/details/46805261 版权声明:本文为博主原创文章,未经博主允许不得转载. 进程是操作系统的最小资源管理单元 ...

  5. [并发并行]_[线程模型]_[Pthread线程使用模型之一管道Pipeline]

    场景 1.经常在Windows, MacOSX 开发C多线程程序的时候, 经常需要和线程打交道, 如果开发人员的数量不多时, 同时掌握Win32和pthread线程 并不是容易的事情, 而且使用Win ...

  6. Linux虚拟地址空间布局以及进程栈和线程栈总结【转】

    转自:http://www.cnblogs.com/xzzzh/p/6596982.html 原文链接:http://blog.csdn.net/freeelinux/article/details/ ...

  7. Linux虚拟地址空间布局以及进程栈和线程栈总结

    原文链接:http://blog.csdn.net/freeelinux/article/details/53782986[侵删] 本文转自多个博客,以及最后有我的总结.我没有单独从头到尾写一个总结的 ...

  8. 多线程编程之pthread线程深入理解

    不同的平台和操作系统上 进程和线程的实现机制不完全一致  但是一般来说线程栈都是独立的 只要得到地址就可以相互访问       Pthread是 POSIX threads 的简称,是POSIX的线程 ...

  9. 【C/C++多线程编程之五】pthread线程深入理解

    多线程编程之pthread线程深入理解       Pthread是 POSIX threads 的简称,是POSIX的线程标准.           前几篇博客已经能给你初步的多线程概念.在进一步学 ...

随机推荐

  1. ajax实现的点击数目加1代码实例

    ajax实现的点击数目加1代码实例:在点击按钮实现数字增加效果代码实例一章节中,介绍如何点击按钮实现数字加1的效果,但是好像并没有什么实际用处,下面就分享一段相对完整的能够在实际应用中派上用场的代码, ...

  2. java中json解析,xml解析

    抓取网页内容,会返回json或者xml(html)格式的数据. 为了方便的对上述两种格式的数据进行解析,可采用解析工具. JsonPath https://github.com/jayway/Json ...

  3. js获取文件输入框的真实目录

    1.问题 页面有一个input file服务器控件,一个div,div是image标签的容器,当点击input file的值改变,我们往div里追加image标签: 但当通过js的onchange事件 ...

  4. js【jquery】 - DOM操作

    1.修改元素样式 js方式: var div2 = document.getElementById("") div2.style.width = '200px'; div2.cla ...

  5. Backbone事件机制核心源码(仅包含Events、Model模块)

    一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性.   二.遗留问题 1.b ...

  6. OLEDB事务

    学过数据的人一般都知道事务的重要性,事务是一种对数据源的一系列更新进行分组或者批处理以便当所有更新都成功时同时提交更新,或者任意一个更新失败时进行回滚将数据库中的数据回滚到执行批处理中的所有操作之前的 ...

  7. Vue表格中,对数据进行转换、处理

    众所周知,后端从Mysql取出的数据,一般是很难单独处理某一个Key的数据的(需要处理的话,可能会浪费大量的性能.而且对页面加载时间有很大的影响),所以,从数据库取出的数据.只能由前端进行处理.但是在 ...

  8. 有关ie9 以下不支持placeholder属性以及获得焦点placeholder的移除

    (一)placeholder 属性起到提示客户输入信息作用 (二)ie9以下出问题了 placeholder不支持 (三)解决办法 先贴html 加上jquery代码就可以了 关于表单获取焦点,chr ...

  9. 转:如何利用已有的切片文件生成TPK

    Tpk是ArcGIS 10.1即将推出的一种新的数据文件类型,主要是用于将切片文件打包形成离线地图包.Tpk可以在ArcGIS Runtime中作为切片底图被加载.在ArcGIS 10.1中Tpk的生 ...

  10. Arcgis flex 切片地图麻点

    在arcgis server中发布地图切片完成后,有时候在访问地图的时候会出现很多麻点, 其实是你切片的时候没有注意到一些选项.... 默认的切片是PNG8,说到这可能就明白了吧,png8的色彩范围: ...