当前位置: 首页 > news >正文

从0开始linux(38)——线程(1)线程概念

欢迎来到博主专栏:从0开始linux
博主ID:代码小豪

文章目录

    • 进程与线程
    • 线程概念
    • 线程的优点
    • 线程的独立数据

进程与线程

如果要理解线程,那么进程将会时绕不开的点。首先我们回顾一下我们之前在进程章节当中是如何描述进程的?

进程=内核数据结构+代码和数据

代码和数据很好理解,我们在c/c++文件当中写的代码和数据,经过编译后形成的二进制可执行程序中,数据和指令都已二进制的形式保存在文件中。而内核数据结构指的是PCB(task_struct),mm_struct,vm_area_struct(进程地址空间),还有页表等等。这些存在于内核当中,描述与管理进程的相关数据结构。

那么线程是什么呢?

这里博主先从其中一个概念开始展开:线程是进程中的一个执行流,是进程的执行分支。

那么问题来了,什么是执行流呢?

我们可以将流看做是进程中连续的cpu指令,cpu在执行这些指令流,就叫做执行流。而根据进程地址空间,我们的cpu执行的指令、数据的地址,都是保存在进程地址空间当中的,而线程相当于是将这个进程地址空间的一部分切割开来,作为自己的地址空间,让cpu从中处理。那么此时进程就分为了两个执行流,继续执行主程序的,叫做主线程,而执行分支程序的,就是分支线程。

在这里插入图片描述

多说无益,我们尝试创建一个线程,创建线程用到的是C语言posix库中的函数pthread_create,关于这posix库是什么,我们在下一篇章节再说,pthread_create的函数原型如下:

 int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

这些参数博主先不多介绍,首先start_routine是一个函数指针,即分支线程开始的函数地址(我们可以看成是线程的main函数)。pthread_t是线程号,线程号我们后面再说。我们只需要传入一个pthread_t的参数的地址即可。

那么程序如下:

void* Thread1Run(void* addr)//分支线程的入口函数
{while(true){std::cout<<"hello 我是线程1"<<std::endl;sleep(1);}
}int main()
{pthread_t tid;//创建分支线程pthread_create(&tid,nullptr,Thread1Run,(void*)"thread-1");while(true){std::cout<<"hello 我是主线程"<<std::endl;sleep(1);}return 0;
}

这段程序的逻辑如下:当进程运行时,此时内部没有分支线程,当执行pthread_create后,创建出一个分支线程。该线程执行Thread1Run函数的逻辑。此时进程1内部就出现了两个执行流,主执行流继续执行主函数的代码,而分支线程执行Thread1Run的代码。

运行结果如下:
在这里插入图片描述
从这里可以看出,主线程和分支线程是并行的,如果不是并行的,那么主线程压根不会执行pthread_create中的代码。

线程概念

如果线程可以并行,那么一定是需要linux系统对线程进行调度的,就像进程调度那样。那么既然linux要对线程管理起来,linux是如何管理线程的呢?老生常谈,linux管理进程,是通过创建内核数据结构,描述进程,接着通过内核数据结构来进行管理,我们将这个操作称为:先描述,再组织

那么线程又要如何描述呢?我们先来思考这么一个问题:我们需要线程有什么?首先,线程要有描述符,这样操作系统才能通过描述符找到要操作的线程;接着,线程要有执行的代码和数据,因此要有自己的地址空间;线程也可以通信、因此还要有文件描述符表fd_array。线程还要能接收到信号,因此要有pending表、block表、handler表。咦,这么看下来线程的内核数据结构好像和进程的内核数据结构一模一样啊?

没错,线程和进程用的内核数据都是一样的,即task_struct!!!!

这么一搞、我们就要对一个问题进行思考了。线程是什么?进程又是什么?

首先博主为进程和线程先下一个定义:线程是操作系统调度的最小单位、进程是承载分配资源的基本实体。

在我们上一个例子中已经看到,cpu会在线程之间进行切换调度,其原理和进程之间的切换调度类似。那么如何理解进程时承载分配资源的基本实体呢?首先我们来思考,一个进程需要什么资源?

首先进程要有地址空间吧,代码、数据、堆区、栈区,这些都是实打实存在于内存当中的,第二个,进程会打开文件吧,这些文件都也是要加载到内存当中的。这些就叫做资源。

那么线程有没有这些呢?答案是有!但是为什么说进程承载资源,而不是线程承载资源呢?这是因为,由线程申请的资源,都是放到进程名下的!如何理解这句话呢?我们在前面不是说了吗?将进程空间的一部分代码、数据分割给分支线程,线程就创建出来了,那么我们逆推一下:进程=主线程+分支线程

