最佳实践 - 线程
- 多线程程序中避免使用信号
线程同步问题 - 线程安全问题
- mutex + cond
- __thread
最佳实践 - 用户空间
文件io
glibc
特殊进程:timer
进程的生命周期
生命阶段 | ||
---|---|---|
生 | fork exec | |
繁殖 | ||
死 | exit | |
抚养 | waitpid |
fork = mmap sbrk
IPC首选socket TCP - 可移植性和可靠性
进程内同步 - 信号屏蔽signalpending
各个线程的上下文,信号处理上下文。在各个上下文中可能访问 同一地址:需要保护
线程间的同步:mutex + cond
线程和信号处理间的同步:sigsuspend
共享方式 - 共享资源 | 同步方式 | |
---|---|---|
多线程共享 | mutex + cond | |
进程的 正常控制流和 异常控制流(信号处理控制流) | sigproc | |
进程的异常控制流 - 信号处理函数 - 多进程少用,信号不好处理
多进程和多线程 要处理和注意的东西太多了,少用,尽量少在里面处理太多
最佳实践 - 內核空间
- 内核同步。spin_lock, mutex
- 内核活动:线程、workqueue。首选workqueue
- 内存映射 mmap
- 异常控制流:上下半部
内核中,代码的几种实例(线程)化方式 首选workqueue
- 中断上下文。中断栈
- 硬中断上下文
- 软中断上下文
- 进程上下文。进程栈
- 内核线程。workqueue 是一种简化方便使用的内核线程,网卡驱动都是用这个。
- 正常进程。
内核空间和用户空间 同步的几种方式:阻塞和非阻塞
多核并行编程模型。首选 pthread + mutex + cond。
少用信号等其他IPC啥的,那些玩意只适合以前的单进程玩玩而已
内核同步
共享方式 - 共享资源 | 同步方式 - 实现互斥访问 | |
---|---|---|
进程 中断 争用 | spin_lock spin_lock_bh | |
进程间 争用 | mutex | |
琐碎
最好都是 多线程+mutex的编程模式。
内存屏障; 一定程度上相当于 内存访问操作的同步
同步的话,是针对一个动作来同步,针对一个访问动作来同步。发起动作的主体间 要做好协调同步。
如果针对的动作主体是 cpu指令。 那就是内存屏障同步。对图灵机来说,内存 就是共享内存。
如果动作主体是 进程,那需要 进程同步,首选 mutex + condition var
不同cpu间的cache同步,由硬件来支持了。
内存屏障
内存屏障的起因 就是 编译器和cpu 乱序执行 对 IO操作影响很大。
对IO影响的几个点。io的特点,要从源头去读写数据(内存地址)。
优化时机 | 读错位置 | 应对 | 啰嗦几句 |
---|---|---|---|
编译时 | 寄存器 | volatile var | 从内存去取变量,而不是cpu寄存器,因为我们是io操作,内存上的变量可能已经发生变化了(操作者非本cpu,所以本cpu无法感知到,也就 优化上时出现信息盲区。) |
运行时 | cache | cpu指令。可封装在mb() | cpu去cache读变量,而不是内存,因为我们是io操作,内存上的变量可能已经被硬件设备给修改(能通过内存总线访问内存的设备,这是cpu无法感知的,这导致cpu在信息不全的情况下,做出优化,这是需要人工干预下。) |
解除优化 | mb 全能型。相当于 volatile + cpu屏障指令。 mb 禁止 从寄存器去读,要从源头去读 |
强制 刷新 读写的cache
asm volatile ("" ::: "memory"); # 編譯器标记这个变量在 内存地址空间
volatile,编译器 不要把变量从 临时的寄存器里 读取,而要从内存去读取。
volatile 只是一种声明而已,加入一个声明标记之后,编译器在编译时 会有所处理
内核抢占
preempt_count_equals
用来判断是否在spinlock保护的区间里
irqs_disabled
判断当前中断是否开启,即是否在中断上下文
ref: - https://www.yuanguohuo.com/2020/03/31/linux-preemption-mode/