• Linux线程的概念


    线程的概念

    一、Linux线程的概念

    • 一般教材中
      线程:是在进程内部运行的一个执行分支(执行流),属于进程的一部分,其粒度要比进程更细和轻量化
      内部:线程在进程的地址空间内运行
      执行分支:CPU调度的时候,只看PCB中每一个PCB曾经被指派过的执行方法和数据,CPU可以直接调度
      进程的一部分
    • 常规操作系统(如Windows)中,进程:线程=1:n,有可能存在着更多的线程,OS要管理这些线程→先描述,再组织!→ 引出线程控制块TCB → struct tcb{ };
    • 但是在Linux中没有专门为线程设计TCB,而是用进程的PCB来模拟线程不用维护复杂的进程和线程的关系,不用单独为线程设计任何算法,直接使用进程的一套相关的算法,OS只需要聚焦在线程间资源分配就可以了。
      在这里插入图片描述
      只创建task_struct,共享同一个进程地址空间,将当前的资源(代码+数据),划分成若干细小份,让每个task_struct使用

    重新理解进程

    • 之前的进程:内部只有一个执行流的进程
    • 今天的进程:内部可以有多个执行流的进程

    内核视角的进程与线程

    创建进程的成本是非常高的,成本——时间+空间,创建进程要使用的资源是非常多的。我们可以从内核视角看这两者区别:
    内核视角:
    进程:是承担分配系统资源的基本实体!
    线程:是CPU调度的基本单位,承担进程资源的一部分实体,进程会划分资源给线程!

    二、Linux线程与接口关系的认识

    Linux下的PCB <= 传统意义上的进程PCB,Linux线程也叫做轻量级进程(Light Weight Process)
    从这两个角度可以看出:

    • 操作系统创建线程
    • CPU调度

    Linux因为是用线程模拟实现的,复用了很多进程的代码和逻辑,所以Linux下,不会给我们直接提供操作线程的接口函数,而是提供给我们在同一个地址空间内创建PCB的方法,分配资源给指定的PCB的接口,但是这些接口从刚设计出来,对用户很不友好!(需要创建线程的接口,释放线程的接口,等待线程的接口等等),工程师对Linux轻量级进程接口进行封装,打包成库,当用户直接使用库接口——原生线程库(用户层)

    三、线程和进程的共享和私有资源

    所有的轻量级进程(可能是“线程”),都是在进程内部运行(内部指的是地址空间)
    地址空间能够标识进程所能看到的大部分资源!

    进程之间具有独立性,可以有部分共享资源
    进程共享资源如下:

    • 管道
    • IPC资源

    线程大部分资源是共享的,可以有部分资源私有(例如:栈、上下文数据)。
    同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

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

    四、线程的优缺点

    优点

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

    ps:
    计算密集型:加密、大数据运算等→主要使用的CPU资源
    I/O密集型:网络下载、云盘,ssh、在线直播,看电影等→主要使用内存和外设的IO资源

    缺点

    • 性能损失
      一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
    • 健壮性降低
      编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
    • 缺乏访问控制
      进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
    • 编程难度提高
      编写与调试一个多线程程序比单线程程序困难得多

    五、线程异常

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

    六、线程用途

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

    七、验证线程

    #include
    #include
    #include
    
    void *thread_run(void* args)
    {
        const char* id=(const char*)args;
        while(1)
        {
            printf("我是%s线程,%d\n",id,getpid());
            sleep(1);
        }
    }
    int main()
    {
        pthread_t tid;
        pthread_create(&tid,NULL,thread_run,(void*)"thread 1");
        while(1)
        {
            printf("我是main线程,%d\n",getpid());
            sleep(1);
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    makefile

    mythread:mythread.c
    	gcc -o $@ $^ -lpthread
    .PHONY:clean
    clean:
    	rm -f mythread
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结果展示:
    在这里插入图片描述
    现象说明:
    此时依旧是只有一个进程,但是进程内部有两个执行流
    指令:ps -aL
    用于查看轻量级进程

    Linux调度时是看的PID还是LWP?
    LWP

    如何理解我们之前单独一个进程的情况?
    PID==LWP
    在这里插入图片描述

  • 相关阅读:
    全网最牛批的java八股面试文(针对秋招)堪称2022最强
    Docker FreeIPA
    设计模式-外观模式
    docker-compose安装和初体验
    R语言使用attach函数绑定dataframe数据(可以直接使用列名称访问数据)
    合成数据: 利用开源技术节约资金、时间和减少碳排放
    Opencv边缘检测、轮廓发现、绘制轮廓
    java中static
    基于卡尔曼滤波的二维目标跟踪(Matlab代码实现)
    Python魔法:20个让你编程事半功倍的奇淫技巧(建议收藏)
  • 原文地址:https://blog.csdn.net/weixin_57675461/article/details/124984488