Linux并发与同步--基础知识

并发与同步

2.atomic_cmpxchg()和atomic_xchg()分别表示什么含义?

答:
    atomic_cmpxchg()和atomic_xchg()是原子操作函数,用于在多线程编程中实现原子操作。

  1. atomic_cmpxchg():该函数表示原子比较并交换操作。它接受三个参数:一个指向要进行比较和交换的内存位置的指针、期望的值和新值。函数首先比较给定内存位置的值是否与期望的值相等,如果相等,则将新值写入该位置。这个操作是原子的,意味着在执行期间不会被其他线程中断。该函数返回之前在给定内存位置的值。

  2. atomic_xchg():该函数表示原子交换操作。它接受两个参数:一个指向要进行交换的内存位置的指针和新值。函数将新值写入给定内存位置,并返回之前在该位置的值。这个操作同样也是原子的,不会被其他线程中断。

这两个函数在多线程编程中常用于确保对共享资源的原子性操作,避免竞态条件和数据不一致的问题。

3.在ARM64中,CAS指令包含了加载-获取和存储-释放指令,它们的作用是什么?

答:
   

4.atomic_try_cmpxchg()函数和atomic_cmpxchg()函数有什么区别?

答:
    atomic_try_cmpxchg()和atomic_cmpxchg()函数在功能上有一些区别。

  1. atomic_try_cmpxchg()函数:该函数是一个尝试比较并交换操作。它接受三个参数:一个指向要进行比较和交换的内存位置的指针、期望的值和新值。该函数会尝试比较给定内存位置的值是否与期望的值相等,如果相等,则将新值写入该位置。如果比较成功,函数返回true;否则,返回false。这个操作是原子的,即在执行期间不会被其他线程中断。

  2. atomic_cmpxchg()函数:该函数也是一个比较并交换操作,与atomic_try_cmpxchg()函数类似。它接受三个参数:一个指向要进行比较和交换的内存位置的指针、期望的值和新值。函数首先比较给定内存位置的值是否与期望的值相等,如果相等,则将新值写入该位置。这个操作是原子的,即在执行期间不会被其他线程中断。与atomic_try_cmpxchg()函数不同的是,atomic_cmpxchg()函数不返回比较结果,而是返回之前在给定内存位置的值。

总结来说,atomic_try_cmpxchg()函数会返回比较结果,而atomic_cmpxchg()函数则返回之前的值。具体使用哪个函数取决于你的需求,如果需要知道比较结果,则可以使用atomic_try_cmpxchg()函数;如果只关心之前的值,则可以使用atomic_cmpxchg()函数。

5.cmpxchg_acquire()函数、cmpxchg_release()函数、cmpxchg_relaxed()函数以及cmpxchg()函数的区别是什么?

答:
   

6.请举例说明内核使用内存屏障的场景。

答:
   

7.smp_cond_load_relaxed()函数的作用和使用场景是什么?

答:
   

8.smp_mb__before_atomic()函数和smp_mb__after_atomic()函数的作用和使用场景是什么?

答:
   

9.为什么自旋锁的临界区不能睡眠(不考虑RT-Linux的情况)?

答:
   

10.Linux内核中经典自旋锁的实现有什么缺点?

答:
   

11.为什么自旋锁的临界区不允许发生抢占?

答:
   

12.基于排队的自旋锁机制是如何实现的?

答:
   

13.如果在spin_lock()和spin_unlock()的临界区中发生了中断,并且中断处理程序也恰巧修改了该临界区,那么会发生什么后果?该如何避免呢?

答:
   

14.排队自旋锁是如何实现MCS锁的?

答:
   

15.排队自旋锁把32位的变量划分成几个域,每个域的含义和作用是什么?

答:
   

16.假设CPU0先持有了自旋锁,接着CPU1、CPU2、CPU3都加入该锁的争用中,请阐述这几个CPU如何获取锁,并画出它们申请锁的流程图。

答:
   

17.与自旋锁相比,信号量有哪些特点?

答:
   

