实时和Linux之二:抢占式内核

实时和Linux之二:抢占式内核

本文译者:
康华:计算机硕士,主要从事Linux操作系统内核、Linux技术标准、计算机安全、软件测
试等领域的研究与开发工作,现就职于信息产业部软件与集成电路促进中心所属的MII-HP
Linux软件实验室。如果需要可以联系通过kanghua151@msn.com联系他。
注明:棕色写的内容为译者注。
如果有意义不明之处请参见原文。
Kevin 将继续他的实时之旅,这次他要着重分析如何通过改造Linux内核来为应用程序带来
实时性能。
在2002年的1-2月发行的嵌入Linux月刊中,我们探讨了有关实时和Linux的基础问题。而这
次我将精力花在改造Linux内核来为应用程序带来实时性能这个主题上。到目前为止,工作
的重心集中在提高内核响应速度——通过减少抢占响应时间缩短系统响应时间,因为我们
知道抢占响应时间在Linux中耗时较长。
通过改进内核——仅仅是剔除一些标准内核的功能——并不去改变或增加内核API,应用程
序就可以获得更快的响应速度。这样做优势明显,因为ISVs(独立软件开发商)不需要为不
同的实时要求开发不同版本的系统。比如,DVD播放器可以在一个改进过的内核上更稳定的
运行,而它不必知道该内核是经过改进的版本。
背景和历史
自2.2版本内核发布以来,内核抢占成为一个热门话题。Paul Barton-Davis 和 Benno
Senoner曾给Linus Torvalds写了一封信(该信后来追加了许多人的签名),请求在2.4版本
内核中显著降低抢占延迟时间。

Linux 同步方法剖析

FROM :http://www.ibm.com/developerworks/cn/linux/l-linux-synchronization.html (这里面还有一些没讲到,2.6同步机制包括:per-cpu variables, atomic operation, memory barrier, spin lock,semaphore, seqlocks, local interrupt disabling, local softirq disable, read-copy-update。详见ULK3)

Linux 同步方法剖析

M. Tim Jones 是一名嵌入式软件工程师,他是 GNU/Linux Application Programming、AI
Application Programming 以及 BSD Sockets Programming from a Multilanguage
Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式
架构设计,再到网络协议的开发。Tim 是位于科罗拉多州 Longmont 的 Emulex Corp. 的
一名顾问工程师。
简介: 在学习 Linux® 的过程中,您也许接触过并发(concurrency)、临界段(
critical section)和锁定,但是如何在内核中使用这些概念呢?本文讨论了 2.6 版内核
中可用的锁定机制,包括原子运算符(atomic operator)、自旋锁(spinlock)、读/写
锁(reader/writer lock)和内核信号量(kernel semaphore)。本文还探讨了每种机制
最适合应用到哪些地方,以构建安全高效的内核代码。
发布日期: 2007 年 11 月 19 日
本文讨论了 Linux 内核中可用的大量同步或锁定机制。这些机制为 2.6.23 版内核的许多
可用方法提供了应用程序接口(API)。但是在深入学习 API 之前,首先需要明白将要解
决的问题。

并发和锁定

当存在并发特性时,必须使用同步方法。当在同一时间段出现两个或更多进程并且这些进
程彼此交互(例如,共享相同的资源)时,就存在并发现象。
在单处理器(uniprocessor,UP)主机上可能发生并发,在这种主机中多个线程共享同一
个 CPU 并且抢占(preemption)创建竞态条件。抢占通过临时中断一个线程以执行另一个
线程的方式来实现 CPU 共享。竞态条件发生在两个或更多线程操纵一个共享数据项时,其
结果取决于执行的时间。在多处理器(MP)计算机中也存在并发,其中每个处理器中共享
相同数据的线程同时执行。注意在 MP 情况下存在真正的并行(parallelism),因为线程
是同时执行的。而在 UP 情形中,并行是通过抢占创建的。两种模式中实现并发都较为困
难。
Linux 内核在两种模式中都支持并发。内核本身是动态的,而且有许多创建竞态条件的方
法。Linux 内核也支持多处理(multiprocessing),称为对称多处理(SMP)。可以在本
文后面的参考资料部分学到更多关于 SMP 的知识。
临界段概念是为解决竞态条件问题而产生的。一个临界段是一段不允许多路访问的受保护
的代码。这段代码可以操纵共享数据或共享服务(例如硬件外围设备)。临界段操作时坚
持互斥锁(mutual exclusion)原则(当一个线程处于临界段中时,其他所有线程都不能
进入临界段)。
临界段中需要解决的一个问题是死锁条件。考虑两个独立的临界段,各自保护不同的资源
。每个资源拥有一个锁,在本例中称为 A 和 B。假设有两个线程需要访问这些资源,线程
X 获取了锁 A,线程 Y 获取了锁 B。当这些锁都被持有时,每个线程都试图占有其他线程
当前持有的锁(线程 X 想要锁 B,线程 Y 想要锁 A)。这时候线程就被死锁了,因为它
们都持有一个锁而且还想要其他锁。一个简单的解决方案就是总是按相同次序获取锁,从
而使其中一个线程得以完成。还需要其他解决方案检测这种情形。表 1 定义了此处用到的
一些重要的并发术语。
表 1. 并发中的重要定义
术语                定义
竞态 两个或更多线程同时操作资源时将会导
条件 致不一致的结果。
临界 用于协调对共享资源的访问的代码段。
互斥 确保对共享资源进行排他访问的软件特
锁  性。
死锁 由两个或更多进程和资源锁导致的一种
特殊情形,将会降低进程的工作效率。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Linux 同步方法