我们之前所见过的进程,是不是没有线程呢?非也,而是这些进程当中只有一个主线程,因此,我们在前面所说的进程调度,本质上也是线程调度,只是这个线程只存在一个而已。那么现在我们知道了,线程申请的一切资源都是放在进程名下的,这也意味着,线程之间使用的资源都是共享的即,同一进程下的线程使用的地址空间是相同的,使用的文件描述表也是相同的!!那么理解了这个,对于线程和进程的关系就很明了了。

我们可以以现实举个例子,比如我们从寝室走到教室,那么我们这个人就是一个进程,执行的任务就是到达教室,而我们的左脚、右脚、左腿、右腿则是进程下的线程,各自执行自己的程序,但是目的都是为了完成一个进程的任务。这些线程所使用的资源是共享的,即人体身上的能量,你总不能说你的左腿运动和右腿运动消耗的能量不属于同一个人吧?

在linux中,线程和进程其实并没有太大的区别,因此在linux当中,线程被称为轻量级进程,又称LWP(light weight process)。所谓的cpu对进程的调度,无论是进程还是轻量级进程,它们的权重没有任何区别。而且我们还能查到轻量级进程的属性,通过指令:ps -aL可以查看,但是考虑到让轻量级进程与进程之间有所区别,因此后面还是叫线程和进程。
在这里插入图片描述

线程的优点

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

我们fork出一个新进程,由于进程之前具有独立性,因此新进程要有自己的地址空间,要有自己的内核数据结构。而线程之间都是共享进程的资源的,因此创建一个新线程,只需要创建一个新的内核数据结构来描述轻量级进程就行。

  • 线程之间切换比进程之间切换的速度快

为什么呢?因为进程之间具有独立性,不同的进程,其代码数据不同,使用的进程地址空间也不同,页表不同,打开的文件也不同,这些东西都是要实打实加载到内存的。要加载,就是要让磁盘与内存之间做慢速I/O,因此速度变慢。

而且在CPU当中也有体现,不同的进程,其上下文也是不一样的,并不是说线程的上下文就相同,而是进程的上下文更复杂。因为在CPU当中存在两个东西,一个叫做TLB,一个叫做cache。这两个硬件都是用来做缓存的。TLB负责的是页表缓存,不同的进程拥有不同的页表,因此TLB肯定不能复用,因此切换进程的时候,TLB要重新作缓存,而线程之间由于使用的是相同的进程空间,因此在切换线程的时候,TLB就不用更新。

而cache我们还没认识过,现在我们就来讲讲。当一个进程运行时,我们要将进程中的数据和代码加载到内存当中,因为cpu与内存之间做交互的速度远快于cpu与磁盘做交互,但是cpu和内存之间的交互是最快的吗?当然不是,而是cpu与cache之间的交互才是最快的,因此,当cpu执行一个代码时,会将代码附近的所有数据都加载到cache当中,我们可以输入指令cat /proc/cpuinfo可以查看到cpu信息,其中就包括cache的大小
在这里插入图片描述

我们可以看到,一个cached可以缓存40M左右的数据,因此当cpu执行一个指令时,首先会查该指令的地址是否在cache当中,如果没有,再去内存当中找,顺便在将cache当中缓存的数据更新成,新执行的指令附近40M的数据。

那么为什么说cache能提高效率呢?这其实是一个概率性的问题,因为当计算机执行完一个代码后,其第二条代码,大概率在该代码的下一条。因此将代码附近的数据加载到cache中,可以大大减少cpu访问内存的次数。但是如果你能写出让cpu访问的代码不停跳转的程序,那么这个cache就相当于是负优化了,因为cache不仅没有让你减少cpu访问内存的频率,还要不停地加载内存中的数据到cache当中,这个时间只会更长。因此,这个基于cache缓存以提高cpu效率的方法,我们称为局部性原理。因此我们所写的代码和数据,要尽可能的减少不必要的跳转,以提升程序的运行速度。

那么我们回到进程,由于进程使用的内存空间是不同的,因此cache缓存的数据,是基本不可能让两个进程一起用的。因此切换进程的时候,cache当中的数据也要更新一下,这不也导致了cpu效率变慢了吗?而线程之间由于使用的资源都是在一个进程当中的,因此cache缓存大概率不用更新,因此切换线程所需要的时间开销也会变小。

