zoukankan      html  css  js  c++  java
  • Linux 进程,线程,线程池

    在linux内核,线程与进程的区别很小,或者说内核并没有真正所谓单独的线程的概念,进程的创建函数是fork,而线程的创建是通过clone实现的。

    而clone与fork都是调用do_fork(),差异如下:

     1 SYSCALL_DEFINE0(fork)
     2 {
     3     return do_fork(SIGCHLD, 0, 0, NULL, NULL);
     4 }
     5 
     6 SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
     7          int __user *, parent_tidptr,
     8          int __user *, child_tidptr,
     9          int, tls_val)
    10 {
    11     return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
    12 }

    实际上就是内核开放大部分参数和do_fork接口来创建线程,看clone的官方解释:

    The main use of clone() is to implement threads: multiple threads of control in a program that run concurrently in a shared memory space.

    所以接下来参考glibc 2.25版本的pthread_create来看看进程和线程的具体实现差异在哪里:

    1 const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM
    2                | CLONE_SIGHAND | CLONE_THREAD
    3                | CLONE_SETTLS | CLONE_PARENT_SETTID
    4                | CLONE_CHILD_CLEARTID
    5                | 0);

    显而易见clone_flags的差别非常大.

    下面再来通过这些flags的作用来区分进程和线程的特性,细数之前先看看do_fork的实现,发现有意思的是,ptrace和perf这2个调试工具也是进程创建的时候初始化的.

    1、CLONE_VM

         首先线程不能脱离父进程独立存在,所以它需要共享父进程的虚拟内存空间

    2、CLONE_FS | CLONE_FILES

        线程做为父进程一个CPU执行单元,它可以直接使用父进程的文件系统信息(包括文件系统根目录,当前工作目录,和文件访问权限)而不需要自己独立创建和持有这些资源和父进程打开的文件描述符

    3、CLONE_SIGHAND | CLONE_THREAD

        线程还接着共享父进程的异步信号处理函数,即父进程能收到的异步信号,它也能收到并处理,不过线程可以自行通过sigprocmask来屏蔽或不屏蔽某些异步信号操作,而不影响其他线程。

    4、CLONE_SYSVSEM

        线程共享父进程的System V semaphore。

    5、CLONE_SETTLS

        线程支持TLS (Thread Local Storage)。TLS使得变量每一个线程有一份独立实体,各个线程的值互不干扰

    6、CLONE_PARENT_SETTID

        父进程和线程会将线程ID保存在内核任务结构体的ptid成员。

    7、CLONE_CHILD_CLEARTID

        清除内核任务结构体的ctid成员上存储的线程ID。

    8、CLONE_THREAD

        将线程放入到父进程的线程组(thread group)里,这样线程在用户态就看不到自己进程ID了,只能看到父进程的进程ID,并且线程共享父进程的异步信号。

    子进程则会复制父进程的很多进程信息,复制与共享的区别还是很大的,复制需要重新申请内核资源,所以开销比线程大很多。

    1、创建进程的时候没有指定 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,所以这些它需要复制进程的,所以父进程这些信息也被子进程继承过去了,但是已经独立存在在子进程里了,以后就没有关系了。

    2、子进程还复制父进程的信号处理函数和信号;

    3、线程和子进程都会继承(复制)父进程的普通优先级,父进程的栈,CPU状态会被设置为RUNNING。

    总结一下:

    1、进程,线程的创建还有很多细节,我这里没有完全列举出来,只列举了我觉得比较重要的部分。

    2、线程,进程的最大区别就是,啃老还是不啃老,通过上面来看,线程就是个啃老的货,而进程是个依靠父母独立成长的好孩子,然并卵,事实证明,不管是现实世界还是计算机这个虚拟世界,越独立就越占用资源!

    4、所以,多用线程可以降低系统资源的消耗。

    上面讲到要多用线程,下面就轮到线程池要出场了,但是这个世界总是有很多熊孩子,就喜欢不停给他老子搞事情,严重到。。。咳咳,我们还是严肃的描述吧:

    严重到不停的创建线程,销毁线程,这就增加了内核的开销了,更有甚之,有的线程函数无比简短,可能线程刚创建完就要销毁了。。。

    所以避免出现这样的情况,很多场合需要用到进程池。

    关于pthread可以参考如下文章:

    http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/

    https://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/

    https://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part3/

    https://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/

    https://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part5/

  • 相关阅读:
    C++经典书籍:游戏编程
    云计算学习笔记Hadoop简介,hadoop实现原理,NoSQL介绍...与传统关系型数据库对应关系,云计算面临的挑战
    A win for the Nokia N8 is a win for Qt
    Qt 为中国移动音乐客户端提供多平台支持
    诺基亚力邀App开发员加入Ovi以对抗苹果
    MeeGo手机或将跳票至2011年
    TinyXML:一个优秀的C++ XML解析器
    企业开发中Qt和.Net小谈
    Qt 的昨天,今天,明天
    学机械的看看吧,一般看不见的机械原理——全动画图解
  • 原文地址:https://www.cnblogs.com/danxi/p/6617625.html
Copyright © 2011-2022 走看看