Debugging mac address of bridge

# strace brctl addbr br0
...
socket(PF_FILE, SOCK_STREAM, 0)         = 3
ioctl(3, SIOCBRADDBR, 0x7fff0b1240c6)   = 0
exit_group(0)                           = ?

# ifconfig br0 (got a random address)
br0       Link encap:Ethernet  HWaddr 5a:34:f1:ba:8f:40 

# strace brctl addif br0 eth0 (no ioctl releated with changing mac)
...
ioctl(4, SIOCGIFINDEX, {ifr_name="eth0", ifr_index=2}) = 0
close(4)                                = 0
ioctl(3, SIOCBRADDIF, 0x7fff57c335c0)   = 0
exit_group(0)                           = ?

# ifconfig br0 (got same addr as eth0)
br0       Link encap:Ethernet  HWaddr 00:22:68:16:c9:e8  

# grep SIOCBRADDIF -nr net/bridge/
net/bridge/br_ioctl.c:409:      case SIOCBRADDIF:
net/bridge/br_ioctl.c:411:              return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

net/bridge/br_ioctl.c:
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
...
        case SIOCBRDELIF:
                return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
                           ^
                           |
                           |
net/bridge/br_ioctl.c      v
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
{
...
        if (isadd)
                ret = br_add_if(br, dev);
                           ^
                           |
                           |
net/bridge/br_if.c:        v
int br_add_if(struct net_bridge *br, struct net_device *dev)
{
...
        changed_addr = br_stp_recalculate_bridge_id(br);
                           ^
                           |
                           |
net/bridge/br_stp_if.c:    v
bool br_stp_recalculate_bridge_id(struct net_bridge *br)
{       
        const unsigned char *br_mac_zero =
                        (const unsigned char *)br_mac_zero_aligned;
        const unsigned char *addr = br_mac_zero;
        struct net_bridge_port *p;
        
        /* user has chosen a value so keep it */ flags & BR_SET_MAC_ADDR)
                return false;
        
        list_for_each_entry(p, &br->port_list, list) {
                if (addr == br_mac_zero ||
                    memcmp(p->dev->dev_addr, addr, ETH_ALEN) dev->dev_addr; <---- a 'min' mac address in port_list will be set to bridge
        }

陕西.凤翔

【当年盛况】
戎王由余在参观雍城宗庙宫室之后曾叹曰:“使鬼为之,则劳神矣;使人为之,则苦民矣!”
公元前238年,22岁的秦始皇在雍城登基,并于39岁完成统一中国的历史大业。
这里位于陕西关中平原西部,北枕千山,南带渭水,东望西安,西扼秦陇;这里曾是周室发祥之地,秦始皇登基处,汉高祖、汉文帝、汉景帝多次前来祭五峙;这里因传说“凤凰鸣于岐,翔于雍”而得名……

说了这么多,这里是秦始皇先祖们建都长达294年的雍城,今天的陕西省凤翔县。这里留有雍城的城址、宗庙、19位秦公大墓以及国人墓葬等大面积遗址,1988年经国务院公布为全国重点文物保护单位。

当年秦始皇在此登基

凤翔从6000多年前的原始社会即有村落分布。周平王元年东迁后,成为秦襄公封地。秦德公元年(公元前677年)徙都雍城,成为春秋至战国中期近300年间秦国的政治、经济、文化、军事中心。经过十九位国君的苦心经营,秦由弱小、落后的部族成为强大的诸侯国,都城雍城则筑有规模庞大的城垣,建有壮丽宏伟的宫殿,成为当时全国发达的大都市之一。戎王由余在参观雍城宗庙宫室之后曾叹曰:“使鬼为之,则劳神矣;使人为之,则苦民矣!”足见雍城的宏伟与瑰丽。

秦献公东迁后,作为故都,列祖列宗的陵寝及秦人宗庙仍在此地,许多重要祀典还在雍城举行。公元前238年,22岁的秦始皇即在雍城登基,并于39岁完成统一中国的历史大业。

雍城城区位于雍水北岸,国人墓区置于南郊和东南远郊,秦公陵园则安排在国人墓区之南的凤翔塬上。城区与墓葬区以雍水为界,国人墓区与秦公陵园区以兆沟为界,离馆别馆分布在城之东、南、西三面,如同繁星拱月般拱卫着都城。”西北大学专家介绍,雍城棋盘格式的城内街道设置,开创了中国古代城市建设的新格局。