18.请简述信号量是如何实现的。

答:
   

19.乐观自旋等待的判断条件是什么?

答:
   

20.为什么在互斥锁争用中进入乐观自旋等待比睡眠等待模式要好?

答:
   

21.假设CPU0~CPU3同时争用一个互斥锁,CPU0率先申请了互斥锁,然后CPU1也加入锁的申请。CPU1在持有锁期间会进入睡眠状态。然后CPU2和CPU3陆续加入该锁的争用中。请画出这几个CPU争用锁的时序图。

答:
   

22.Linux内核已经实现了信号量机制,为何要单独设置一个互斥锁机制呢?

答:
   

23.请简述MCS锁机制的实现原理。

答:
   

24.在编写内核代码时,该如何选择信号量和互斥锁?

答:
   

25.什么时候使用读者锁?什么时候使用写者锁?怎么判断?

答:
   

26.读写信号量使用的自旋等待机制是如何实现的?

答:
   

27.RCU相比读写锁有哪些优势?

答:
   

28.请解释静止状态和宽限期。

答:
   

29.请简述RCU实现的基本原理。

答:
   

30.在大型系统中,经典RCU遇到了什么问题?Tree RCU又是如何解决该问题的?

答:
   

31.在RCU实现中,为什么要使用ULONG_CMP_GE()和ULONG_CMP_LT()宏来比较两个数的大小,而不直接使用大于号或者小于号来比较?

答:
   

32.请简述一个宽限期的生命周期及其状态机的变化。

答:
   

33.请阐述原子操作、自旋锁、信号量、互斥锁以及RCU的特点和使用规则。

答:
    原子操作:原子操作是指在执行过程中不会被中断的操作。它是在多线程环境下保证数据的一致性和线程安全的一种机制。原子操作通常是不可分割的,要么完全执行,要么完全不执行。在并发编程中,原子操作可以用来保护共享资源,避免多个线程同时访问和修改同一数据导致的竞态条件。

自旋锁:自旋锁是一种基本的锁机制,它采用忙等待的方式来实现线程的互斥。当一个线程尝试获取自旋锁时,如果锁已经被其他线程占用,那么该线程会一直循环等待,直到获取到锁为止。自旋锁适用于锁的持有时间很短,且线程竞争不激烈的情况下,可以避免线程切换的开销。

信号量:信号量是一种用于控制对共享资源的访问的同步机制。它可以用来限制同时访问某个资源的线程数量。信号量维护一个计数器,线程在访问资源之前需要申请信号量,如果信号量计数器大于0,则线程可以继续执行;如果计数器为0,则线程需要等待其他线程释放信号量。信号量适用于线程竞争激烈的情况下,可以控制并发访问的数量。

互斥锁:互斥锁是一种常用的线程同步机制,用于保护共享资源的访问。互斥锁在同一时刻只允许一个线程访问被保护的资源,其他线程需要等待锁的释放。互斥锁可以防止多个线程同时访问共享资源,从而避免数据的不一致性和竞态条件的发生。

RCU(Read-Copy-Update):RCU是一种用于实现读写并发的机制。它通过在更新数据时创建副本,而不是直接修改原始数据,来实现读操作的无锁化。当有读操作时,RCU可以保证读取到的是一个稳定的数据副本,而不会受到更新操作的影响。RCU适用于读操作频繁、写操作相对较少的场景,可以提高并发性能。

以上是对原子操作、自旋锁、信号量、互斥锁和RCU的特点和使用规则的简要介绍。具体使用时需要根据具体的编程语言和场景进行选择和配置。

34.在KSM中扫描某个VMA以寻找有效的匿名页面时,假设此VMA恰巧被其他CPU销毁了,会不会有问题呢?

答:
   

35.请简述PG_locked的常见使用方法。

答:
   

36.在mm/rmap.c文件中的page_get_anon_vma()函数中,为什么要使用rcu_read_lock()函数?什么时候注册RCU回调函数呢?

