【Linux】多线程(一万六千字)

07-07 1533阅读

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

文章目录

前言

线程的概念

线程的理解(Linux系统为例)

在Linux系统里如何保证让正文部分的代码可以并发的去跑呢?

为什么要有多进程呢?

为什么要这么设计Linux"线程"?

线程的优点

线程的缺点

线程异常

线程用途

Linux进程 VS 线程

进程和线程

进程的多个线程共享

进程和线程的关系

关于调度的问题

再次谈谈进程地址空间

多个执行流是如何进行代码划分的?如何理解?

OS如何管理内存呢?

线程的控制

POSIX线程库

创建线程

PID和LWP

Linux中有没有真线程呢?

线程ID及进程地址空间布局

线程终止

线程等待

分离线程

面试题

多线程创建

总结



前言

世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!


提示:以下是本篇文章正文内容,下面案例可供参考

线程的概念

  • 线程是进程内部的一个执行分支,线程在进程的地址空间内运行。
  • 线程是CPU调度的基本单位,CPU在调度的时候,会有很多进程和线程混在一起,但是CPU不管这些,在调度的时候,都是让task_struct进行排队的,CPU只调度task_struct,所以说线程是CPU调度的基本单位是对的。

    线程的理解(Linux系统为例)

    • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
    • 一切进程至少都有一个执行线程
    • 线程在进程内部运行,本质是在进程地址空间内运行
    • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
    • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

      【Linux】多线程(一万六千字)

      • 正文:代码段(区),我们的代码在进程中,全部都是串行调用的。
      • 就一个进程,正文部分有很多对应的函数,但我们在执行的时候,所有的函数调用都是串行调用的。
      • 比如:main()函数中有a、b、c、d四个函数,我们单进程执行main()函数时,所有的函数都是串行跑的;那么今天我们想办法将代码拆成两部分,a、b函数一部分,c、d函数一部分,让一个执行流执行a、b,让另一个执行流执行c、d函数,如果a、b和c、d函数没有明显的前后关系的话,分成两个执行流让它能跑,那么此时我们的函数调用过程就是并行跑了。

        无论是多进程,还是多线程,它的核心思想:把串行的东西变成并行的东西。

        在Linux系统里如何保证让正文部分的代码可以并发的去跑呢?

        • 以前:再创建进程PCB、进程地址空间、页表,再从磁盘中向物理内存中加载新的程序,经过新创建进程的页表与物理内存建立映射关系,此时就有了独立的代码和数据,独立的内核数据结构,所以这两个进程是独立的。但是我们发现按照之前的做法,进程创建的成本(时间和空间)是非常高的。
        • 用户想要的是多执行流,所以Linux创建了一个线程,假设正文部分有很多的代码,想办法将代码分为若干份区域,比如三份区域,进程地址空间中的其它区域可以都看到,再创建一个执行流的时候,不用创建地址空间和页表,只需要在地址空间内创建两个新的task_struct,让两个新的task_struct指向同一块进程地址空间,那么它们就能看到同一份地址空间的资源,让A进程用第一个区域,让B进程用第二个区域,让C进程用第三个区域,那么CPU调度的时候,拿着三个task_struct,把当前进程的串行执行的三份代码,变成了并发式执行这三份代码了,所以我们把这种在地址空间内创建的"进程",把它叫做线程。

          进程地址空间上布满了虚拟地址,进程地址空间以及上面的虚拟地址的本质是一种资源。

          我们之前说的,代码可以并行或并发的去跑,比如:父子进程的代码是共享的,数据写实拷贝各自一份,所以可以让父子执行不同的代码块,这样就可以将代码块进行两个各自调度运行了。

          为什么要有多进程呢?

          目标不是为了多进程,是为了多执行流并发执行,为了让多个进程之间可以并发的去跑相同或不同的代码。

          为什么要这么设计Linux"线程"?

          线程跟进程一样,也是要被调度的。

          线程在一个进程内部,就意味着一个进程内部可能会存在很多个线程。

          如果我们要设计线程,OS也要对线程进行管理!先描述,再组织。描述线程:线程控制块(struct TCB),要保证线程被OS管理,比如用链表将线程管理,还要保证进程PCB和这些线程进行关联,PCB中的对应的指针指向对应的线程,但是这样是非常复杂的。

          【Linux】多线程(一万六千字)

          • 管理线程的策略和进程是非常像的,OS要求我们对应的线程在进程内运行,是进程内的执行分支,只要符合这个特点,就都是线程,并不一定必须上面的实现。管理进程已经设计数据结构,设计调度算法,还写了创建、等待、终止等各种接口,那么可以把进程的数据结构和调度算法等代码复用起来。
          • Linux的设计者认为,进程和线程都是执行流,具有极度的相似性,没有必要单独设计数据结构和调度算法,直接复用代码。
          • 使用进程来模拟线程。
          • Windows是单独的设计了线程模块。Linux用的是复用进程的代码来设计的。

            线程的优点

            • 创建一个新线程的代价要比创建一个新进程小得多
            • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
            • 线程占用的资源要比进程少很多
            • 能充分利用多处理器的可并行数量
            • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
            • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
            • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作

              线程的缺点

              性能损失

              • 一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型 线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的 同步和调度开销,而可用的资源不变。

                健壮性降低

                • 编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了 不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

                  缺乏访问控制

                  • 进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

                    编程难度提高

                    • 编写与调试一个多线程程序比单线程程序困难得多

                      线程异常

                      • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
                      • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该 进程内的所有线程也就随即退出

                        线程用途

                        • 合理的使用多线程,能提高CPU密集型程序的执行效率
                        • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是 多线程运行的一种表现)

                          Linux进程 VS 线程

                          进程和线程

                          • 以前的进程:一个内部只有一个线程的进程。
                          • 今天的进程:一个内部至少有一个线程的进程。我们以前的讲的进程是今天讲的进程的一种特殊情况。

                            什么是进程呢?

                            • 内核的数据结构+进程的代码和数据(也就是一个或者多个执行流、进程地址空间、页表和进程的代码和数据)
                            • 线程(task_struct)叫做进程内部的一个执行分支。
                            • 线程是调度的基本单位。
                            • 进程的内核角度:承担分配系统资源的基本实体。
                            • 不要站在调度角度理解进程,而应该站在资源角度理解进程。

                              线程共享进程数据,但也拥有自己的一部分数据:

                              • 线程ID
                              • 一组寄存器
                              • errno
                              • 信号屏蔽字
                              • 调度优先级

                                讲一个故事:

                                • 我们的社会就是一个大的系统,在社会中承担分配社会资源(汽车、彩电等)的基本实体是家庭,家庭中的每一个人都是一个执行流,各自都做着不同的事情,但每一个人都会互相协作起来,完成一个公共的事情,把日子过好。家庭中的每一个人就是线程,家庭就是一个进程。

                                  进程的多个线程共享

                                  共享同一地址空间,因此代码段(Text Segment)、数据段(Data Segment)都是共享的:

                                  • 如果定义一个函数,在各线程中都可以调用;
                                  • 如果定义一个全局变量,在各线程中都可以访问到;

                                    除此之外,各线程还共享以下进程资源和环境:

                                    • 文件描述符表
                                    • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
                                    • 当前工作目录
                                    • 用户id和组id

                                      进程和线程的关系

                                      【Linux】多线程(一万六千字)

                                      关于调度的问题

                                      CPU在选择执行流去调度的时候,用不用区分一个进程内部唯一的执行流呢?还是一个进程内部某一个执行流呢?

                                      不需要管。因为线程也有PCB、进程地址空间、页表、进程代码和数据,与进程一致,都是执行流,不需要区分。

                                      • CPU调度的执行流:线程
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]