你可以使用mod_timer()修改my_timer的到期时间,使用del_timer()取消定时器,或使用timer_pending()以查看my_timer当前是否处于pending状态。查看kernel/timer.c源代码,你会发现schedule_timeout()内部就使用了这些API。
用户空间的
clock_settime()
和
clock_gettime()
函数可用于获得内核定时器服务。用户应用程序可以使用
setitimer()
和
getitimer()
来控制一个
alarm
信号在特定的超时后发生。
短延时
在内核中,小于
jiffy
的延时被认为是短延时。这种延时在进程或中断上下文都可能发生。由于不可能使用基于
jiffy
的方法实现短延时,之前讨论的睡眠等待将不再能用于小的超时。这种情况下,唯一的解决途径就是忙等待。
实现短延时的内核
API
包括
mdelay()
、
udelay()
和
ndelay()
,分别支持毫秒、微妙和纳秒级的延时。这些函数的实际实现依赖于体系结构,而且也并非在所有平台上都被完整实现。忙等待的实现方法是测量
CPU
执行一条指令的时间,为了延时,执行一定数量的指令。从前文可知,内核会在启动过程中进行测量出一个
loops_per_jiffy
值。短延时
API
就使用了
loops_per_jiffy
值来决定它们需要进行循环的数量。
为了实现握手进程中
1
微妙的延时,
USB
主机控制器驱动(
drivers/usb/host/ehci-hcd.c
)会调用
udelay()
,而
udelay()
的内部会调用
loops_per_jiffy
:
result = ehci_readl(ehci, ptr);
/* ... */
if (result == done) return 0;
udelay(1); /* Internally uses loops_per_jiffy */
usec--;
} while (usec > 0);
Pentium
时间戳计数器
时间戳计数器(
TSC
)是
Pentium
兼容处理器中的一个计数器,它记录自启动以来
CPU
消耗的时钟周期数。由于
TSC
睡着处理器周期速率的比例正常,它提供了非常高的精确度。
TSC
通常被用于剖析
和监测
代码。使用
rdtsc
指令可测量某段代码的执行时间,其精度达到微妙级。
TSC
的节拍可以被转化为秒,方法是将其除以
CPU
时钟速率(包含在内核变量
cpu_khz
中)。
在如下的代码片段中。
low_tsc_ticks
和
high_tsc_ticks
分别包含了
TSC
的低
32
位和高
32
位。低
32
位可能在数秒内溢出(具体时间依赖于处理器速度),但是这已经用于许多代码的
剖析
了:
unsigned long low_tsc_ticks0, high_tsc_ticks0;
unsigned long low_tsc_ticks1, high_tsc_ticks1;
unsigned long exec_time;
rdtsc(low_tsc_ticks0, high_tsc_ticks0); /* Timestamp
before */
printk("Hello World\n"); /* Code to be
profiled */
rdtsc(low_tsc_ticks1, high_tsc_ticks1); /* Timestamp after */
exec_time = low_tsc_ticks1 - low_tsc_ticks0;
在
1.8GHz Pentium
处理器上,
exec_time
的结果为
871
(或半微妙)。
在
2.6.21
内核中,针对高精度定时器的支持
(CONFIG_HIGH_RES_TIMERS)
已经被融入了内核。它使用了硬件特定的高速定时器来提供对
nanosleep()
等
API
高精度的支持。
在基于
Pentium
的机器上,内核借助的是
TSC
。
实时钟
RTC
在非易失性存储器上记录绝对时间。在
x86 PC
上,
RTC
位于由电池供电
[4]
的
互补金属氧化物半导体
(
CMOS
)存储器的顶部。从第
5
章《字符设备驱动》的图
5.1
可以看出传统
PC
体系结构中
CMOS
的位置。在嵌入式系统中,
RTC
可能被集成到处理器中,也可能通过
I2C
或
SPI
总线在外部连接,见第
8
章。
[4]RTC
的电池能够持续使用很多年,通过会超过电脑的使用寿命,因此,你从来都不需要替换它。
使用
RTC
,你可以完成如下工作:
(
2
)产生频率从
2HZ
到
8192HZ
之间的周期性中断;
(
3
)设置
alarm
许多应用程序需要使用绝对时间或称墙上时间(
wall time
)。
jiffies
是相对于系统启动后的时间,它不包含墙上时间。内核将墙上时间记录在
xtime
变量中,在启动过程中,会根据从
RTC
读取到的目前的墙上时间初始化
xtime
,在系统停机后,墙上时间会被写回
RTC
。你可以使用
do_gettimeofday()
读取墙上时间,其最高精度
由
硬件
决定:
static struct timeval curr_time;
do_gettimeofday(&curr_time);
my_timestamp = cpu_to_le32(curr_time.tv_sec); /* Record timestamp */
用户空间也包含一系列可以访问墙上时间的函数,包括:
(
1
)
time()
,该函数返回日历时间,或从
Epoch
(
1970
年
1
月
1
日
00:00:00
)以来经历的秒数;
(
2
)
localtime()
,以分散的形式返回日历时间;
(
3
)
mktime()
,
进行
localtime()
的反向工作;
(
4
)
gettimeofday()
,如果你的平台支持的话,该函数将以微妙精度返回日历时间。
内核中的并发
随
着多核笔记本电脑时代的到来,对称多处理器(
SMP
)的使用不再被限于高科技用户。
SMP
和内核抢占是多线程执行的
2
种场景。多个线程能够同时操作共享的内核数据结构,因此,对这些数据结构的访问必须被串行化。
接下来,我们会讨论并发访问情况下保护共享内核资源的基本概念。我们以一个简单的例子开始,并逐步引入中断、内核抢占和
SMP
等复杂概念。
自选锁可以确保在同时只有一个线程进入临界区。其他想进入临界区的线程必须不停地原地打转,知道第1个线程释放自选锁。
注意:这里所说的线程不仅限于内核线程,还包含用户线程进入内核后的代表。
#include <linux/spinlock.h>
spinlock_t mylock = SPIN_LOCK_UNLOCKED; /* Initialize */
/* Acquire the spinlock. This is inexpensive if there
* is no one inside the critical section. In the face of
* contention, spinlock() has to busy-wait.
*/
spin_lock(&mylock);
/* ... Critical Section code ... */
spin_unlock(&mylock); /* Release the lock */
与自选锁不同的是,互斥体在进入一个被占用的临界区之前,不会原地打转而是使当前线程进入睡眠状态。如果要等待的时间较长,互斥体比自选锁会更合适,因为自选锁会消耗CPU资源。在使用互斥体的场合,多于2次进程切换时间都可被认为是长时间,因此一个互斥体会引起本线程睡眠,而当其被唤醒时,它需要被切换回来。
因此,在很多情况下,决定使用自选锁还是互斥体相对来说很容易:
(2)由于互斥体会在面临竞争的情况下将当前线程置于睡眠状态,因此,在中断处理函数中,只能使用自选锁。(在第4章中,你将学习到更多的关于中断上下文的限制。)
#include <linux/mutex.h>
/* Statically declare a mutex. To dynamically
create a mutex, use mutex_init() */
static DEFINE_MUTEX(mymutex);
/* Acquire the mutex. This is inexpensive if there
* is no one inside the critical section. In the face of
* contention, mutex_lock() puts the calling thread to sleep.
*/
mutex_lock(&mymutex);
/* ... Critical Section code ... */
mutex_unlock(&mymutex); /* Release the mutex */
(1)非抢占内核,单CPU情况下存在于进程上下文的临界区;
(2)非抢占内核,单CPU情况下存在于进程和中断上下文的临界区;
(3)可抢占内核,单CPU情况下存在于进程和中断上下文的临界区;
(4)可抢占内核,SMP情况下存在于进程和中断上下文的临界区。
互斥体接口代替了老的信号量接口(semaphore),它互斥体诞生于-rt树,在2.6.16内核中被融入主线内核。
尽管如此,但是老的信号量仍然在内核和驱动中被广泛使用。信号量接口的基本用法如下:
#include <asm/semaphore.h> /* Architecture dependent
header */
/* Statically declare a semaphore. To dynamically
create a semaphore, use init_MUTEX() */
static DECLARE_MUTEX(mysem);
down(&mysem); /* Acquire the semaphore */
/* ... Critical Section code ... */
up(&mysem); /* Release the semaphore */
|
案例
1
:进程上下文,单
CPU
,非抢占内核
这种情况最为简单,不需要加锁,因此不再赘述。
案例
2
:进程和上下文,单
CPU
,非抢占内核
在这种情况下,为了保护临界区,仅仅需要禁止中断。如图2.4,假定进程上下文的执行单元A、B以及中断上下文的执行单元C都企图进入相同的临界区。
由于执行单元C总是在中断上下文执行,它会优先于执行单元A和B,因此,它不要担心保护的问题。执行单元A和B也不必关心彼此会被互相打断,因为内核是非抢占的。因此,执行单元A和B仅仅需要担心C会在它们进入临界区的时候横行进入。为了实现此目的,它们会在进入临界区之前禁止中断:
Point A
:
local_irq_disable(); /* Disable Interrupts in local CPU */
/* ... Critical Section ... */
local_irq_enable(); /* Enable Interrupts in local CPU */
但是,如果当执行到Point A的时候已经被禁止,local_irq_enable()将产生副作用,它会重新使能中断,而不是恢复之前的中断状态。可以这样修复它:
Point A:
local_irq_save(flags); /* Disable Interrupts */
/* ... Critical Section ... */
local_irq_restore(flags); /* Restore state to what
it was at Point A */
不论Point A的中断处于什么状态,上述工作都将正确执行。
案例
3
:进程和中断上下文,单
CPU
,抢占内核
如果内核使能了抢占,仅仅禁止中断将不再能确保对临界区的保护,因为另一个处于进程上下文的执行单元可能会进入临界区。重新回到图2.4,现在,除了C以外,执行单元A和B必须提防彼此。显而易见,解决该问题的方法是在进入临界区之前禁止内核抢占、中断,并在退出临界区的时候恢复内核抢占和中断。因此,执行单元A和B使用了自选锁API附带irq的变体:
unsigned long flags;
Point A:
/* Save interrupt state.
* Disable interrupts - this implicitly disables preemption */
spin_lock_irqsave(&mylock, flags);
/* ... Critical Section ... */
/* Restore interrupt state to what it was at Point A */
spin_unlock_irqrestore(&mylock, flags);
我们不需要在最后显示地恢复Point A的抢占状态,因为内核自身会通过一个名叫抢占计数器的变量维护它。在抢占被禁止时(通过调用preempt_disable()),计数器会增加;在抢占被使能时(通过调用preempt_enable()),计数器被减少。只有在计数器为0的时候,抢占才发挥作用。
案例
4
:进程和中断上下文,
SMP
机器,抢占内核
现在临界区执行于SMP机器上,而且你的内核配置了CONFIG_SMP和CONFIG_PREEMPT。到目前为止讨论的场景中,自旋锁原语发挥的作用仅限于使能和禁止抢占和中断,时间的锁功能并未被完全编译进来。在SMP机器内,锁逻辑被编译进来,而且自旋锁原语确保了SMP安全性。SMP使能的含义如下:
unsigned long flags;
Point A:
/*
- Save interrupt state on the local CPU
- Disable interrupts on the local CPU. This implicitly disables
preemption.
- Lock the section to regulate access by other CPUs
*/
spin_lock_irqsave(&mylock, flags);
/* ... Critical Section ... */
/*
- Restore interrupt state and preemption to what it
was at Point A for the local CPU
- Release the lock
*/
spin_unlock_irqrestore(&mylock, flags);
在SMP系统上,获取自旋锁时,仅仅本CPU上的中断被禁止。因此,一个进程上下文的执行单元(图2.4中的执行单元A)在一个CPU上运行的同时,一个中断处理函数(图2.4中的执行单元C)可能运行在另一个CPU上。非本CPU上的中断处理函数必须自旋等待本CPU上的进程上下文代码退出临界区。中断上下文需要调用spin_lock()/spin_unlock():
/* ... Critical Section ... */
spin_unlock(&mylock);
除了有irq变体以外,自旋锁也有底半部(BH)变体。在锁被获取的时候,spin_lock_bh()会禁止底半部,而spin_unlock_bh()则会在锁被释放时重新使能底半部。我们将在第4章讨论底半部。
-rt
树
实时(-rt)树,也被称作CONFIG_PREEMPT_RT补丁集,实现了内核中一些针对低延时的修改。该补丁集可以从[url]www.kernel.org/pub/linux/kernel/projects/rt[/url]下载,它允许内核的大部分位置可被抢占,但是用自旋锁代替了一些互斥体。它也合并了一些高精度的定时器。数个-rt功能已经被融入了主线内核。在工程的wiki页上,能找到详细的文档,地址为[url]http://rt.wiki.kernel.org/[/url]。
|
原子操作
if (!skb->cloned ||
/* Atomically decrement and check if the returned value is zero */
!atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 :
1,&skb_shinfo(skb)->dataref)) {
/* ... */
kfree(skb->head);
}
当skb_release_data()执行的时候,另一个调用skbuff_clone()(也定义于net/core/skbuff.c)的执行单元也许在同步地增加数据引用计数:
/* ... */
/* Atomically bump up the data reference count */
atomic_inc(&(skb_shinfo(skb)->dataref));
/* ... */
内核也支持set_bit()、clear_bit()和test_and_set_bit()操作,它们可用于原子地进行位修改。查看include/asm-your-arch/atomic.h文件可以看出你所在体系结构所支持的原子操作。
另一个特定的并发保护机制是自旋锁的变体读者—写者锁。如果每个执行单元在访问临界区的时候要么是读,要么是写共享的数据结构,但是它们都不会同时进行读和写操作,这种锁是最好的选择。多则读执行单元被允许同时进入临界区。读者自旋锁可以这样定义:
read_lock(&myrwlock); /* Acquire reader lock */
/* ... Critical Region ... */
read_unlock(&myrwlock); /* Release lock */
但是,如果一个写执行单元进入了临界区,其他的读和写都不被允许进入。写者锁的用法如下:
rwlock_t myrwlock = RW_LOCK_UNLOCKED;
write_lock(&myrwlock); /* Acquire writer lock */
/* ... Critical Region ... */
write_unlock(&myrwlock); /* Release lock */
net/ipx/ipx_route.c
中的IPX路由代码是使用读者—写者锁的真实例子。一个称作ipx_routes_lock的读者—写者锁将保护IPX路由表的并发访问。要通过查找路由表实现包转发的执行单元需要请求读者锁。需要添加和删除路由表中入口的执行单元必须获取写者锁。由于通过读路由表的情况比更新路由表的情况多地多,使用读者—写者锁重大地提供了性能。
和传统的自旋锁一样,读者—写者锁也有相应的irq变体:read_lock_irqsave()、read_lock_irqrestore()、write_lock_irqsave()和write_lock_irqrestore()。这些函数的含义与传统自旋锁相应的变体相似。
2.6
内核引入的顺序锁(seqlock)是一种支持写着多于读者的读者—写者锁。在在一个变量的写操作比读操作多地多的情况下,这种锁非常有用。前文讨论的jiffies_64变量就是使用顺序锁的一个例子。写执行单元不必等待一个已经进入临界区的读者,因此,读执行单元也许会它们进入临界区的操作会失败因此需要重试:
u64 get_jiffies_64(void) /* Defined in kernel/time.c */
{
unsigned long seq;
u64 ret;
do {
seq = read_seqbegin(&xtime_lock);
ret = jiffies_64;
} while (read_seqretry(&xtime_lock, seq));
return ret;
}
2.6
内核还引入了另一种称为读—拷贝—更新(RCU)的机制,该锁用于提高读操作远多于写操作时的性能。其基本理念是读执行单元不需要加锁,但是写执行单元会变得更加复杂,它们会在数据结构的一份拷贝上执行更新操作,并代替读者看到的指针。为了确保所有正在进行的读操作的完成,原子拷贝会一直被保持到所有CPU上的下一次上下文切换。使用RCU的情况很复杂,因此,只有在确保你确实需要使用它而不是前文的其他原语的时候,才适宜选择它。include/linux/rcupdate.h文件中定义了RCU的数据结构和接口函数,Documentation/RCU/*包含丰富的文档。
fs/dcache.c
文件中包含一个RCU的使用例子。在Linux中,每个文件都与一个目录入口信息(dentry结构体)、元数据信息(存放在inode中)和实际的数据(存放在数据块中)关联。每次你操作一个文件的时候,文件路径中的组件会被解析,相应的dentry会被获取。为了加速未来的操作,dentry结构体被缓存在称为dcache的数据结构中。任何时候,对dcache进行查找的数量都远多于dcache的更新操作,因此,对dcache的访问适宜用RCU原语进行保护。
由于难于重现,并发相关的问题通常非常难调试。在编译和测试你的代码的时候使能SMP(CONFIG_SMP)和抢占(CONFIG_PREEMPT)是一种很好的理念,即便你的产品将运行在当CPU、禁止抢占的情况下。在Kernel hacking下有一个称为Spinlock and rw-lock debugging的配置选项CONFIG_DEBUG_SPINLOCK,它能帮助你找到一些常见的自旋锁错误。Lockmeter([url]http://oss.sgi.com/projects/lockmeter/[/url])等工具可用于锁相关的统计信息。
spin_lock(&mylock); /* Acquire lock */
/* ... Critical Section ... */
if (error) { /* This error condition occurs rarely */
return -EIO; /* Forgot to release the lock! */
}
spin_unlock(&mylock); /* Release lock */
如果在你写完代码的数月或数年以后首次出现了问题,回过头来调试它将变地更为辣手。(在第21章《设备驱动的调试》一章的《Kdump》一节有一个相关的调试例子。)因此,为了避免遭遇这种不快,在你设计软件的结构的时候,就应该考虑并发逻辑。
p
roc
文件系统
proc
文件系统(procfs)是一种虚拟的文件系统,它创建内核内部的视窗。你浏览procfs看到的数据是在内核运行过程中产生的。procfs中文件可被用于配置内核参数、查看内核结构体、从设备驱动中收集统计信息或者获取通用的系统信息。
Procfs
是一种虚假的文件系统,这意味着驻留于procfs中的文件并不与物理存储设备如硬盘等关联。相反地,这些文件中的数据由内核中相应的入口点按需动态创建。因此,procfs中的文件大小都显示为0。Procfs通常被在启动过程中挂载在/proc目录,通过运行mount命令你可以看出这一点。
为了取得procfs能力的第一感觉,请查看/proc/cpuinfo、/proc/meminfo、 /proc/interrupts、/proc/tty/driver/serial、/proc/bus/usb/devices和/proc/stat的内容。通过写/proc/sys/目录中的文件可以在运行时修改某些内核参数。例如,通过向/proc/sys/kernel/printk文件echo一个新的值,你可以改变内核printk 日志的级别。许多实用程序(如ps)和系统性能监视工具(如sysstat)就是通过驻留于/proc中的文件来获取信息的。
2.6
内核引入的seq文件简化了大的procfs操作。附录C《Seq文件》对此进行了描述。
内存分配
内核会以分页形式组织物理内存,而页大小则依赖于具体的体系结构。在基于x86的机器上,其大小为4096字节。物理内存中的每一页都有一个与之对应的page结构体(定义在include/linux/mm_types.h文件中):
unsigned long flags; /* Page status */
atomic_t _count; /* Reference count */
/* ... */
void * virtual; /* Explained later on */
};
在32位x86系统上,缺省的内核配置会将4GB的地址空间分成给用户空间的3GB的虚拟内存空间和给内核空间的1GB的空间(如图2.5)。这导致内核能处理的处理内存有1GB的限制。现实情况是,限制为896MB,因为地址空间的128MB已经被内核数据结构占据。通过改变3GB/1GB的分割线你可以增加这个限制,但是由于减少了用户进程虚拟地址空间的大小,对于内存密集型的应用程序,可能会导致一些问题。
内核中用于映射低于896MB物理内存的地址与物理地址之间存在线性便宜,被称作逻辑地址。在支持“高端内存”的情况下,在通过特定的方式映射这些区域产生对应的虚拟地址后,内核将能访问超过896MB的内存。所有的逻辑地址都是内核虚拟地址,而所有的虚拟地址并非一定是逻辑地址。
因此,存在如下的内存zone:
(1)ZONE_DMA (<16MB),该zone用于直接内存访问(DMA)。由于传统的ISA设备有24条地址线,只能访问开始的16MB,因此,内核将该区域献给了这些设备;
(3)ZONE_HIGH (>896MB), 仅仅在通过kmap()映射页为虚拟地址后才能访问。(通过kunmap()可去除映射)。相应的内核地址为虚拟地址而非逻辑地址。如果相应的页未被映射的话,高端内存页的page结构体的virtual字段将指向NULL。
kmalloc()
是一个用于从ZONE_NORMAL区域返回连续内存的内存分配函数,其原型如下:
void *kmalloc(int count, int flags);
count
是要分配的字节数,flags是一个模式说明符。支持的所有标志列在include/linux./gfp.h文件中(gfp的意思是“get free page”),如下的标志最常用:
(2)GFP_ATOMIC,用于在中断上下问获取内存。在这种模式下,kmalloc()不允许进行睡眠等待以获得空闲页,因此GFP_ATOMIC分配成功的可能性比用GFP_KERNEL低。
由于kmalloc()返回的内存保留了“前世”的内容,因此,如果将它暴露给用户空间,可到会导致安全问题,因此我们可以kzalloc()获得被填充为0的内存。
如果你需要分配大的内存缓冲区,而且也不要求内存在物理上联系,可以用vmalloc()代替kmalloc():
count
是要请求分配的内存大小,该函数返回内核虚拟地址。
Vmalloc()
允许比kmalloc()更大的分配尺寸,但是它更慢,而且不能从中断上下文调用。另外,你不能用vmalloc()返回的物理上不连续的内存执行DMA。在设备打开时,高性能的网络驱动通常会使用vmalloc()来分配较大的描述符环行缓冲区。
内核还提供了一些更复杂的内存分配技术,包括后备缓冲区(look aside buffer)、slab和mempool,它们超出了本章的范围。
内存启动始于执行arch/x86/boot/目录中的实模式汇编代码。查看arch/x86/kernel/setup_32.c文件可以看出保护模式的内核怎样获取实模式内核收集的信息。
第一条信息来自于init/main.c中的代码,深入挖掘init/calibrate.c可以对BogoMIPS校准理解地更清楚,而include/asm-your-arch/bugs.h则包含体系结构相关的检查。
内核中的时间服务由驻留于arch/your-arch/kernel/中的体系结构相关的部分和实现于kernel/timer.c中的通用部分组成。从include/linux/time*.h可以获取相关的定义。
内存管理源代码存放在顶级mm/目录中。
表2.1给出了本章中主要的数据结构以及其在源代码树中定义的位置。表2.2则列出了本章中主要内核编程接口及其定义的位置。
表2.1 数据结构总结
数据结构
|
位置
|
描述
|
HZ
|
include/asm-your-arch/param.h
|
Number of times the system timer ticks in 1 second
|
loops_per_jiffy
|
init/main.c
|
|
timer_list
|
include/linux/timer.h
|
|
timeval
|
include/linux/time.h
|
Timestamp
|
spinlock_t
|
include/linux/spinlock_types.h
|
A busy-locking mechanism to ensure that only a single thread enters a critical section
|
semaphore
|
include/asm-your-arch/semaphore.h
|
|
mutex
|
include/linux/mutex.h
|
|
rwlock_t
|
include/linux/spinlock_types.h
|
Reader-writer spinlock
|
page
|
include/linux/mm_types.h
|
表2.2 内核编程接口总结
位置
|
描述
|
|
time_after()
time_after_eq()
time_before()
ime_before_eq()
|
include/linux/jiffies.h
|
Compares the current value of jiffies with a specified future value
|
schedule_timeout()
|
kernel/timer.c
|
|
wait_event_timeout()
|
include/linux/wait.h
|
|
DEFINE_TIMER()
|
include/linux/timer.h
|
Statically defines a timer
|
init_timer()
|
kernel/timer.c
|
Dynamically defines a timer
|
add_timer()
|
include/linux/timer.h
|
Schedules the timer for execution after the timeout has elapsed
|
mod_timer()
|
kernel/timer.c
|
Changes timer expiration
|
timer_pending()
|
include/linux/timer.h
|
Checks if a timer is pending at the moment
|
udelay()
|
include/asm-your-arch/delay.h arch/your-arch/lib/delay.c
|
Busy-waits for the specified number of microseconds
|
rdtsc()
|
include/asm-x86/msr.h
|
Gets the value of the TSC on Pentium-compatible processors
|
do_gettimeofday()
|
kernel/time.c
|
Obtains wall time
|
local_irq_disable()
|
include/asm-your-arch/system.h
|
Disables interrupts on the local CPU
|
local_irq_enable()
|
include/asm-your-arch/system.h
|
Enables interrupts on the local CPU
|
local_irq_save()
|
include/asm-your-arch/system.h
|
Saves interrupt state and disables interrupts
|
local_irq_restore()
|
include/asm-your-arch/system.h
|
|
spin_lock()
|
include/linux/spinlock.h kernel/spinlock.c
|
Acquires a spinlock.
|
spin_unlock()
|
include/linux/spinlock.h
|
Releases a spinlock
|
spin_lock_irqsave()
|
include/linux/spinlock.h kernel/spinlock.c
|
|
spin_unlock_irqrestore()
|
include/linux/spinlock.h kernel/spinlock.c
|
Restores interrupt state and preemption and releases the lock
|
DEFINE_MUTEX()
|
include/linux/mutex.h
|
Statically declares a mutex
|
include/linux/mutex.h
|
Dynamically declares a mutex
|
|
mutex_lock()
|
kernel/mutex.c
|
Acquires a mutex
|
mutex_unlock()
|
kernel/mutex.c
|
Releases a mutex
|
DECLARE_MUTEX()
|
include/asm-your-arch/semaphore.h
|
Statically declares a semaphore
|
init_MUTEX()
|
include/asm-your-arch/semaphore.h
|
Dynamically declares a semaphore
|
up()
|
arch/your-arch/kernel/semaphore.c
|
Acquires a semaphore
|
down()
|
arch/your-arch/kernel/semaphore.c
|
Releases a semaphore
|
atomic_inc()
atomic_inc_and_test()
atomic_dec()
atomic_dec_and_test()
clear_bit()
set_bit()
test_bit()
test_and_set_bit()
|
include/asm-your-arch/atomic.h
|
Atomic operators to perform lightweight operations
|
read_lock()
read_unlock()
read_lock_irqsave()
read_lock_irqrestore()
write_lock()
write_unlock()
write_lock_irqsave()
write_lock_irqrestore()
|
include/linux/spinlock.h kernel/spinlock.c
|
Reader-writer variant of spinlocks
|
down_read()
up_read()
down_write()
up_write()
|
kernel/rwsem.c
|
Reader-writer variant of semaphores
|
read_seqbegin()
read_seqretry()
write_seqlock()
write_sequnlock()
|
include/linux/seqlock.h
|
Seqlock operations
|
kmalloc()
|
include/linux/slab.h mm/slab.c
|
Allocates physically contiguous memory from ZONE_NORMAL
|
kzalloc()
|
include/linux/slab.h mm/util.c
|
Obtains zeroed kmalloced memory
|
kfree()
|
mm/slab.c
|
Releases kmalloced memory
|
vmalloc()
|
mm/vmalloc.c
|
|
本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120804,如需转载请自行联系原作者