答:
    在 mm/rmap.c 文件中的 page_get_anon_vma() 函数中,使用 rcu_read_lock() 函数是为了实现对RCU保护的数据结构进行读取操作。

在Linux内核中,RCU机制常用于保护共享的数据结构,例如匿名VMA(Anon VMA)链表。 page_get_anon_vma() 函数用于获取与给定页相关联的匿名VMA。由于匿名VMA链表可能会被其他线程修改,在访问链表时使用 rcu_read_lock() 函数可以获得一个RCU读取锁,以确保读取链表时不会被其他线程修改。

至于何时注册RCU回调函数,一般是在数据结构的创建或初始化阶段进行。当需要更新受RCU保护的数据结构时,可以通过注册RCU回调函数来延迟数据结构的释放,以确保没有其他线程正在使用该数据结构。具体的注册方式和时机取决于具体的实现和需求。

37.在mm/oom_kill.c的select_bad_process()函数中,为什么要使用rcu_read_lock()函数?什么时候注册RCU回调函数呢?

答:
    在 mm/oom_kill.c 文件中的 select_bad_process() 函数中,使用 rcu_read_lock() 函数是为了实现读取RCU(Read-Copy-Update)保护的数据结构。

RCU是一种用于并发编程中的内存管理机制,它允许在不阻塞其他线程的情况下对共享数据进行更新。在Linux内核中,RCU机制常用于保护共享的数据结构,例如进程列表。

select_bad_process() 函数中,需要访问进程列表以选择一个被认为是"bad"的进程。由于进程列表可能被其他线程修改,使用 rcu_read_lock() 函数可以获得一个RCU读取锁,以确保读取进程列表时不会被其他线程修改。