但是线程并非完全没有缺点。

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

这里我们做简单的了解,博主在后面的章节当中还会继续说明

线程的独立数据

进程当中有哪些资源是线程之间共享的?而线程之间又有什么资源是不互通的?

共享的资源有:

  • 进程地址空间
  • 信号:所有线程对于信号的处理方式,都是一致的
  • 异常:如果一个线程出现了异常,那么整个进程都会崩溃
    其实这也很好理解,比如广西周某偷了电瓶,总不能说是我的手偷的,而不是总体偷的,进而免除惩罚吧?由于线程作为进程的一部分,所有的线程的目的,都是为了让进程完成某个任务,因此如果一个线程出现了异常,那么进程的任务还能完成吗?因此将进程中所有线程一起处理,才是正确的处理方法。
  • 文件描述符

不共享的资源有:

  • LWP:线程ID
  • 信号屏蔽字:即block表,虽然线程之间对待信号的处理方法一致,但是线程可以选择屏蔽掉啊
  • 线程的上下文数据:虽然使用的资源是相同的,但是它们具体执行的指令可不同,因此cpu在切换线程时,它们的上下文数据也要进行切换(只是切换线程的复杂度远低于切换进程。)
  • errno
  • 调度优先级

相关文章:

从0开始linux(38)——线程(1)线程概念

欢迎来到博主专栏&#xff1a;从0开始linux 博主ID&#xff1a;代码小豪 文章目录 进程与线程线程概念线程的优点线程的独立数据 进程与线程 如果要理解线程&#xff0c;那么进程将会时绕不开的点。首先我们回顾一下我们之前在进程章节当中是如何描述进程的&#xff1f; 进程&…...

Ubuntu源码安装gitlab13.7集群多前端《二》

Ubuntu源码安装gitlab13.7《一》 gitaly需要调整的服务 redis socket->ipbind ....* # 0.0.0.0pg vim /etc/postgresql/14/main/pg_hba.confhost all all ..../32 md5gitaly vim /home/git/gitaly/config.tomlbin_dir "/home/gi…...

身份证OCR 识别 API 接口的发展前景

随着信息时代的到来&#xff0c;大量的身份证数据需要进行整理、存储和管理&#xff0c;OCR 识别技术可以将身份证信息转化为结构化的电子文本&#xff0c;方便后续的数据管理和分析&#xff0c;提高工作效率。 未来&#xff0c;随着人工智能和深度学习等技术的不断发展&#…...

Spring boot之BeanDefinition介绍

在spring框架中IOC容器进行bean的创建和管理。Bean的创建是一个比较复杂的过程&#xff0c;它并不像我们创建对象一样只是直接new一下就行&#xff0c;虽然有些bean确实就是New一下。但在Spring中可以通过一些途径对bean进行增强扩展。在这个过程中&#xff0c;BeanDefinition作…...

30分钟学会正则表达式

正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成一个“规则字符串”&#xff0c;这个“规则字符串”用来表达对字符串的一种过滤逻辑。 作用 匹配 查看一个字符串是否符合正则表达式的语法 搜索 正…...

Python 自动化办公的 10 大脚本

大家好&#xff0c;我是你们的 Python 讲师&#xff01;今天我们将讨论 10 个实用的 Python 自动化办公脚本。这些脚本可以帮助你简化日常工作&#xff0c;提高效率。无论是处理 Excel 文件、发送邮件&#xff0c;还是自动化网页操作&#xff0c;Python 都能派上用场。 1. 批量…...

Python蒙特卡罗MCMC:优化Metropolis-Hastings采样策略Fisher矩阵计算参数推断应用—模拟与真实数据...

全文链接&#xff1a;https://tecdat.cn/?p38397 本文介绍了其在过去几年中的最新开发成果&#xff0c;特别阐述了两种有助于提升 Metropolis - Hastings 采样性能的新要素&#xff1a;跳跃因子的自适应算法以及逆 Fisher 矩阵的计算&#xff0c;该逆 Fisher 矩阵可用作提议密…...

成绩排序

成绩排序 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给出班里某门课程的成绩单&#xff0c;请你按成绩从高到低对成绩单排序输出&#xff0c;如果有相同分数则名字字典序小的在前。 输入 第一行为…...

MySQL底层概述—7.优化原则及慢查询

大纲 1.Explain概述 2.Explain详解 3.索引优化数据准备 4.索引优化原则详解 5.慢查询设置与测试 6.慢查询SQL优化思路 1.Explain概述 使用Explain关键字可以模拟查询优化器来执行SQL查询语句&#xff0c;从而知道MySQL是如何处理SQL语句的&#xff0c;从而分析出查询语句…...

