同步机制
在使用共享内存时,必须注意保护共享资源,多线程并发访问共享资源,在内核中也要特别留意。
1. 临界区和竞争条件
临界区(Critical section)就是访问和操作共享数据的代码段。竞争条件(race conditions)指多个执行线程可能同时执行同一个临界区,出现竞争情况。避免并发和防止竞争条件称为同步(Synchronization)。
Hope I can study from bits to qubits.
在使用共享内存时,必须注意保护共享资源,多线程并发访问共享资源,在内核中也要特别留意。
临界区(Critical section)就是访问和操作共享数据的代码段。竞争条件(race conditions)指多个执行线程可能同时执行同一个临界区,出现竞争情况。避免并发和防止竞争条件称为同步(Synchronization)。
因为中断对时限的要求,也不能阻塞,因此整个中断处理流程被分成两部分,上半部分执行对时限要求高的、不能被中断的任务,很多能推迟执行的工作则被放在下半部分。
下半部执行与中断处理密切相关但中断处理程序本身不执行的工作。中断处理程序会异步执行,并且在最好的情况下也会锁定当前的中断线。对上半部和下半部的划分,有一些提示:
内核2.6以后,提供三种下半部实现机制:软中断(softirq)、tasklets、工作队列(work queues)
处理器和外部硬件的速度往往不在一个数量级,如何高效地让处理器和这些外部设备协调工作?
处理器和外部设备协调的方式,从处理器出发,很容易想到的是轮询的方式,但是轮询肯定会做很多无用功;所以应该从外部设备角度,让硬件在需要的时候向内核发出信号,即中断机制。
中断本质上是一种特殊电信号,由硬件设备产生,直接送入中断控制器输入引脚,再发向处理器,处理器接收到中断马上向操作系统反映信号的到来,然后由操作系统执行对应的中断处理程序。
每个中断都有一个中断号,被称为中断请求IRQ。
中断与异常
异常产生需要考虑处理器时钟同步,常常被称为同步中断。这样我们可以把中断分为异步中断和同步中断.
内核中实现了很多通用数据结构,包括Linked list、Queue、Map、Binary tree等
Linux内核中链表随处可见,它将链表节点塞入数据结构中。
链表在linux/types.h中声明,只有两个指针next、prev,是很简单的双向链表,linux/list.h中实现了很多链表操作的宏。
在使用的时候只需要在自己的结构中加上struct list_head list,如
内核常用的一个宏,获得包含member的结构体的指针:
利用这个宏,可以方便地得到包含链表的结构体:
内核提供给用户进程与内核交互的接口
系统调用是用户空间进程与硬件设备间的中间人,它为用户空间提供了硬件的抽象接口;保证系统的稳定和安全;帮助实现多任务和虚拟内存。在Linux中,系统调用是用户空间访问内核的唯一手段(除了异常和trap)
系统调用syscall,通常通过C库中定义的函数调用进行
函数getpid()的一个实现:
编译指令asmlinkage限定词在定义系统调用时使用。
系统调用号
在Linux中,每个系统调用都有一个系统调用号,通过这个号就可以联系系统调用,进程实际上用这个号来指明执行哪个系统调用。
内核会记录一张sys_call_table。
用户空间程序无法直接执行内核代码,因此需要一种方式通知系统,自己需要执行一个系统调用,这种机制靠软中断实现:通过引发一个异常促使系统切换到内核态去执行异常处理程序,此时的异常处理程序实际上就是系统调用处理程序。
绑定一个系统调用的步骤:
在用户空间访问系统调用:
Linux提供一组宏,用于直接访问系统调用,他们会设置好寄存器并调用陷入指令,这些宏是_syscalln(),其中n的范围是0-6。
x86可以用 INT 0x80执行系统调用,这就是一个中断
Linux作为多任务处理系统,同一时刻肯定会有多个任务进程存在于内存,因此内核的调度程序需要决定为哪些进程运行,哪些进程等待,以及决定进程运行的时间(时间片)。调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用CPU(即尽量少的切换进程,进程切换过多,CPU的时间会浪费在切换上);另一方面,保证各个进程能公平的使用CPU(即防止一个进程长时间独占CPU的情况)。
Linux是抢占式的内核调度,进程有优先级的,Linux最出名的调度程序被称为完全公平调度算法——CFS
Linux调度器以模块方式提供,这样可以针对不同进程类型选择合适的调度算法,这种模块化结构被称为[scheduler classes]。
每个调度器都有一个优先级,调度器代码在kernel/sched,调度器策略uapi/linux/sched.h
Linux提供的实时调度策略:
参考资料:
进程是操作系统抽象概念中最基本的一种
进程是程序运行的实例,是操作系统分配资源的基本单元,因此,进程除了可执行代码(代码段,text section)外,还包括其他资源,如打开的文件、信号、数据段、堆栈等,一个进程包含一个多个执行线程(thread of execution),对进程的管理由内核完成。
执行线程,简称线程,线程属于某个进程中的活动对象,线程共享进程的地址空间和数据,但每个线程有自己的程序计数器、线程栈和寄存器,内核调度的对象是线程,而不是进程。
Linux内核不区分线程和进程,而是称task,对应task_struct结构体。
进程提供了2种虚拟机制:虚拟处理器和虚拟内存,每个进程有独立的虚拟处理器和虚拟内存,每个线程有独立的虚拟处理器,线程间可能共享虚拟内存。
获取Linux的源码常见方式:
https://github.com/torvalds/linux
https://www.kernel.org/
https://mirrors.tuna.tsinghua.edu.cn/ 清华大学
https://mirrors.aliyun.com/ 阿里云
在线查看:http://lxr.free-electrons.com/ 没有好的2.6版本的在线查看地址,还是下载源码用Source Insight工具查看方便。
内核源码一般在/usr/src/linux目录
目录 | 说明 |
---|---|
Documentation/ | 文档 |
arch/ | 特点体系结构代码 |
block/ | 块设备I/O |
certs/ | 证书 |
crypto/ | 加密API |
drivers/ | 设备驱动程序 |
firmware/ | 某些设备固件 |
fs/ | VFS和各种文件系统 |
include/ | 内核头文件 |
init/ | 内核引导和初始化 |
ipc/ | 进程间通信代码 |
kernel/ | 核心子系统 |
lib/ | 通用内核函数 |
mm/ | 内存管理子系统和VM |
net/ | 网络子系统 |
samples/ | 示例代码 |
scripts/ | 编译内核所用脚本 |
security/ | Linux安全模块 |
sound/ | 语音子系统 |
tools/ | 开发工具 |
usr/ | 早期用户空间代码(initramfs) |
virt/ | 虚拟化基础结构 |
COPYING | copyright |
CREDITS | 核心开发者列表 |
Kbuild | |
Kconfig | |
MAINTAINERS | 维护者列表 |
Makefile | for make |
- 打补丁: patch -pl < ../patch-x.y.z
“do one thing, do it well.” —— Linus Torvalds
Linux诞生于1991年,由芬兰的大神Linus开发,现在Linux这个词主要指内核。
每个处理器在任何指定的时间点上的活动为下列情况之一:
概念 | 优点 | 缺点 | |
---|---|---|---|
单内核 | 单内核运行在一个单独的地址空间上,可以看做一个单独的整体,通常为一个单独的二进制文件 | 单内核运行在一个单独的地址空间上,可以看做一个单独的整体,通常为一个单独的二进制文件 | 一个服务崩溃导致整个内核无法使用 |
微内核 | 微内核按功能划分为多个独立的过程,每个过程运行在各自的地址空间上,服务之间通过信息传递通信 | 1.安全性提高:只有少数核心功能运行在特权模式下,其他服务运行在用户空间 2. 一个服务崩溃不影响其他服务 | 各服务之间通信比较复杂 |
Linux是单内核,运行在单独的地址空间上,同时吸收微内核有点,支持模块化设计、抢占式内核、内核线程、动态加载内核模块、内核态与用户态等,与传统Unix相比:
linux内核分为:stable和development,举例说明:Linux-2.6.26.1
2:主版本号
6:副版本号
26:修订版本号
1:稳定版本号
副版本号为奇数表示开发版,为偶数表示稳定版
https://www.kernel.org/ 内核源码
http://lxr.free-electrons.com/ 内核源码在线阅读推荐
https://kernelnewbies.org/ 内核开发基础知识
https://lkml.org/ 内核 Mailing List
https://github.com/torvalds/linux github
该文件是是一份内核符号表kernel symbol table,包含了内核中的变量名和函数名地址,在每次编译内核时,自动生成。
相关资料:
GNU Binutils wiki
GNU Binutils
What Are Symbols?
System.map文件格式:地址 类型 符号
类型是小写表示local symbol,大写表示global(external)
重点了解几个类型:
T The symbol is in the text(code) section
D The symbol is in the initialized data section
R The sysbol is in a read only data section
t static
d static
R const
r static const
使用nm命令可以查看更多信息:
nm - list symbols from object files
类型 | 说明 |
---|---|
A | The symbol’s value is absolute, and will not be changed by further linking |
B b | The symbol is in the uninitialized data section(known as BS |
C | The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references. |
D d | The symbol is in the initialized data section. |
G g | The symbol is in an initialized data section for small objects. Some object file formats permit more efficient access to small data objects, such as a global int variable as opposed to a large global array. |
i | For PE format files this indicates that the symbol is in a section specific to the implementation of DLLs. For ELF format files this indicates that the symbol is an indirect function. This is a GNU extension to the standard set of ELF symbol types. It indicates a symbol which if referenced by a relocation does not evaluate to its address, but instead must be invoked at runtime. The runtime execution will then return the value to be used in the relocation. |
N | The symbol is a debugging symbol. |
p | The symbols is in a stack unwind section. |
R r | The symbol is in a read only data section. |
S s | The symbol is in an uninitialized data section for small objects. |
T t | The symbol is in the text (code) section. |
U | The symbol is undefined. |
u | The symbol is a unique global symbol. This is a GNU extension to the standard set of ELF symbol bindings. For such a symbol the dynamic linker will make sure that in the entire process there is just one symbol with this name and type in use. |
V v | The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error. On some systems, uppercase indicates that a default value has been specified. |
W w | The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined in a system-specific manner without error. On some systems, uppercase indicates that a default value has been specified. |
- | The symbol is a stabs symbol in an a.out object file. In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. Stabs symbols are used to hold debugging information. |
? | The symbol type is unknown, or object file format specific. |