【Linux下】线程概念

news2024/7/4 5:43:36

文章目录

  • 【Linux下】线程概念
      • 理解线程
      • 线程操作接口
      • 线程和进程
        • 线程和进程的关系
        • 代码验证
      • 线程优点
      • 线程缺点

【Linux下】线程概念

理解线程

一般的书上都是这么描述线程的

线程:是在进程内部运行的一个执行分支,属于进程的一部分,粒度要比进程更加细和轻量化

从上面我们就可以知道,进程是可能存在多个线程,即进程比线程可能是1:n的关系;所以os肯定是要管理线程的,怎么管理呢?

常规os的做法:先描述再组织,例如windows

即windows下os管理线程实际上是像Linux下管理进程一样,为线程设计专门的线程描述块(TCB), 而后使用数据结构将所有线程串起来进行管理

如下图:

而Linux下:选择了复用进程描述块PCB描诉线程,即使用管理进程的方式管理线程。

这样做的好处是:

  • 不用再专门为线程设计线程描述块,各种算法,并且维护线程和进程的复杂的关系(处理TCB和PCB的复杂关系)
  • os只需要聚焦于线程之间的资源分配上即可

线程使用的也是PCB描述块,那么Linux下的进程到底是怎么样的呢?之前的博客说的并不完整,因为没有涉及到线程相关的知识

重新理解Linux下线程和进程

我们在之前博客所讨论的进程是这样的

  • 进程中只有一个TCB 描述块 --即进程中只有一个执行流

实际上LInux下的进程

  • 含有多个TCB描述块 – 即进程含有多个执行流

然后再重新理解一下CPU眼中的PCB描述块

我们之前曾说,CPU调度进程,实际上将一个一个PCB放到自己的runqueue里面,之前我们是将一个PCB看成一个进程,而现在我们知道实际上的PCB要小于我们之前所说的PCB的粒度,即实际上CPU眼中,一个PCB就是一个等待调度的执行流–线程(可能属于不同的进程)

即在os视角下的进程和线程之间的关系是这样的

  • 进程是承担分配os资源的基本实体
  • 线程是CPU调度的基本单位,承担进程部分资源的基本实体–进程划分资源给线程

如图:

这样我们就可以重新理解一下一开始说的线程的概念了

  • 线程:是在进程内部运行的一个执行分支,属于进程的一部分,粒度要比进程更加细和轻量化

理解线程是在进程运行的

Linux下的线程是在进程的地址空间下运行的,不同的线程可能运行进程中的不同代码和数据

理解线程是一个执行分支

CPU调度时,只看到线程是一个PCB结构,而每个线程都是被委派过对应的代码和数据的(可以单独执行),CPU眼中一个PCB就是一个可调度的执行流,所以CPU可以直接调度线程

理解LInux下的进程也被称为轻量级进程

Linux下,使用进程描述块task_struct 来描述线程,即相对于其他系统实现来说,一个PCB可能就代表一个进程,而在Linux下,可能描述的是一个进程(只有一个PCB的进程) ,也可能是一个线程,所以说Linux下的,进程更加轻量化,本质上是因为,就于管理机制这部分,os对进程和线程并没有做过多概念上的区分

线程操作接口

因为Linux下对进程和线程管理机制,本质上没有对进程和线程做太大区分,Linux只给我们提供了在同一个地址空间内创建PCB的接口–创建线程,以及分配资源给指定的PCB的接口–分配资源给线程,即Linux本身并未提供像操作进程的接口给我们操作线程;(补充:实际上,Linux也可以为我们提供线程操作的系统级别接口,但为了LInux中的代码不会太过于臃肿和赘余,就将这部分工作交给了我们用户层)

而如果使用原生接口,实际上对用户是极其不友好的,例如:我们要自己写分配资源的逻辑,以及回收线程资源的逻辑…等部分代码

于是就有一些系统级别的工程师站了出来,对原生接口进行了封装,于是线程库就产生了(属于用户层),于是我们引进该库之后,就可以像操作进程一样操作线程了,而我们之后对线程控制的函数都是依据该线程库展开的

线程和进程

线程组里的线程共享的资源

  • 文件描述符表
  • 各种信号的处理方式
  • 当前工作目录
  • 用户组id和组id
  • 同一地址空间(即代码和数据)