R““有什么作用在C++中,举例说明

在C中&#xff0c;R""&#xff08;双引号前加R&#xff09;表示一个原始字符串字面量&#xff08;Raw String Literal&#xff09;&#xff0c;其主要作用是让字符串中的反斜杠\和其他特殊字符不被当作转义字符处理&#xff0c;而是保留其原始字面意义。这在处理包含…...

linux中top 命令返回数据解释

当您在 Linux 终端中运行 top 命令时,它会显示一个动态更新的系统状态视图,其中包括许多有关系统性能的数据。下面是对 top 命令返回数据的详细解释: 标题栏 top - 22:46:12 up 2 days, 3:14, 1 user, load average: 0.05, 0.07, 0.09 22:46:12:当前时间。up 2 days, 3:14…...

深入理解二叉树及其变体:平衡二叉树、红黑树、B-树和B+树

一、二叉树简介 二叉树是一种非常常见的数据结构&#xff0c;它具有以下特点&#xff1a; 每个节点最多有两个子节点&#xff0c;分别称为左子节点和右子节点。每个节点的左子树和右子树都是二叉树。 二叉树的常见操作包括&#xff1a;创建、插入、删除、查找、遍历等。下面…...

C++ 编程技巧之StrongType(1)

最近看到一个NamedType的开源库&#xff0c;被里面的Strong Type这个概念和里面的模版实现给秀了一脸&#xff0c;特此总结学习一下 GitHub - joboccara/NamedType: Implementation of strong types in C C本身是一种强类型语言&#xff0c;类型包括int、double等这些build i…...

芯片测试-smith圆图

smith圆图 &#x1f4a2;smith圆图的故事&#x1f4a2;&#x1f4a2;smith圆图中的各部分来历&#x1f4a2;&#x1f4a2;公式推导&#x1f4a2;&#x1f4a2;等电阻圆特点&#x1f4a2;&#x1f4a2;等电抗圆&#x1f4a2;&#x1f4a2;等电抗圆特点&#x1f4a2; &#x1f4a…...

HTML技术深度解析:构建现代网页的基石

引言 HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是构建网页和网上应用的标准标记语言。随着互联网技术的飞速发展&#xff0c;HTML已经成为前端开发中不可或缺的核心技术之一。本文将深入探讨HTML的基本概念、核心元素、最新发展以及在…...

Leecode刷题C语言之判断是否可以赢得数字游戏

执行结果:通过 执行用时和内存消耗如下&#xff1a; bool canAliceWin(int* nums, int numsSize) {int single_digit_sum 0;int double_digit_sum 0;for (int i 0; i < numsSize; i) {if (nums[i] < 10) {single_digit_sum nums[i];} else {double_digit_sum nums[…...

Ubuntu 关机命令

在 Ubuntu 系统中&#xff0c;有几种方法可以关机。以下是常用的关机命令及其说明&#xff1a; 1. 使用 shutdown 命令 shutdown 命令是最常用和最灵活的关机方式。它可以设置定时关机&#xff0c;并且可以发送警告消息给所有登录用户。 立即关机 sudo shutdown now定时关机…...

数据采集中,除了IP池的IP被封,还有哪些常见问题?

在数据采集的过程中&#xff0c;代理IP池的使用无疑为我们打开了一扇通往信息宝库的大门。然而&#xff0c;除了IP被封禁这一常见问题外&#xff0c;还有许多其他问题可能影响数据采集的效果。本文将探讨在数据采集中&#xff0c;除了IP被封之外&#xff0c;还可能遇到的一些常…...

【Anaconda】 创建环境报错:CondaHTTPError: HTTP 000 CONNECTION FAILED for url

问题描述 使用 Anaconda 创建环境时报错&#xff1a; CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/free/noarch/repodata.json.bz2> Elapsed: -An HTTP error occurred when trying to retrieve this URL. HTTP errors are o…...

社交电商破局之“2+1 链动模式 O2O 商城小程序源码”赋能流量困境突围

摘要&#xff1a;本文聚焦于当下商家在流量困境中挣扎的现状&#xff0c;剖析传统电商高流量成本、平台流量获取难等痛点&#xff0c;阐述私域流量池兴起的缘由与价值。重点探究“21 链动模式 O2O 商城小程序源码”如何融入社交电商架构&#xff0c;通过创新机制与线上线下融合…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...