1 进程、线程、协程
定义
【Are u OKay?——协程、线程、进程】 https://www.bilibili.com/video/BV1Wr4y1A7DS/?share_source=copy_web&vd_source=1e4d767755c593476743c8e4f64e18db
高并发:线程池,不要无休止的创建线程。--> task很小很快,thread需要切换(中断)-->协程
拥有资源
切换过程、开销
进程切换:
-
上下文保存(保存当前进程状态): 当操作系统决定切换到另一个进程时,首先需要保存当前进程的上下文信息,包括寄存器的状态、程序计数器、内存页表等。
-
切换到内核模式: 进程切换通常涉及从用户模式切换到内核模式,以便操作系统能够执行敏感的指令并更改进程的状态。
-
调度新进程: 操作系统选择要切换到的新进程,将其上下文信息加载到 CPU 寄存器和内存管理单元中。
-
切换到用户模式: 切换到新进程的用户模式,允许其执行。
在任务切换的过程中,通常会涉及到系统调用。系统调用是用户程序与操作系统之间进行通信的一种方式,用于请求操作系统提供服务。在任务切换中,以下情况可能触发系统调用:
-
上下文保存: 将当前进程的上下文信息保存到内存中,可能涉及到对内存的写操作,这可能需要调用操作系统提供的服务。
-
切换到内核模式: 用户程序切换到内核模式通常需要通过中断或异常来触发,这会涉及到一些与特权级别相关的系统调用,例如进入内核模式的中断服务例程。
-
调度新进程: 选择和调度新进程也可能涉及到系统调用,例如获取进程列表、更新进程状态等。
通信、并发
进程是应用程序/程序的执行副本,进程要管理硬件资源、CPU资源、文件资源、内存分页等,执行只需要CPU和内存。进程来回切换消耗资源,所以抽象出一个更小的单位线程,当一个程序启动以后(进程),会产生一个主线程,操作系统将计算资源不给进程,而是直接给主线程。进程是资源管理的单位,线程是程序执行的单位,线程只需要执行程序。操作系统的调度线程的执行,一个一个线程排队执行,每个线程执行时都有一个时间片,当时间片执行完了以后,切换下一个线程执行。程序执行产生一个进程,这个进程都会有一个主线程。但是操作系统调度的是自己的线程,自己的线程表,程序员在用户空间创建线程,但是真正执行的是操作系统的线程。操作系统的线程和用户线程是映射关系,也就是说,操作系统的线程才是真正的线程,程序员并不能直接执行直接的线程,必须要挂靠到操作系统的线程执行。高并发的场景下,会有很多task,为每个task创建一个线程(线程对象)很占用内存空间,每个线程都对应一个操作系统的线程(内核线程),这样操作系统忙不过来。所以设计了线程池技术,溢出线程池怎么办,让task排队(线程会回收到线程池)。
2 Linux 进程通信方式
管道
消息队列
-
内核中的链表: 消息队列通常在内核中维护一个链表,用于存储进程发送的消息。每个消息都是链表中的一个节点,包含了消息内容、发送者、接收者等信息。这样的设计使得内核能够高效地管理和调度消息。
-
系统调用: 在Linux系统中,消息队列的相关操作是通过系统调用来实现的。
共享内存
基于虚拟内存实现,不需要走系统调用。往往配合信号量
信号量(同步,限制临界资源)
pv操作
套接字
信号
3 线程通信
信号
锁机制
条件变量
信号量
4 Linux 同步
-
POSIX信号量:可用于进程同步,也可用于线程同步。
-
POSIX互斥锁 + 条件变量:只能用于线程同步。
5 进程同步
-
互斥锁(Mutex):
- 使用互斥锁来确保一次只有一个进程能够进入临界区(一段关键代码)。
- 进入临界区前先尝试获得锁,如果锁已经被其他进程占用,则等待。
-
信号量(Semaphore):
- 信号量是一种更为通用的同步工具,可以用来实现互斥和合作。
- 可以用于控制对共享资源的访问,也可以用于进程之间的通信。
读者写者问题
允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define N 100
typedef int semaphore;
semaphore mutex = 1; // 互斥信号量,用于对临界区的访问进行互斥
semaphore empty = N; // 表示缓冲区中的空槽位数量
semaphore full = 0; // 表示缓冲区中的已有数据项数量
// 生产者函数
void producer() {
while (1) {
int item = produce_item(); // 生产一个数据项
down(&empty); // 等待缓冲区有空槽位可用
down(&mutex); // 进入临界区前先获取互斥锁
insert_item(item); // 将数据项插入缓冲区
up(&mutex); // 退出临界区,释放互斥锁
up(&full); // 增加已有数据项数量
}
}
// 消费者函数
void consumer() {
while (1) {
down(&full); // 等待缓冲区中有数据项可用
down(&mutex); // 进入临界区前先获取互斥锁
int item = remove_item(); // 从缓冲区中移除数据项
consume_item(item); // 消费数据项
up(&mutex); // 退出临界区,释放互斥锁
up(&empty); // 增加空槽位数量
}
}
-
条件变量(Condition Variable):
- 条件变量用于在某个条件得到满足之前使线程等待,只有当条件满足时,才唤醒等待的线程。
- 通常与互斥锁一起使用,以确保在检查条件和等待/唤醒之间的操作是原子的。
<