线程所独享的资源

  • 线程id
  • 一组寄存器(上下文数据)–保存线程执行的临时数据
  • 栈 --因为线程是一个单独的执行流,所以线程肯定是有自己的栈结构的
  • Errno
  • 信号屏蔽字
  • 调度优先级

线程和进程的关系

例子:就好像一个国家,分配国家资源的基本实体就是一个家庭,一个家庭里面可能会有不同的人,不同的角色,但一家人都是为了让家庭过得更好而努力,互帮互助,共享家里面的资源,也独享在家里面的部分资源,例如自己的卧室等

os就像一个国家,而进程就好像上面的家庭,线程就是家庭里面的成员

代码验证

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>


#define NUM 5

//测试线程创建和线程退出
void* pthread_run(void *args)
{
    int id = *(int *)args;
    while(1)
    {
        printf("I am 新线程[%d]:%p\n",id,pthread_self());
        sleep(1);
    }
    return (void*)123;
}

int main()
{
    pthread_t tid[NUM];
    for(int i=0;i<NUM;i++)
    {
        pthread_create(&tid[i], NULL, pthread_run, (void *)&i);
    }
    while (1)
    {    
        printf("I am main线程:%d\n", getpid());                                                                                                                                               
        sleep(1);
    }    
    return 0;
}

实验现象:

我们发现,竟然同时有6个执行流同时在向显示器打印,这在之前的单进程程序是不可能实现的,虽然说打印时有些凌乱(因为线程是同时向显示器打印的,我们目前还未控制线程访问显示器文件的次序)

查看线程的命令

ps -aL

例:使用ps -L查看上面的程序里的线程,我们会发现主线程的线程id和进程的pid是一样的,我们后面会提及线程组的概念,实际上进程pid也叫线程组id

在这还需要再说明一个,我们之前曾经说,CPU调度进程PCB时是通过PID进行区分的,而实际上CPU调度PCB是通过线程id–LWP,进行调度的,因为线程是CPU调度的基本实体;而我们之前说CPU调度进程是是通过PID进行区分的,其实也没有错误,因为我们之前讨论的都是单线程的情况,即进程PID=主线程LWP的(如上图进程pid 和主线程的LWP 都为23243),

线程优点

  • 创建一个新线程的代价要比创建一个新进程小得多

创建一个进程,需要在内核中重新申请(task_struct mm_struct files_struct 页表…等等资源),也就是说,创建进程实际上是一个从0到1的过程;而创建一个线程,mm_struct ,files_struct…这些资源都是直接使用其所属的进程的

  • 于进程之间的切换相比,线程之间的切换操作需要操作系统做的工作要少得多

切换的临时数据少,且因为线程之间可能是使用同一地址空间的,就是说访问的页表可能不需要重新改变

  • 线程所占用的资源要比进程少得多
  • 能充分使用多处理器的可并行数量
  • 在等待慢速i/o操作结束的同时,程序可执行其他的计算任务

3,4 条实际上多进程也具备这个优点

  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现计算
  • i/o密集型应用,为了提高性能,可以将i/o等待的时间重叠,线程可以同时等待不同的i/o操作

而“密集”到什么程度呢? 线程是否是越多越好呢?

并不是,对于计算密集型来说,如果线程太多,就会导致线程的过度切换(100个线程同时在run_queue里面等待CPU调度),整体运行速度依旧还是会很慢

对于i/o密集型应用来说,虽然说线程数虽然可以多一点–因为i/o主要时间都是线程在等待i/o资源就绪,就没有这么多的线程在等待CPU调度,但如果太多了,也会产生和计算密集型应用一样的困扰

所以线程的优点有如下:

  • 创建一个新线程的代价要比创建一个新进程小得多

  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多线程占用的资源要比进程少很多

  • 能充分利用多处理器的可并行数量

  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务

  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现

  • I/O密集型应用,为了提高性能,将I/O操作重叠(主要是等待的时间)。线程可以同时等待不同的I/O操作。

