上次讲解了信号:Linux:进程信号(二.信号的保存与处理、递达、volatile关键字、SIGCHLD信号)
文章目录
1. Linux线程概念
1.1概念
在Linux系统中,线程是指在同一个进程中并发执行的多个执行序列。每个线程都有自己的程序计数器、寄存器集合、栈空间、线程特有数据等,但它们共享同一个进程的地址空间和其他资源。Linux系统中的线程是由内核进行调度和管理的,因此线程之间的切换是由内核来控制的。
以下是一些关于Linux线程的重要概念:
轻量级:Linux线程是轻量级的执行单元,它们共享进程的资源,如内存空间、文件描述符等。相比于进程,线程的创建、销毁和切换开销更小。
并发执行:在一个进程中可以创建多个线程,这些线程可以并发执行,从而提高程序的性能和响应速度。每个线程可以执行不同的任务,或者并发执行同一任务的不同部分。
共享资源:线程之间**共享同一进程的资源**,包括全局变量、静态变量、堆内存等。这种共享使得线程之间可以方便地进行通信和共享数据。
独立栈空间:每个线程有自己的栈空间,用于存储函数调用、局部变量等信息。线程的栈空间是独立的,但位于同一进程的地址空间中。
线程同步:由于线程之间共享资源,因此需要进行线程同步来避免竞争条件和数据访问冲突。Linux提供了多种线程同步机制,如互斥锁、条件变量、信号量等。
线程调度:Linux内核负责对线程进行调度,根据优先级、调度策略等进行线程切换。Linux支持多种调度策略,如先进先出(FIFO)、循环调度(Round-Robin)等。
线程状态:Linux线程可以处于运行、就绪、阻塞等不同状态,内核根据线程的状态来进行调度和管理。
- 线程是进程内部的一个执行分支,线程是CPU调度的基本单位,能够实现高效的并发执行
- 通过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
- 执行流是指程序在运行过程中的控制流程,即程序的执行路径或者执行顺序。线程是进程内部的执行单元,每个线程都有自己的执行流
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
1.2线程的理解
在
fork()
创建的子进程中,子进程会从fork()
调用的位置开始执行,继续执行父进程的代码。父进程和子进程在fork()
调用之后会并发执行,每个进程有自己独立的执行流,它们可以独立地执行不同的任务或者并发执行相同的任务。因此,子进程可以被看作是父进程的一个执行流,它继承了父进程的一部分代码和状态,但是拥有自己独立的执行环境
但是,每次创建子进程都要复制父进程的地址空间、代码段、数据段等资源,然后将子进程的执行流独立地运行起来。这代价也不小。所以线程的优势便体现出来:创建一个新线程的代价要比创建一个新进程小得多
task_struct现在是一个个的线程了
正文:代码段(区),我们的代码在进程中,全部都是串行调用的
地址空间和地址空间上的虚拟地址,本质是一种"资源
将不同的代码段分配给不同的线程,使得线程能够并发执行,从而将串行执行转变为并发执行。通过将不同的代码段分配给不同的线程,并发执行可以充分利用多核处理器的性能,提高系统的并发能力
线程在进程的地址空间内运行
Linux中线程设计的巧思
首先我们来设想一下:让我们自己来设计线程怎么设计
但是我们也想到这些逻辑,我们在设计进程时已经设计过了
Linux的设计者认为,进程和线程都是执行流,具有极度的相似性,没必要单独设计数据结构和算法直接复用代码
使用进程来模拟线程,所以Linux中没有真正意义上的线程,采用了巧妙的复用
1.3Linux线程VS进程
我们之前学习的进程:内部只有一个线程的进程
现在的进程:一个内部至少有一个线程的进程
而我们一直说进程=内核数据结构(PCB、地址空间、页表等总和)+代码数据。也是一直成立了,不过可以有多个PCB
进程的内核角度:承担分配系统资源的基本实体
线程:就是一个一个执行流,是进程内的执行分支
从内核角度来看,进程是操作系统分配系统资源的基本实体,包括内存空间、文件描述符、CPU时间等。每个进程都有自己的地址空间和资源,是操作系统中最基本的执行单位。
而线程则是进程内的执行流,是进程的一个执行分支。线程共享进程的地址空间和资源,但拥有独立的执行流和栈空间。线程之间可以更快地进行通信和数据共享,同时也能更高效地实现并发执行。
在操作系统中,进程和线程的关系可以理解为:一个进程可以包含一个或多个线程,每个线程都是进程的一部分,共享进程的资源。进程和线程之间的关系是一种包含和被包含的关系,进程是线程的容器,线程是进程的执行单元。
1.4调度与轻量级进程
在Linux系统中,所有的执行流都被称为轻量级进程(Lightweight Process,LWP),实际上就是操作系统概念中的线程。在Linux中,线程和进程的区别并不是很明显,因为Linux将线程实现为与进程相似的实体,即轻量级进程。
在Linux中,每个轻量级进程(线程)都对应一个task_struct
结构体,操作系统通过调度算法选择下一个要执行的轻量级进程,而不关心这个task_struct
属于哪个进程,或者是属于一个进程的其中一个线程。
因此,在Linux中,CPU调度的实际执行单元是轻量级进程(线程),而不是进程。每个轻量级进程都有自己的执行流,可以独立执行代码,拥有独立的栈空间和寄存器状态。多个轻量级进程可以共享一个进程的资源,实现并发执行。
在Linux中,所有的执行流都被称为轻量级进程(线程),操作系统通过调度算法选择下一个要执行的轻量级进程,而不关心它们属于哪个进程。这种设计使得线程和进程之间的切换更加高效,同时也方便了并发编程和多任务处理。
1.5程序如何划分(重拾页表、见一下LWP)
操作系统进行内存管理的基本单位是4KB
内存里都是以4kb大小分的一个一个内存块——空间
可执行程序也是以4kb进行分——内容
现在我们再来重新看待页表:
后12位叫做叶内偏移
同时读取时还要结合数据类型的大小
给不同的线程分配不同的区域,本质就是给让不同的线程,各自看到全部页表的子集
#include <iostream>
#include <thread>
#include <unistd.h>
#include <sys/types.h>
using namespace std;
void *test(void *arg)
{
while (true)
{
std::cout << "I am new thread, pid: " << getpid() << std::endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, test, nullptr);
while (true)
{
std::cout << "I am main thread, pid: " << getpid() << std::endl;
sleep(1);
}
return 0;
}
函数编译完成,是若干行代码块,函数名是该代码块的入口地址。每一行代码都有地址 -----虚拟地址(逻辑地址)
最终形成的可执行程序中的所有函数,都会按照地址空间统一编址。这意味着每个函数在虚拟地址空间中都有自己的地址范围
- 一个主线程执行main()函数
- 另一个线程执行其他函数
本质就是:代码按照不同的函数,把程序分为不同的区域。每一个执行流执行的是该函数对应的代码区域