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

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. 使用Git(msysgit)和TortoiseGit上传代码到GitHub

    1.准备 下载Git for Windows (msysgit) 下载TortoiseGit 安装过程很简单,一直点击下一步到完成即可. 2.配置TortoiseGit 1.双击TortoiseGit ...

  2. WebGrease—异常来自 HRESULT:0x80131040

    一.错误源: 未能加载文件或程序集“WebGrease, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一 ...

  3. Oracle SQL语句优化34条

    非常好用的SQL语句优化34条 1)选择最有效率的表名顺序(只在基于规则的优化器中有效): ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表(基础表 dri ...

  4. owin解决跨域js请求

    最近在用owin打了一个建议的http的api服务,但遇到了js跨域访问的问题,后来在网上找到了答案,已帮助遇到此问题的人 1.首先nuget先按着owin依赖的包,然后至关重要的一步是引用Micro ...

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

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

  6. C/C++中的auto关键词

    C语言 auto被解释为一个自动存储变量的关键字,也就是申明一块临时的变量内存. 例如: auto double a=3.7; 表示a为一个自动存储的临时变量. C++语言 C++ 98标准/C++0 ...

  7. 洛谷P1351 联合权值(树形dp)

    题意 题目链接 Sol 一道很简单的树形dp,然而被我写的这么长 分别记录下距离为\(1/2\)的点数,权值和,最大值.以及相邻儿子之间的贡献. 树形dp一波.. #include<bits/s ...

  8. 软件项目技术点(8)—— canvas调用drawImage绘制图片

    AxeSlide软件项目梳理   canvas绘图系列知识点整理 html5中标签canvas,函数drawImage(): 使用drawImage()方法绘制图像.绘图环境提供了该方法的三个不同版本 ...

  9. javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作

    树型结构是一类非常重要的非线性结构.直观地,树型结构是以分支关系定义的层次结构. 树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构:在数据库系统中,可用树来组织信息:在分 ...

  10. 正则表达式备忘录-Regular Expressions Cheatsheet中文版

    正则表达式备忘录Regular Expressions Cheatsheet中文版原文:https://www.maketecheasier.com/cheatsheet/regex/ 测试文件a.t ...