线程缺点

  • 性能损失:

    • 一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变
    • 理解: 因为一个计算机密集型线程,是一直保持在CPU的runqueue中进行等待CPU调度的,而不像其他线程可能大部分时间在等待某些资源就绪(在wait_queue里)
  • 健壮性降低

    • 理解:我们知道进程和进程之间是存在独立性的,所以一个进程挂掉,并不会影响其他进程的正常工作;而线程和线程之间,是存在共享资源的情况的,而当一个线程组中任意一个线程出现程序异常时,整个线程组都会崩溃掉。–因为线程程序异常os是给线程所在的进程发送的信号,而后将整个进程终止掉,进程都不在了,里面的线程怎么可能还存在
  • 缺乏控制:

    • 因为线程之间可能存在**共享资源(共享程序地址空间)**的关系,而且一个线程是可以做到访问其他线程的代码和数据的,这样就可能会出现问题
  • 编程难度提高:

    • 编程和调试一个多线程代码比调试一个多进程的代码要难得多

    因为一个线程可能在任何时刻修改了其他线程的代码和数据,而造成程序异常的问题的出现

掉,进程都不在了,里面的线程怎么可能还存在

  • 缺乏控制:

    • 因为线程之间可能存在**共享资源(共享程序地址空间)**的关系,而且一个线程是可以做到访问其他线程的代码和数据的,这样就可能会出现问题
  • 编程难度提高:

    • 编程和调试一个多线程代码比调试一个多进程的代码要难得多

    因为一个线程可能在任何时刻修改了其他线程的代码和数据,而造成程序异常的问题的出现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/500808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简单搭建Fabric网络

Fabric网络的搭建分为两个阶段&#xff1a;生成网络拓扑和启动网络。在这里&#xff0c;我们假设要搭建一个具有一个orderer节点和两个peer节点的Fabric网络。 在生成网络拓扑之前&#xff0c;需要准备好以下文件&#xff1a; crypto-config.yaml&#xff1a;用于生成组织和节…

什么牌子蓝牙耳机好用不贵?国产性价比高的蓝牙耳机推荐

相较于有线耳机&#xff0c;无线蓝牙耳机更便携、功能更丰富&#xff0c;不用受到耳机孔与线的限制。那么&#xff0c;什么牌子的蓝牙耳机好用不贵&#xff1f;针对这个问题&#xff0c;我给大家推荐几款国产性价比高的蓝牙耳机&#xff0c;可以当个参考。 一、南卡小音舱Lite…

JAVA代码规范审查

JAVA代码规范审查 1. 添加必要的注释 所有的类都必须添加创建者和创建日期&#xff0c;以及简单的注释描述 方法内部的复杂业务逻辑或者算法&#xff0c;需要添加清楚的注释 一般情况下&#xff0c;注释描述类、方法、变量的作用 任何需要提醒的警告或TODO&#xff0c;也要注…

python基础语法1

基本语句 判断语句 1.if语句 if 语法格式: if 要判断的条件:条件成立时&#xff0c;要做的事情 -------------------------- 执行流程:判断条件为True就执行if的语句体.条件为False就执行if以外的代码. if 案例: age int(input(请输入年龄))print(-----开始-----) if ag…

测试遵循的原则

测试遵循的原则 软件测试是在规定的条件下对程序进行操作&#xff0c;以发现程序错误&#xff0c;衡量软件质量&#xff0c;并对其是否能满足设计要求进行评估的过程。软件测试的基本原则有助于测试人员进行高质量的测试&#xff0c;尽早尽可能多的发现缺陷&#xff0c;并负责跟…

【51单片机】:串口通信基础知识详解

学习目标&#xff1a; 串口通信的基础知识和设置详解。 学习内容&#xff1a;串口通信基础知识 并行通信&#xff1a;多条数据线将数据字节的各个位同时传送*缺点&#xff1a;1.电磁干扰 2.成本高 3.由于传输线较多&#xff0c;长距离传输成本高串口通信&#xff1a;将数据字节…

STL常用梳理——STACK、QUEUE

STL——适配器篇 1、ListSTL list 容器介绍list使用 2、适配器介绍3、Deque容器Stack、Queue适配器实现 1、List STL list 容器介绍 STL list 容器&#xff0c;又称双向链表容器&#xff0c;即该容器的底层是以双向链表的形式实现的。这意味着&#xff0c;list 容器中的元素可…

Lecture 11(Preparation):领域自适应 (Domain Adaptation)

Domain shift: Training and testing data have different distributions. Transfer learning&#xff1a;在A任务上学到的技能&#xff0c;可以被用在B任务上 Domain Adaptation的技术&#xff0c;可以看作是Transfer learning的一种 Domain Adaptation: 第一种情况&#xf…

