continue to revise gitbook links

This commit is contained in:
Dongliang Mu
2024-05-05 10:28:48 +08:00
parent 94d3410447
commit 73bb068aee
7 changed files with 43 additions and 45 deletions

View File

@@ -4,7 +4,7 @@
简介
--------------------------------------------------------------------------------
这是 [linux 内核揭秘](/README.md) 的新一章的第一部分。你可以根据这部分的标题猜测 - 这一部分将涉及 Linux 内核中的 [`控制组`](https://en.wikipedia.org/wiki/Cgroups) 或 `cgroups` 机制。
这是 [linux 内核揭秘](/) 的新一章的第一部分。你可以根据这部分的标题猜测 - 这一部分将涉及 Linux 内核中的 [`控制组`](https://en.wikipedia.org/wiki/Cgroups) 或 `cgroups` 机制。
`Cgroups` 是由 Linux 内核提供的一种机制,它允许我们分配诸如处理器时间、每组进程的数量、每个 `cgroup` 的内存大小,或者针对一个或一组进程的上述资源的组合。`Cgroups` 是按照层级结构组织的,这种机制类似于通常的进程,他们也是层级结构,并且子 `cgroups` 会继承其上级的一些属性。但实际上他们还是有区别的。`cgroups` 和进程之间的主要区别在于,多个不同层级的 `cgroup` 可以同时存在,而进程树则是单一的。同时存在的多个不同层级的 `cgroup` 并不是任意的,因为每个 `cgroup` 层级都要附加到一组 `cgroup` "子系统"中。

View File

@@ -2,7 +2,7 @@
=====================
终结篇
-------------------------
本文是 Linux 内核[中断和中断处理](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/index.html)的第十节。在[上一节](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-9.html),我们了解了延后中断及其相关概念,如 `softirq``tasklet``workqueue`。本节我们继续深入这个主题,现在是见识真正的硬件驱动的时候了。
本文是 Linux 内核[中断和中断处理](/Interrupts/)的第十节。在[上一节](/Interrupts/linux-interrupts-9.md),我们了解了延后中断及其相关概念,如 `softirq``tasklet``workqueue`。本节我们继续深入这个主题,现在是见识真正的硬件驱动的时候了。
以 [StringARM** SA-100/21285 评估板](http://netwinder.osuosl.org/pub/netwinder/docs/intel/datashts/27813501.pdf)串行驱动为例,我们来观察驱动程序如何请求一个 [IRQ](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29) 线,一个中断被触发时会发生什么之类的。驱动程序代码位于 [drivers/tty/serial/21285.c](https://github.com/torvalds/linux/blob/master/drivers/tty/serial/21285.c) 源文件。好啦,源码在手,说走就走!
@@ -257,7 +257,7 @@ if (!action)
return -ENOMEM;
```
欲知 `kzalloc` 详情,请查阅专门介绍 Linux 内核[内存管理](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/index.html)的章节。为 `irqaction` 分配空间后,我们即对这个结构体进行初始化,设置它的中断处理程序,中断标志,设备名称等等:
欲知 `kzalloc` 详情,请查阅专门介绍 Linux 内核[内存管理](/MM/)的章节。为 `irqaction` 分配空间后,我们即对这个结构体进行初始化,设置它的中断处理程序,中断标志,设备名称等等:
```C
action->handler = handler;
@@ -297,7 +297,7 @@ if (new->thread_fn && !nested) {
准备处理中断
------------------------------
通过上文,我们观察了为给定的中断描述符请求中断号,为给定的中断注册 `irqaction` 结构体的过程。我们已经知道,当一个中断事件发生时,中断控制器向处理器通知该事件,处理器尝试为这个中断找到一个合适的中断门。如果你已阅读本章[第八节](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-8.html),你应该还记得 `native_init_IRQ` 函数。这个函数会初始化本地 [APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)。这个函数的如下部分是我们现在最感兴趣的地方:
通过上文,我们观察了为给定的中断描述符请求中断号,为给定的中断注册 `irqaction` 结构体的过程。我们已经知道,当一个中断事件发生时,中断控制器向处理器通知该事件,处理器尝试为这个中断找到一个合适的中断门。如果你已阅读本章[第八节](/Interrupts/linux-interrupts-8.md),你应该还记得 `native_init_IRQ` 函数。这个函数会初始化本地 [APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)。这个函数的如下部分是我们现在最感兴趣的地方:
```C
for_each_clear_bit_from(i, used_vectors, first_system_vector) {
@@ -342,7 +342,7 @@ common_interrupt:
interrupt do_IRQ
```
`interrupt` 宏定义在同一个源文件中。它把[通用](https://en.wikipedia.org/wiki/Processor_register)寄存器的值保存在栈中。如果需要,它还会通过 `SWAPGS` 汇编指令在内核中改变用户空间 `gs` 寄存器。它会增加 [per-cpu](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html) 的 `irq_count` 变量,来表明我们处于中断状态,然后调用 `do_IRQ` 函数。该函数定义于 [arch/x86/kernel/irq.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/irq.c) 源文件中,作用是处理我们的设备中断。让我们一起考察这个函数。`do_IRQ` 函数接受一个参数 - `pt_regs` 结构体,它存放着用户空间寄存器的值:
`interrupt` 宏定义在同一个源文件中。它把[通用](https://en.wikipedia.org/wiki/Processor_register)寄存器的值保存在栈中。如果需要,它还会通过 `SWAPGS` 汇编指令在内核中改变用户空间 `gs` 寄存器。它会增加 [per-cpu](/Concepts/linux-cpu-1.md) 的 `irq_count` 变量,来表明我们处于中断状态,然后调用 `do_IRQ` 函数。该函数定义于 [arch/x86/kernel/irq.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/irq.c) 源文件中,作用是处理我们的设备中断。让我们一起考察这个函数。`do_IRQ` 函数接受一个参数 - `pt_regs` 结构体,它存放着用户空间寄存器的值:
```C
__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
@@ -409,7 +409,7 @@ return 1;
退出中断
---------------------
好了,中断处理程序执行完毕,我们必须从中断中返回。在 `do_IRQ` 函数将工作处理完毕后,我们将回到 [arch/x86/entry/entry_64.S](https://github.com/torvalds/linux/blob/master/arch/x86/entry_entry_64.S) 汇编代码的 `ret_from_intr` 标签处。首先,我们通过 `DISABLE_INTERRUPTS` 宏禁止中断,这个宏被扩展成 `cli` 指令,将 [per-cpu](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html) 的 `irq_count` 变量值减 1。记住当我们处于中断上下文的时候这个变量的值是 `1`
好了,中断处理程序执行完毕,我们必须从中断中返回。在 `do_IRQ` 函数将工作处理完毕后,我们将回到 [arch/x86/entry/entry_64.S](https://github.com/torvalds/linux/blob/master/arch/x86/entry_entry_64.S) 汇编代码的 `ret_from_intr` 标签处。首先,我们通过 `DISABLE_INTERRUPTS` 宏禁止中断,这个宏被扩展成 `cli` 指令,将 [per-cpu](/Concepts/linux-cpu-1.md) 的 `irq_count` 变量值减 1。记住当我们处于中断上下文的时候这个变量的值是 `1`
```assembly
DISABLE_INTERRUPTS(CLBR_NONE)
@@ -444,12 +444,11 @@ native_irq_return_iret:
总结
--------------------------
这里是[中断和中断处理](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/index.html) 章节的第十节的结尾。如你在本节开头读到的那样,这是本章的最后一节。本章开篇阐述了中断理论,我们于是明白了什么是中断,中断的类型,然后也了解了异常以及对这种类型中断的处理,延后中断。最后在本节,我们考察了硬件中断和对这些中断的处理。当然,本节甚至本章都未能覆盖到 Linux 内核中断和中断处理的所有方面。这样并不现实,至少对我而言如此。这是一项浩大工程,不知你作何感想,对我来说,它确实浩大。这个主题远远超出本章讲述的内容,我不确定地球上能否找到一本书可以涵盖这个主题。我们漏掉了关于中断和中断处理的很多内容,但我相信,深入研究中断和中断处理相关的内核源码是个不错的点子。
这里是[中断和中断处理](/Interrupts/) 章节的第十节的结尾。如你在本节开头读到的那样,这是本章的最后一节。本章开篇阐述了中断理论,我们于是明白了什么是中断,中断的类型,然后也了解了异常以及对这种类型中断的处理,延后中断。最后在本节,我们考察了硬件中断和对这些中断的处理。当然,本节甚至本章都未能覆盖到 Linux 内核中断和中断处理的所有方面。这样并不现实,至少对我而言如此。这是一项浩大工程,不知你作何感想,对我来说,它确实浩大。这个主题远远超出本章讲述的内容,我不确定地球上能否找到一本书可以涵盖这个主题。我们漏掉了关于中断和中断处理的很多内容,但我相信,深入研究中断和中断处理相关的内核源码是个不错的点子。
如果有任何疑问或者建议,撰写评论或者在 [twitter](https://twitter.com/0xAX) 上联系我。
**请注意,英语并非我的母语。任何不便之处,我深感抱歉。如果发现任何错误,请在 [linux-insides](https://github.com/0xAX/linux-insides) 向我发送 PR。(译者注:翻译问题请发送 PR 到 [linux-insides-cn](https://www.gitbook.com/book/xinqiu/linux-insides-cn))**
**请注意,英语并非我的母语。任何不便之处,我深感抱歉。如果发现任何错误,请在 [linux-insides](https://github.com/0xAX/linux-insides) 向我发送 PR。(译者注:翻译问题请发送 PR 到 [linux-insides-zh](https://github.com/hust-open-atom-club/linux-insides-zh))**
链接
---------------------------------
@@ -460,16 +459,16 @@ native_irq_return_iret:
* [initcall](http://kernelnewbies.org/Documents/InitcallMechanism)
* [uart](https://en.wikipedia.org/wiki/Universal_asynchronous_receiver/transmitter)
* [ISA](https://en.wikipedia.org/wiki/Industry_Standard_Architecture)
* [内存管理](https://xinqiu.gitbooks.io/linux-insides-cn/content/MM/index.html)
* [内存管理](/MM/)
* [i2c](https://en.wikipedia.org/wiki/I%C2%B2C)
* [APIC](https://en.wikipedia.org/wiki/Advanced_Programmable_Interrupt_Controller)
* [GNU 汇编器](https://en.wikipedia.org/wiki/GNU_Assembler)
* [处理器寄存器](https://en.wikipedia.org/wiki/Processor_register)
* [per-cpu](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html)
* [per-cpu](/Concepts/linux-cpu-1.md)
* [pid](https://en.wikipedia.org/wiki/Process_identifier)
* [设备树](https://en.wikipedia.org/wiki/Device_tree)
* [系统调用](https://en.wikipedia.org/wiki/System_call)
* [上一节](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-9.html)
* [上一节](/Interrupts/linux-interrupts-9.md)

View File

@@ -4,7 +4,7 @@
延后中断(软中断Tasklets 和工作队列)介绍
--------------------------------------------------------------------------------
这是 Linux 内核[中断和中断处理](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/index.html)的第九节,在[上一节](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-8.html)我们分析了源文件 [arch/x86/kernel/irqinit.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/irqinit.c) 中的 `init_IRQ` 实现。接下来的这一节我们将继续深入学习外部硬件中断的初始化。
这是 Linux 内核[中断和中断处理](/Interrupts/)的第九节,在[上一节](/Interrupts/linux-interrupts-8.md)我们分析了源文件 [arch/x86/kernel/irqinit.c](https://github.com/torvalds/linux/blob/master/arch/x86/kernel/irqinit.c) 中的 `init_IRQ` 实现。接下来的这一节我们将继续深入学习外部硬件中断的初始化。
中断处理会有一些特点,其中最主要的两个是:
@@ -144,7 +144,7 @@ void raise_softirq(unsigned int nr)
__raise_softirq_irqoff(nr);
```
然后,通过 `in_interrupt` 函数获得 `irq_count` 值。我们在这一章的第一[小节](http://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-1.html)已经知道它是用来检测一个 cpu 是否处于中断环境。如果我们处于中断上下文中,我们就退出 `raise_softirq_irqoff` 函数,装回 `IF` 标志位并允许当前处理器的中断。如果不在中断上下文中,就会调用 `wakeup_softirqd` 函数:
然后,通过 `in_interrupt` 函数获得 `irq_count` 值。我们在这一章的第一[小节](/Interrupts/linux-interrupts-1.md)已经知道它是用来检测一个 cpu 是否处于中断环境。如果我们处于中断上下文中,我们就退出 `raise_softirq_irqoff` 函数,装回 `IF` 标志位并允许当前处理器的中断。如果不在中断上下文中,就会调用 `wakeup_softirqd` 函数:
```C
if (!in_interrupt())
@@ -226,7 +226,7 @@ void __init softirq_init(void)
}
```
可以看到在函数开头定义了一个名为 cpu 的 integer 类型变量。接下来他会作为参数传递给宏 `for_each_possible_cpu` 来获得系统中所有的处理器。如果 `possible_cpu` 对你来说是一个新的术语,你可以阅读 [CPU masks](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-2.html) 章节来了解更多知识。简单的说,`possible_cpu` 是系统运行期间插入的处理器集合。所有的 `possible processor` 存储在 `cpu_possible_bits` 位图中,你可以在 [kernel/cpu.c](https://github.com/torvalds/linux/blob/master/kernel/cpu.c) 中找到他的定义:
可以看到在函数开头定义了一个名为 cpu 的 integer 类型变量。接下来他会作为参数传递给宏 `for_each_possible_cpu` 来获得系统中所有的处理器。如果 `possible_cpu` 对你来说是一个新的术语,你可以阅读 [CPU masks](/Concepts/linux-cpu-2.md) 章节来了解更多知识。简单的说,`possible_cpu` 是系统运行期间插入的处理器集合。所有的 `possible processor` 存储在 `cpu_possible_bits` 位图中,你可以在 [kernel/cpu.c](https://github.com/torvalds/linux/blob/master/kernel/cpu.c) 中找到他的定义:
```C
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
@@ -363,7 +363,7 @@ static void tasklet_action(struct softirq_action *a)
}
```
`tasklet_action` 开始时利用 `local_irq_disable` 宏禁用了当前处理器的中断(你可以阅读本书[第二部分](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-2.html)了解更多关于此宏的信息)。接下来获取到当前处理器对应的普通优先级 tasklet 列表并把它设置为 `NULL` ,这是因为所有的 tasklet 都将被执行。然后使能当前处理器的中断,循环遍历 tasklet 列表,每一次遍历都会对当前 tasklet 调用 `tasklet_trylock` 函数来更新它的状态为 `TASKLET_STATE_RUN`
`tasklet_action` 开始时利用 `local_irq_disable` 宏禁用了当前处理器的中断(你可以阅读本书[第二部分](/Interrupts/linux-interrupts-2.md)了解更多关于此宏的信息)。接下来获取到当前处理器对应的普通优先级 tasklet 列表并把它设置为 `NULL` ,这是因为所有的 tasklet 都将被执行。然后使能当前处理器的中断,循环遍历 tasklet 列表,每一次遍历都会对当前 tasklet 调用 `tasklet_trylock` 函数来更新它的状态为 `TASKLET_STATE_RUN`
```C
static inline int tasklet_trylock(struct tasklet_struct *t)
@@ -477,7 +477,7 @@ bool queue_work_on(int cpu, struct workqueue_struct *wq,
}
```
`__queue_work` 函数得到参数 `work poll`。是的,是 `work poll` 而不是 `workqueue`。实际上,所有的 `works` 都没有放在 `workqueue` 中,而是放在 Linux 内核中由 `worker_pool` 数据结构所定义的 `work poll`。如上所述,`workqueue_struct` 数据结构的 `pwqs` 成员是一个 `worker_pool` 列表。当我们创建一个 `workqueue`,他针对每一个处理器都创建了 `worker_pool`。每一个和 `worker_pool` 相关联的 `pool_workqueue` 都分配在相同的处理器上对应的优先级队列,`workqueue` 通过他们和 `worker_pool` 交互。在 `__queue_work` 函数里使用 `raw_smp_processor_id` 设置 cpu 为当前处理器在[第四章](https://xinqiu.gitbooks.io/linux-insides-cn/content/Initialization/linux-initialization-4.html)你可以找到更多相关信息),得到与所给 `work_struct` 对应的 `pool_workqueue` 并将 `work` 插入到 `workqueue`
`__queue_work` 函数得到参数 `work poll`。是的,是 `work poll` 而不是 `workqueue`。实际上,所有的 `works` 都没有放在 `workqueue` 中,而是放在 Linux 内核中由 `worker_pool` 数据结构所定义的 `work poll`。如上所述,`workqueue_struct` 数据结构的 `pwqs` 成员是一个 `worker_pool` 列表。当我们创建一个 `workqueue`,他针对每一个处理器都创建了 `worker_pool`。每一个和 `worker_pool` 相关联的 `pool_workqueue` 都分配在相同的处理器上对应的优先级队列,`workqueue` 通过他们和 `worker_pool` 交互。在 `__queue_work` 函数里使用 `raw_smp_processor_id` 设置 cpu 为当前处理器在[第四章](/Initialization/linux-initialization-4.md)你可以找到更多相关信息),得到与所给 `work_struct` 对应的 `pool_workqueue` 并将 `work` 插入到 `workqueue`
```C
static void __queue_work(int cpu, struct workqueue_struct *wq,
@@ -505,13 +505,13 @@ insert_work(pwq, work, worklist, work_flags);
总结
--------------------------------------------------------------------------------
现在结束了[中断和中断处理](https://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/index.html)的第九节。这一节中我们继续讨论了外部硬件中断。在之前部分我们看到了 `IRQs` 的初始化和 `irq_desc` 数据结构,在这一节我们看到了用于延后函数的三个概念:`软中断``tasklet``工作队列`
现在结束了[中断和中断处理](/Interrupts/)的第九节。这一节中我们继续讨论了外部硬件中断。在之前部分我们看到了 `IRQs` 的初始化和 `irq_desc` 数据结构,在这一节我们看到了用于延后函数的三个概念:`软中断``tasklet``工作队列`
下一节将是 `中断和中断处理` 的最后一节。我们将会了解真正的硬件驱动,并试着学习它是怎样和中断子系统一起工作的。
如果你有任何问题或建议,请给我发评论或者给我发 [Twitter](https://twitter.com/0xAX)。
**请注意英语并不是我的母语,我为任何表达不清楚的地方感到抱歉。如果你发现任何错误请发 PR 到 [linux-insides](https://github.com/hust-open-atom-club/linux-insides-zh)。(译者注:翻译问题请发 PR 到 [linux-insides-cn](https://www.gitbook.com/book/xinqiu/linux-insides-cn))**
**请注意英语并不是我的母语,我为任何表达不清楚的地方感到抱歉。如果你发现任何错误请发 PR 到 [linux-insides](https://github.com/MintCN/linux-insides-zh)。(译者注:翻译问题请发 PR 到 [linux-insides-cn](https://github.com/hust-open-atom-club/linux-insides-zh))**
链接
@@ -520,7 +520,7 @@ insert_work(pwq, work, worklist, work_flags);
* [initcall](http://www.compsoc.man.ac.uk/~moz/kernelnewbies/documents/initcall/index.html)
* [IF](https://en.wikipedia.org/wiki/Interrupt_flag)
* [eflags](https://en.wikipedia.org/wiki/FLAGS_register)
* [CPU masks](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-2.html)
* [per-cpu](https://xinqiu.gitbooks.io/linux-insides-cn/content/Concepts/linux-cpu-1.html)
* [CPU masks](/Concepts/linux-cpu-2.md)
* [per-cpu](/Concepts/linux-cpu-1.md)
* [Workqueue](https://github.com/torvalds/linux/blob/master/Documentation/workqueue.txt)
* [Previous part](http://xinqiu.gitbooks.io/linux-insides-cn/content/Interrupts/linux-interrupts-8.html)
* [Previous part](/Interrupts/linux-interrupts-8.md)

View File

@@ -4,17 +4,16 @@
引言
--------------------------------------------------------------------------------
这是本章的第四部分 [chapter](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/index.html) ,本章描述了内核中的同步原语,且在之前的部分我们介绍完了 [自旋锁](https://en.wikipedia.org/wiki/Spinlock) 和 [信号量](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) 两种不同的同步原语。我们将在这个章节持续学习 [同步原语](https://en.wikipedia.org/wiki/Synchronization_%28computer_science%29),并考虑另一简称 [互斥锁 (mutex)](https://en.wikipedia.org/wiki/Mutual_exclusion) 的同步原语,全名 `MUTual EXclusion`
这是本章的第四部分 [chapter](/SyncPrim/index.html) ,本章描述了内核中的同步原语,且在之前的部分我们介绍完了 [自旋锁](https://en.wikipedia.org/wiki/Spinlock) 和 [信号量](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) 两种不同的同步原语。我们将在这个章节持续学习 [同步原语](https://en.wikipedia.org/wiki/Synchronization_%28computer_science%29),并考虑另一简称 [互斥锁 (mutex)](https://en.wikipedia.org/wiki/Mutual_exclusion) 的同步原语,全名 `MUTual EXclusion`
与 [本书](https://xinqiu.gitbooks.io/linux-insides-cn/content) 前面所有章节一样我们将先尝试从理论面来探究此同步原语在此基础上再探究Linux内核所提供用来操作 `互斥锁` 的 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。
与 [本书](/) 前面所有章节一样我们将先尝试从理论面来探究此同步原语在此基础上再探究Linux内核所提供用来操作 `互斥锁` 的 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。
让我们开始吧。
`互斥锁` 的概念
--------------------------------------------------------------------------------
从前一 [部分](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/linux-sync-3.html),我们已经很熟悉同步原语 [信号量](https://en.wikipedia.org/wiki/Semaphore_%28programming%29)。其表示为:
从前一 [部分](/SyncPrim/linux-sync-3.html),我们已经很熟悉同步原语 [信号量](https://en.wikipedia.org/wiki/Semaphore_%28programming%29)。其表示为:
```C
struct semaphore {
@@ -78,7 +77,7 @@ struct mutex_waiter {
};
```
此结构定义在[include/linux/mutex.h](https://github.com/torvalds/linux/blob/master/include/linux/mutex.h) 头文件且使用上可能会被睡眠。在研究Linux内核提供用来操作 `互斥锁` 的 [API](https://en.wikipedia.org/wiki/Application_programming_interface) 前,让我们先研究 `mutex_waiter` 结构。如果你有阅读此章节的 [前一部分](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/linux-sync-3.html),你可以能会注意到 `mutex_waiter` 结构与 [kernel/locking/semaphore.c](https://github.com/torvalds/linux/blob/master/kernel/locking/semaphore.c) 源代码中的 `semaphore_waiter` 结构相似:
此结构定义在[include/linux/mutex.h](https://github.com/torvalds/linux/blob/master/include/linux/mutex.h) 头文件且使用上可能会被睡眠。在研究Linux内核提供用来操作 `互斥锁` 的 [API](https://en.wikipedia.org/wiki/Application_programming_interface) 前,让我们先研究 `mutex_waiter` 结构。如果你有阅读此章节的 [前一部分](/SyncPrim/linux-sync-3.md),你可以能会注意到 `mutex_waiter` 结构与 [kernel/locking/semaphore.c](https://github.com/torvalds/linux/blob/master/kernel/locking/semaphore.c) 源代码中的 `semaphore_waiter` 结构相似:
```C
struct semaphore_waiter {
@@ -115,7 +114,7 @@ struct semaphore_waiter {
}
```
这个宏被定义在 [相同的](https://github.com/torvalds/linux/blob/master/include/linux/mutex.h) 头文件,并且我们可以了解到它初始化了 `mutex` 解构体中的字段。`count` 被初始化为 `1` ,这代表该互斥锁状态为 `无锁``wait_lock` [自旋锁](https://en.wikipedia.org/wiki/Spinlock) 被初始化为无锁状态,而最后的栏位 `wait_list` 被初始化为空的 [双向列表](https://xinqiu.gitbooks.io/linux-insides-cn/content/DataStructures/linux-datastructures-1.html)。
这个宏被定义在 [相同的](https://github.com/torvalds/linux/blob/master/include/linux/mutex.h) 头文件,并且我们可以了解到它初始化了 `mutex` 解构体中的字段。`count` 被初始化为 `1` ,这代表该互斥锁状态为 `无锁``wait_lock` [自旋锁](https://en.wikipedia.org/wiki/Spinlock) 被初始化为无锁状态,而最后的栏位 `wait_list` 被初始化为空的 [双向列表](/DataStructures/linux-datastructures-1.md)。
第二种做法允许我们动态初始化一个 `互斥锁`。为此我们需要调用在 [kernel/locking/mutex.c](https://github.com/torvalds/linux/blob/master/kernel/locking/mutex.c) 源码文件的 `__mutex_init` 函数。事实上,大家很少直接调用 `__mutex_init` 函数。取而代之,我们使用下面的 `mutex_init` :
@@ -160,7 +159,7 @@ static inline bool osq_is_locked(struct optimistic_spin_queue *lock)
}
```
`__mutex_init` 函数的结尾阶段,我们可以看到它调用了 `debug_mutex_init` 函数,不过就如同我在此 [章节](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/) 前面提到的那样,我们并不会在此章节讨论调试相关的内容。
`__mutex_init` 函数的结尾阶段,我们可以看到它调用了 `debug_mutex_init` 函数,不过就如同我在此 [章节](/SyncPrim/) 前面提到的那样,我们并不会在此章节讨论调试相关的内容。
`互斥锁` 结构被初始化后,我们可以继续研究 `互斥锁` 同步原语的 `上锁``解锁` API。`mutex_lock``mutex_unlock` 函数的实现位于 [kernel/locking/mutex.c](https://github.com/torvalds/linux/blob/master/kernel/locking/mutex.c) 源码文件。首先,让我们从 `mutex_lock` 的实现开始吧。代码如下:
@@ -177,7 +176,7 @@ void __sched mutex_lock(struct mutex *lock)
`might_sleep` 宏之后,我们可以看到 `__mutex_fastpath_lock` 函数被调用。此函数是体系结构相关的,并且因为我们此书探讨的是 [x86_64](https://en.wikipedia.org/wiki/X86-64) 体系结构, \ `__mutex_fastpath_lock` 的实现部分位于 [arch/x86/include/asm/mutex_64.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/mutex_64.h) 头文件。正如我们从`__mutex_fastpath_lock` 函数名称可以理解的那样,此函数将尝试通过 fast path 试图获取一个锁,或者换句话说此函数试图递减一个给定互斥锁的 `count` 字段。
`__mutex_fastpath_lock` 函数的实现由两部分组成。第一部分是 [内联汇编](https://xinqiu.gitbooks.io/linux-insides-cn/content/Theory/linux-theory-3.html) 。让我们看看:
`__mutex_fastpath_lock` 函数的实现由两部分组成。第一部分是 [内联汇编](/Theory/linux-theory-3.md) 。让我们看看:
```C
asm_volatile_goto(LOCK_PREFIX " decl %0\n"
@@ -403,7 +402,7 @@ if (!list_empty(&lock->wait_list)) {
* `mutex_lock_killable`;
* `mutex_trylock`.
以及对应相同前缀的 `unlock` 函数. 我们就不在此解释这些 `API` 了, 因为他们跟 `信号量` 所提供的 `API` 类似。你可以通过阅读 [前一部分](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/linux-sync-3.html) 来了解更多。
以及对应相同前缀的 `unlock` 函数. 我们就不在此解释这些 `API` 了, 因为他们跟 `信号量` 所提供的 `API` 类似。你可以通过阅读 [前一部分](/SyncPrim/linux-sync-3.md) 来了解更多。
总结
--------------------------------------------------------------------------------
@@ -427,12 +426,12 @@ if (!list_empty(&lock->wait_list)) {
* [lock validator](https://www.kernel.org/doc/Documentation/locking/lockdep-design.txt)
* [Atomic](https://en.wikipedia.org/wiki/Linearizability)
* [MCS lock](http://www.cs.rochester.edu/~scott/papers/1991_TOCS_synch.pdf)
* [Doubly linked list](https://xinqiu.gitbooks.io/linux-insides-cn/content/DataStructures/linux-datastructures-1.html)
* [Doubly linked list](/DataStructures/linux-datastructures-1.md)
* [x86_64](https://en.wikipedia.org/wiki/X86-64)
* [Inline assembly](https://xinqiu.gitbooks.io/linux-insides-cn/content/Theory/linux-theory-3.html)
* [Inline assembly](/Theory/linux-theory-3.md)
* [Memory barrier](https://en.wikipedia.org/wiki/Memory_barrier)
* [Lock instruction](http://x86.renejeschke.de/html/file_module_x86_id_159.html)
* [JNS instruction](http://unixwiz.net/techtips/x86-jumps.html)
* [preemption](https://en.wikipedia.org/wiki/Preemption_%28computing%29)
* [Unix signals](https://en.wikipedia.org/wiki/Unix_signal)
* [Previous part](https://xinqiu.gitbooks.io/linux-insides-cn/content/SyncPrim/linux-sync-3.html)
* [Previous part](/SyncPrim/linux-sync-3.md)

View File

@@ -4,9 +4,9 @@ Synchronization primitives in the Linux kernel. Part 5.
Introduction
--------------------------------------------------------------------------------
This is the fifth part of the [chapter](https://0xax.gitbooks.io/linux-insides/content/SyncPrim/index.html) which describes synchronization primitives in the Linux kernel and in the previous parts we finished to consider different types [spinlocks](https://en.wikipedia.org/wiki/Spinlock), [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) and [mutex](https://en.wikipedia.org/wiki/Mutual_exclusion) synchronization primitives. We will continue to learn [synchronization primitives](https://en.wikipedia.org/wiki/Synchronization_%28computer_science%29) in this part and start to consider special type of synchronization primitives - [readerswriter lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock).
This is the fifth part of the [chapter](/SyncPrim/) which describes synchronization primitives in the Linux kernel and in the previous parts we finished to consider different types [spinlocks](https://en.wikipedia.org/wiki/Spinlock), [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) and [mutex](https://en.wikipedia.org/wiki/Mutual_exclusion) synchronization primitives. We will continue to learn [synchronization primitives](https://en.wikipedia.org/wiki/Synchronization_%28computer_science%29) in this part and start to consider special type of synchronization primitives - [readerswriter lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock).
The first synchronization primitive of this type will be already familiar for us - [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29). As in all previous parts of this [book](https://0xax.gitbooks.io/linux-insides/content), before we will consider implementation of the `reader/writer semaphores` in the Linux kernel, we will start from the theoretical side and will try to understand what is the difference between `reader/writer semaphores` and `normal semaphores`.
The first synchronization primitive of this type will be already familiar for us - [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29). As in all previous parts of this [book](/), before we will consider implementation of the `reader/writer semaphores` in the Linux kernel, we will start from the theoretical side and will try to understand what is the difference between `reader/writer semaphores` and `normal semaphores`.
So, let's start.
@@ -15,7 +15,7 @@ Reader/Writer semaphore
Actually there are two types of operations may be performed on the data. We may read data and make changes in data. Two fundamental operations - `read` and `write`. Usually (but not always), `read` operation is performed more often than `write` operation. In this case, it would be logical to we may lock data in such way, that some processes may read locked data in one time, on condition that no one will not change the data. The [readers/writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) allows us to get this lock.
When a process which wants to write something into data, all other `writer` and `reader` processes will be blocked until the process which acquired a lock, will not release it. When a process reads data, other processes which want to read the same data too, will not be locked and will be able to do this. As you may guess, implementation of the `reader/writer semaphore` is based on the implementation of the `normal semaphore`. We already familiar with the [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) synchronization primitive from the third [part]((https://0xax.gitbooks.io/linux-insides/content/SyncPrim/sync-4.html) of this chapter. From the theoretical side everything looks pretty simple. Let's look how `reader/writer semaphore` is represented in the Linux kernel.
When a process which wants to write something into data, all other `writer` and `reader` processes will be blocked until the process which acquired a lock, will not release it. When a process reads data, other processes which want to read the same data too, will not be locked and will be able to do this. As you may guess, implementation of the `reader/writer semaphore` is based on the implementation of the `normal semaphore`. We already familiar with the [semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) synchronization primitive from the third [part](/SyncPrim/linux-sync-3.md) of this chapter. From the theoretical side everything looks pretty simple. Let's look how `reader/writer semaphore` is represented in the Linux kernel.
The `semaphore` is represented by the:
@@ -422,12 +422,12 @@ Links
* [Semaphore](https://en.wikipedia.org/wiki/Semaphore_%28programming%29)
* [Mutex](https://en.wikipedia.org/wiki/Mutual_exclusion)
* [x86_64 architecture](https://en.wikipedia.org/wiki/X86-64)
* [Doubly linked list](https://0xax.gitbooks.io/linux-insides/content/DataStructures/dlist.html)
* [Doubly linked list](/DataStructures/linux-datastructures-1.md)
* [MCS lock](http://www.cs.rochester.edu/~scott/papers/1991_TOCS_synch.pdf)
* [API](https://en.wikipedia.org/wiki/Application_programming_interface)
* [Linux kernel lock validator](https://www.kernel.org/doc/Documentation/locking/lockdep-design.txt)
* [Atomic operations](https://en.wikipedia.org/wiki/Linearizability)
* [Inline assembly](https://0xax.gitbooks.io/linux-insides/content/Theory/asm.html)
* [Inline assembly](/Theory/linux-theory-3.md)
* [XADD instruction](http://x86.renejeschke.de/html/file_module_x86_id_327.html)
* [LOCK instruction](http://x86.renejeschke.de/html/file_module_x86_id_159.html)
* [Previous part](/SyncPrim/linux-sync-4.md)

View File

@@ -4,7 +4,7 @@ Linux内核中的时钟和时间管理 第6部分
x86_64有关的时钟资源
--------------------------------------------------------------------------------
这是[本章](https://xinqiu.gitbooks.io/linux-insides-cn/content/Timers/index.html)的第六部分将描述Linux内核中的时钟和时间管理的相关内容。上一节中我们了解了clockevents框架现在继续深入研究Linux内核中的时间管理相关内容本节将讲述x86架构中时钟源的实现更多关于时钟源的概念可以参考本章第二节
这是[本章](/Timers/)的第六部分将描述Linux内核中的时钟和时间管理的相关内容。上一节中我们了解了clockevents框架现在继续深入研究Linux内核中的时间管理相关内容本节将讲述x86架构中时钟源的实现更多关于时钟源的概念可以参考本章第二节
首先我们需要知道x86架构上可以使用哪些时钟源。这个问题很容易从[sysfs](https://en.wikipedia.org/wiki/Sysfs)或文件`/sys/devices/system/clocksource/clocksource0/available_clocksource`中获得答案。文件夹`/sys/devices/system/clocksource/clocksourceN`内有两个特殊文件保存:
@@ -30,7 +30,7 @@ tsc hpet acpi_pm
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
```
作者的系统中是[Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter)。由[本章第二节内容](https://xinqiu.gitbooks.io/linux-insides-cn/content/Timers/linux-timers-2.html)可知,系统中最好的时钟源是具有最佳(最高)等级的时钟源,或者说是具有最高频率的时钟源。
作者的系统中是[Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter)。由[本章第二节内容](/Timers/linux-timers-2.md)可知,系统中最好的时钟源是具有最佳(最高)等级的时钟源,或者说是具有最高频率的时钟源。
[ACPI](https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface)电源管理时钟的频率是3.579545MHz。而[High Precision Event Timer(高精度事件定时器)](https://en.wikipedia.org/wiki/High_Precision_Event_Timer)的频率至少是10MHz而[Time Stamp Counter(时间戳计数器)](https://en.wikipedia.org/wiki/Time_Stamp_Counter)的频率取决于处理器。例如在较早的处理器上,`TSC`用来计算处理器内部的时钟周期,就是说当处理器的频率比生变化时,其频率也会发生变化。这种现象在较新的处理器上有所改善。新的处理器有一个不变的时间戳计数器,无论处理器在什么状态下都会以恒定的速率递增。我们可以在`/proc/cpuinfo`的输出中获得它的频率。例如:
@@ -368,8 +368,8 @@ static struct clocksource clocksource_tsc = {
Conclusion
--------------------------------------------------------------------------------
这是[本章](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html)的第六节描述了Linux内核中的时钟和时钟管理。上一节中熟悉了`clockevents`框架。这一节中继续学习了Linux内核中时钟管理并且看到了在[x86](https://en.wikipedia.org/wiki/X86)架构中使用的三种不同的时钟源。下一节将是[本章](https://0xax.gitbooks.io/linux-insides/content/Timers/index.html)的最后一节,将看到一些与用户空间有关的事情,即一些与时间有关的[系统调用](https://en.wikipedia.org/wiki/System_call)如何在Linux内核中实现。
如果有问题或建议请随时在twitter[0xAX](https://twitter.com/0xAX)上与我联系,给我发[email](https://0xax.gitbooks.io/linux-insides/content/Timers/anotherworldofworld@gmail.com)或直接创建[issue](https://github.com/0xAX/linux-insides/issues/new)。
这是[本章](/Timers/)的第六节描述了Linux内核中的时钟和时钟管理。上一节中熟悉了`clockevents`框架。这一节中继续学习了Linux内核中时钟管理并且看到了在[x86](https://en.wikipedia.org/wiki/X86)架构中使用的三种不同的时钟源。下一节将是[本章](/Timers/)的最后一节,将看到一些与用户空间有关的事情,即一些与时间有关的[系统调用](https://en.wikipedia.org/wiki/System_call)如何在Linux内核中实现。
如果有问题或建议请随时在twitter[0xAX](https://twitter.com/0xAX)上与我联系,给我发[email](anotherworldofworld@gmail.com)或直接创建[issue](https://github.com/0xAX/linux-insides/issues/new)。
**请注意英语不是我的第一语言我真的很抱歉给你带来的不便。如果你发现任何错误请给我发送PR到[linux-insides](https://github.com/0xAX/linux-insides)**
链接
@@ -388,4 +388,4 @@ Conclusion
* [IRQ0](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29#Master_PIC)
* [i8259](https://en.wikipedia.org/wiki/Intel_8259)
* [initcall](http://www.compsoc.man.ac.uk/~moz/kernelnewbies/documents/initcall/kernel.html)
* [previous part](https://xinqiu.gitbooks.io/linux-insides-cn/content/Timers/linux-timers-5.html)
* [previous part](/Timers/linux-timers-5.md)

View File

@@ -4,7 +4,7 @@ Timers and time management in the Linux kernel. Part 7.
Time related system calls in the Linux kernel
--------------------------------------------------------------------------------
This is the seventh and last part [chapter](https://xinqiu.gitbooks.io/linux-insides-cn/content/Timers/index.html) which describes timers and time management related stuff in the Linux kernel. In the previous [part](https://xinqiu.gitbooks.io/linux-insides-cn/content/Timers/linux-timers-6.html) we saw some [x86_64](https://en.wikipedia.org/wiki/X86-64) like [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer) and [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter). Internal time management is interesting part of the Linux kernel, but of course not only the kernel needs in the `time` concept. Our programs need to know time too. In this part, we will consider implementation of some time management related [system calls](https://en.wikipedia.org/wiki/System_call). These system calls are:
This is the seventh and last part [chapter](/Timers/) which describes timers and time management related stuff in the Linux kernel. In the previous [part](/Timers/linux-timers-6.md) we saw some [x86_64](https://en.wikipedia.org/wiki/X86-64) like [High Precision Event Timer](https://en.wikipedia.org/wiki/High_Precision_Event_Timer) and [Time Stamp Counter](https://en.wikipedia.org/wiki/Time_Stamp_Counter). Internal time management is interesting part of the Linux kernel, but of course not only the kernel needs in the `time` concept. Our programs need to know time too. In this part, we will consider implementation of some time management related [system calls](https://en.wikipedia.org/wiki/System_call). These system calls are:
* `clock_gettime`;
* `gettimeofday`;