至于什么时候注册RCU回调函数,一般是在数据结构初始化阶段进行。当需要更新受RCU保护的数据结构时,可以通过注册RCU回调函数来延迟数据结构的释放,以确保没有其他线程正在使用该数据结构。具体的注册方式和时机取决于具体的实现和需求。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 操作系统概述 1 1.1 认识操作系统 1 1.1.1 从使用者角度 1 1.1.2 从程序开发者角度 2 1.1.3 从操作系统在整个计算机系统中所处位置 2 1.1.4 从操作系统设计者的角度 3 1.2 操作系统的发展 4 1.2.1 操作系统的演变 4 1.2.2 硬件的发展轨迹 5 1.2.3 软件的轨迹 6 1.2.4 单内核与微内核操作系统 7 1.3 开放源代码的Unix/Linux操作系统 8 1.3.1 Unix的诞生和发展 8 1.3.2 Linux的诞生 9 1.3.3 操作系统标准POSIX 9 1.3.4 GNU和Linux 9 1.3.5 Linux的开发模式 10 1.4 Linux内核 10 1.4.1 Linux内核的位置 10 1.4.2 Linux内核的作用 11 1.4.3 Linux内核子系统 11 1.5 Linux内核源代码 13 1.5.1 多版本的内核源代码 13 1.5.2 Linux内核源代码的结构 13 1.5.3 Linux内核源代码分析工具 14 习题1 15 第2章 内存寻址 17 2.1 内存寻址简介 17 2.1.1 Intel x86 CPU寻址方式的演变 18 2.1.2 IA32寄存器简介 19 2.1.3 物理地址、虚拟地址及线性地址 21 2.2 分段机制 22 2.2.1 地址转换及保护 24 2.2.2 Linux中的段 24 2.3 分页机制 25 2.3.1 页与页表 25 2.3.2 线性地址到物理地址的转换 28 2.3.3 分页示例 28 2.3.4 页面高速缓存(cache) 29 2.3.5 Linux中的分页机制 30 2.4 Linux中的汇编语言 31 2.4.1 AT&T与Intel汇编语言的比较 31 2.4.2 AT&T汇编语言的相关知识 32 2.5 Linux系统地址映射示例 33 习题2 35 第3章 进程 37 3.1 进程介绍 37 3.1.1 程序和进程 37 3.1.2 进程的层次结构 38 3.1.3 进程状态 39 3.1.4 进程实例 40 3.2 进程控制块 41 3.2.1 进程状态 42 3.2.2 进程标识符 43 3.2.3 进程之间的亲属关系 43 3.2.4 进程控制块的存放 44 3.3 进程的组织方式 45 3.3.1 进程链表 45 3.3.2 散列表 46 3.3.3 可运行队列 47 3.3.4 等待队列 47 3.4 进程调度 48 3.4.1 基本原理 48 3.4.2 时间片 50 3.4.3 Linux进程调度时机 50 3.4.4 进程调度的依据 51 3.4.5 调度函数schedule()的实现 52 3.5 进程的创建 54 3.5.1 创建进程 55 3.5.2 线程及其创建 56 3.6 与进程相关的系统调用及其应用 58 3.6.1 fork系统调用 58 3.6.2 exec系统调用 59 3.6.3 wait系统调用 60 3.6.4 exit系统调用 62 3.6.5 进程的一生 63 3.7 与调度相关的系统调用及应用 63 习题3 65 第4章 内存管理 67 4.1 Linux的内存管理概述 67 4.1.1 虚拟内存、内核空间和用户空间 67 4.1.2 虚拟内存实现机制间的关系 69 4.2 进程用户空间的管理 70 4.2.1 进程用户空间的描述 71 4.2.2 进程用户空间的创建 74 4.2.3 虚存映射 76 4.2.4 进程的虚存区示例 76 4.2.5 与用户空间相关的系统调用 78 4.3 请页机制 79 4.3.1 缺页异常处理程序 79 4.3.2 请求调页 81 4.3.3 写时复制 83 4.4 物理内存的分配与回收 83 4.4.1 伙伴算法 85 4.4.2 物理页面的分配 86 4.4.3 物理页面的回收 88 4.4.4 slab分配模式 89 4.4.5 内核空间非连续内存区的分配 93 4.5 交换机制 95 4.5.1 交换的基本原理 95 4.5.2 页面交换守护进程kswapd 99 4.6 内存管理实例 99 4.6.1 相关背景知识 100 4.6.2 代码体系结构介绍 100 4.6.3 实现步骤 103 4.6.4 程序代码 103 习题4 108 第5章 中断和异常 110 5.1 中断的基本知识 110 5.1.1 中断向量 110 5.1.2 外设可屏蔽中断 111 5.1.3 异常及非屏蔽中断 112 5.1.4 中断描述符表 112 5.1.5 相关汇编指令 113 5.2 中断描述符表的初始化 114 5.2.1 IDT表项的设置 114 5.2.2 对陷阱门和系统门的初始化 115 5.2.3 中断门的设置 116 5.3 中断处理 116 5.3.1 中断和异常的硬件处理 116 5.3.2 中断请求队列的建立 117 5.3.3 中断处理程序的执行 119 5.3.4 从中断返回 121 5.4 中断的下半部处理机制 121 5.4.1 为什么把中断分为两部分来处理 122 5.4.2 小任务机制 122 5.4.3 下半部 124 5.4.4 任务队列 125 5.5 中断应用——时钟中断 125 5.5.1 时钟 125 5.5.2 时钟运作机制 126 5.5.3 Linux的时间系统 127 5.5.4 时钟中断处理程序 128 5.5.5 时钟中断的下半部处理 129 5.5.6 定时器及其应用 129 习题5 132 第6章 系统调用 133 6.1 系统调用与应用编程接口、系统命令、内核函数的关系 133 6.1.1 系统调用与API 133 6.1.2 系统调用与系统命令 134 6.1.3 系统调用与内核函数 134 6.2 系统调用处理程序及服务例程 135 6.2.1 初始化系统调用 136 6.2.2 system_call()函数 136 6.2.3 参数传递 137 6.2.4 跟踪系统调用的执行 139 6.3 封装例程 140 6.4 添加新系统调用 141 6.5 实例——利用系统调用实现一个调用日志收集系统 143 6.5.1 代码体系结构 143 6.5.2 把代码集成到内核中 146 6.5.3 实现步骤 148 习题6 148 第7章 内核中的同步 149 7.1 临界区和竞争状态 149 7.1.1 临界区举例 149 7.1.2 共享队列和加锁 150 7.1.3 确定保护对象 151 7.1.4 死锁 152 7.1.5 并发执行的原因 153 7.2 内核同步方法 153 7.2.1 原子操作 153 7.2.2 自旋锁 155 7.2.3 信号量 156 7.3 并发控制实例 157 7.3.1 内核任务及其并发关系 158 7.3.2 实现机制 158 7.3.3 关键代码解释 162 7.3.4 实现步骤 163 习题7 164 第8章 文件系统 165 8.1 Linux文件系统基础 165 8.1.1 Linux文件结构 165 8.1.2 Linux文件系统 166 8.1.3 文件类型 167 8.1.4 文件访问权限 168 8.2 虚拟文件系统 168 8.2.1 虚拟文件系统的引入 168 8.2.2 VFS中的数据结构 170 8.2.3 VFS超级块数据结构 171 8.2.4 VFS的索引节点 173 8.2.5 目录项对象 174 8.2.6 与进程相关的文件结构 176 8.2.7 主要的数据结构之间的关系 179 8.3 文件系统的注册、安装与卸载 180 8.3.1 文件系统的注册和注销 180 8.3.2 文件系统的安装 181 8.3.3 文件系统的卸载 183 8.4 页缓冲区 183 8.4.1 address_space对象 183 8.4.2 address_space对象的操作函数表 184 8.5 文件的打开与读写 185 8.5.1 打开文件 185 8.5.2 读写文件 187 8.6 编写一个文件系统 189 8.6.1 Linux文件系统的实现要素 189 8.6.2 什么是romfs文件系统 191 8.6.3 romfs文件系统的布局与文件结构 191 8.6.4 具体实现的对象 192 习题8 195 第9章 设备驱动 196 9.1 概述 196 9.2 设备驱动程序基础 198 9.2.1 I/O端口 199 9.2.2 设备文件 200 9.2.3 中断处理 201 9.2.4 设备驱动程序框架 203 9.3 字符设备驱动程序 204 9.3.1 字符设备驱动程序的注册 204 9.3.2 简单的字符设备驱动程序示例 205 9.4 块设备驱动程序 208 9.4.1 块设备驱动程序的注册 209 9.4.2 块设备请求 212 习题9 215 附录A 内核中的链表 216 A.1 链表数据结构简介 216 A.2 内核链表数据结构的定义及初始化 217 A.3 操作链表的接口 218 A.4 遍历链表 219 附录B 内核模块 221 B.1 什么是模块 221 B.2 编写一个简单的模块 221 B.3 模块编程的基础知识 222 B.4 模块的编译 224 B.5 模块实用程序modutils 226 附录C Linux内核编译 228 C.1 内核简介 228 C.2 为什么重新编译内核 228 C.3 内核编译模式 229 C.4 新版本内核的获取和更新 229 C.5 内核编译 230 C.6 修改并重启管理器 232 附录D Linux编程基础(C语言环境) 233 D.1 Linux编程常识 233 D.1.1 相关标准(ANSI C、POSIX、SVID、XPG) 233 D.1.2 函数库和系统调用 234 D.1.3 在线文档(man、info、HOWTO) 235 D.1.4 C语言编程风格 237 D.2 Linux上的C/C++编译器和调试器 238 D.2.1 运行gcc/egcs 238 D.2.2 gcc/egcs的主要选项 240 D.2.3 gdb简介 240 D.2.4 gdb的常用命令 241 D.2.5 gdb使用示例 242 D.3 GNU make和makefile 243 D.3.1 GNU make 243 D.3.2 makefile的基本结构 243 D.3.3 makefile的变量 244 D.3.4 GNU make的主要预定义变量 245 D.3.5 GNU make的隐含规则 245 D.3.6 运行make 246

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值