栈和队列OJ题思路分享之括号匹配(C语言实现)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:刷题分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你刷更多C语言和数据结构的题!   &#x1f51d;&#x1f51d; 栈和队列刷题分享 1. 前言&…

【密码学复习】第七章 公钥加密体制

公钥加密模型 密钥生成过程&#xff1a;接收消息的端系统&#xff08;如图中的接收者Alice&#xff09;产生一对密钥(PKA ,SKA ), PKA 是公开钥&#xff08;用于加密&#xff09;&#xff0c; SKA 是秘密密钥&#xff08;用于解密&#xff09;. 加密过程&#xff1a;Bob想向…

从C出发 30 --- 指针与堆空间

数组 就是变量的有序集&#xff0c;因此只有可以动态的创建一个变量&#xff0c;就可以动态的创建多个变量了&#xff0c; 堆空间就是一片内存区域&#xff0c;用于动态创建内存变量的区域 void 类型是基础类型&#xff0c;不是基础数据类型&#xff0c;为什么&#xff1f;因为…

MySQL --- 图形化工具DDL表结构操作

一. 图形化工具 1. 介绍 前面学习了DDL中关于数据库操作的SQL语句&#xff0c;在编写这些SQL时&#xff0c;都是在命令行当中完成的。在命令行当中来敲这些SQL语句很不方便&#xff0c;主要的原因有以下 3 点&#xff1a; 没有任何代码提示。&#xff08;全靠记忆&#xff0…

行为型模式-备忘录模式

备忘录模式 概述 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;很多软件都提供了撤销&#xff08;Undo&#xff0…

【自动化测试】Appium环境设置

启动Appium需要的环境配置有些复杂&#xff0c;这里将需要的环境配置整理出来&#xff0c;附上博主亲测有效的教程&#xff0c;有些教程有一点点老了&#xff0c;博主也做了备注&#xff0c;并且有些老一点步骤&#xff0c;博主根据现有的情况做了新的图文教程&#xff0c;大家…

近源渗透学习

一、近源渗透 近源渗透测试是网络空间安全领域逐渐兴起的一种新的安全评估手段。 它是一种集常规网络攻防、物理接近、社会工程学及无线电通信攻防等能力于一体的高规格网络安全评估行动。网络安全评估小组在签订渗透测试授权协议后&#xff0c;通过乔装、社工等方式实地物理侵…

微服务体系

目录 结构服务注册中心EurekaZookeeperConsul上面三者的对比 Ribbon 负载均衡负载均衡OpenFeign 远程服务调用Hystrix服务熔断常见的设置 Hystrix 工作流程Hystrix-DashBoard 的使用 SpringCloud Gateway核心工作流程 SpringCloud Config 分布式配置中心bootstrap.yml配置文件 …

独立按键识别

项目文件 文件 关于项目的内容知识点可以见专栏单片机原理及应用 的第四章 IO口编写 参考图电路编写程序&#xff0c;要求实现如下功能: 开始时LED均为熄灭状态&#xff0c;随后根据按键动作点亮相应LED(在按键释放后能继续保持该亮灯状态&#xff0c;直至新的按键压下时为止…

【实用工具】SpringBoot实现接口签名验证

需求场景 由于项目需要开发第三方接口给多个供应商&#xff0c;为保证Api接口的安全性&#xff0c;遂采用Api接口签名验证。 Api接口签名验证主要防御措施为以下几个&#xff1a; 请求发起时间得在限制范围内请求的用户是否真实存在是否存在重复请求请求参数是否被篡改 项目…

java8新特性——StreamAPI

1.集合处理数据的弊端 当我们在需要对集合中的元素进行操作的时候&#xff0c;除了必需的添加&#xff0c;删除&#xff0c;获取外&#xff0c;最典型的操作就是集合遍历。 package com.wxj.streamapi;import java.util.ArrayList; import java.util.Arrays; import java.uti…

E. Vasya and Good Sequences(异或)

Problem - E - Codeforces Vasya有一个由n个整数组成的序列a。 Vasya可以执行以下操作&#xff1a;从序列中选择一些数字&#xff0c;并交换其二进制表示中的任意一对位。例如&#xff0c;Vasya可以将数字6&#xff08;… 000000001102&#xff09;转换为3&#xff08;… 00000…