﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-mysileng-随笔分类-linux内核</title><link>http://www.cppblog.com/mysileng/category/20064.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 01 Oct 2014 19:29:35 GMT</lastBuildDate><pubDate>Wed, 01 Oct 2014 19:29:35 GMT</pubDate><ttl>60</ttl><item><title>__builtin_expect 解惑</title><link>http://www.cppblog.com/mysileng/archive/2014/09/29/208454.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 29 Sep 2014 13:31:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2014/09/29/208454.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/208454.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2014/09/29/208454.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/208454.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/208454.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自：http://my.oschina.net/moooofly/blog/175019最近看 GLib 的代码遇到这个东东，网上搜索一圈，发现很多人都写过这个，自己今天才研究到，汗颜一下，扫盲一个点，留此记录为证！ 首先看一篇最官方的讲解： ====== likely() and unlikely() What are they ? &nbsp; &nbsp; &nbsp; In Linux ...&nbsp;&nbsp;<a href='http://www.cppblog.com/mysileng/archive/2014/09/29/208454.html'>阅读全文</a><img src ="http://www.cppblog.com/mysileng/aggbug/208454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2014-09-29 21:31 <a href="http://www.cppblog.com/mysileng/archive/2014/09/29/208454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>11.Linux内核设计与实现 P160---顺序锁总结 (转) </title><link>http://www.cppblog.com/mysileng/archive/2012/11/09/194988.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Fri, 09 Nov 2012 10:14:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/11/09/194988.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/194988.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/11/09/194988.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/194988.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/194988.html</trackback:ping><description><![CDATA[<span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">当使用读/写自旋锁时，内核控制路径发出的执行read_lock或write_lock操作的请求具有相同的优先权：读者必须等待，直到写操作完成。同样地，写者也必须等待，直到读操作完成。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">Linux 2.6中引入了顺序锁（seqlock），它与读/写自旋锁非常相似，只是它为写者赋予了较高的优先级：事实上，即使在读者正在读的时候也允许写者继续运行。这种策略的好处是写者永远不会等待读（除非另外一个写者正在写），缺点是有些时候读者不得不反复读多次相同的数据直到它获得有效的结果。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">每个顺序锁都是包括两个字段的seqlock_t结构：</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">typedef struct {</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; unsigned sequence;</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; spinlock_t lock;</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">} seqlock_t;</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">一个类型为spinlock_t的lock字段和一个整型的sequence字段，第二个字段sequence是一个顺序计数器。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">每个读者都必须在读数据前后两次读顺序计数器，并检查两次读到的值是否相同，如果不相同，说明新的写者已经开始写并增加了顺序计数器，因此暗示读者刚读到的数据是无效的。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">通过把SEQLOCK_UNLOCKED赋给变量seqlock_t或执行seqlock_init宏，把seqlock_t变量初始化为&#8220;未上锁&#8221;，并把sequence设为0：</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">#define __SEQLOCK_UNLOCKED(lockname) /</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{ 0, __SPIN_LOCK_UNLOCKED(lockname) }</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">#define SEQLOCK_UNLOCKED /</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;__SEQLOCK_UNLOCKED(old_style_seqlock_init)</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "># define __SPIN_LOCK_UNLOCKED(lockname) /</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; (spinlock_t)&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp; .raw_lock = __RAW_SPIN_LOCK_UNLOCKED,&nbsp;&nbsp;&nbsp; /</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; SPIN_DEP_MAP_INIT(lockname) }</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">#define __RAW_SPIN_LOCK_UNLOCKED&nbsp;&nbsp;&nbsp; { 1 }</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">写者通过调用write_seqlock()和write_sequnlock()获取和释放顺序锁。write_seqlock()函数获取seqlock_t数据结构中的自旋锁，然后使顺序计数器sequence加1；write_sequnlock()函数再次增加顺序计数器sequence，然后释放自旋锁。这样可以保证写者在整个写的过程中，计数器sequence的值是奇数，并且当没有写者在改变数据的时候，计数器的值是偶数。读者进程执行下面的临界区代码：</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; unsigned int seq;</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; do {</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; seq = read_seqbegin(&amp;seqlock);</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* ... CRITICAL REGION ... */</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; } while (read_seqretry(&amp;seqlock, seq));</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">read_seqbegin()返回顺序锁的当前顺序号；如果局部变量seq的值是奇数（写者在read_seqbegin()函数被调用后，正更新数据结构），或seq的值与顺序锁的顺序计数器的当前值不匹配（当读者正执行临界区代码时，写者开始工作），read_seqretry()就返回1：</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">static __always_inline int read_seqretry(const seqlock_t *sl, unsigned iv)</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">{</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; smp_rmb();</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">&nbsp;&nbsp;&nbsp; return (iv &amp; 1) | (sl-&gt;sequence ^ iv);</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">}</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">注意在顺序锁机制里，读者可能反复读多次相同的数据直到它获得有效的结果（read_seqretry返回0）。另外，当读者进入临界区时，不必禁用内核抢占；另一方面，由写者获取自旋锁，所以它进入临界区时自动禁用内核抢占。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">并不是每一种资源都可以使用顺序锁来保护。一般来说，必须在满足下述条件时才能使用顺序锁：</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">（1）被保护的数据结构不包括被写者修改和被读者</span><strong style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; "><span style="color: #ff0000; ">间接引用</span>&nbsp;</strong><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">的指针（否则，写者可能在读者的眼皮子底下就修改指针）。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">（2）读者的临界区代码没有副作用（否则，多个读者的操作会与单独的读操作有不同的结果）。</span><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><br style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; " /><span style="color: #333333; font-family: Arial; line-height: 26px; text-align: left; background-color: #ffffff; ">此外，读者的临界区代码应该简短，而且写者应该不常获取顺序锁，否则，反复的读访问会引起严重的开销。在Linux 2.6中，使用顺序锁主要是保护一些与系统时间处理相关的数据结构。</span>&nbsp;<img src ="http://www.cppblog.com/mysileng/aggbug/194988.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-11-09 18:14 <a href="http://www.cppblog.com/mysileng/archive/2012/11/09/194988.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>10.Linux内核设计与实现 P148---自旋锁总结 (转)</title><link>http://www.cppblog.com/mysileng/archive/2012/11/09/194982.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Fri, 09 Nov 2012 08:34:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/11/09/194982.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/194982.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/11/09/194982.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/194982.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/194982.html</trackback:ping><description><![CDATA[<span style="color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">自旋锁可分为用在单核处理器上和用在多核处理器上。</span><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; "></div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; "></div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">单核处理器：</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">用在单核处理器上，又可分为两种：</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">1.系统不支持内核抢占</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">此时自旋锁什么也不做，确实也不需要做什么，因为单核处理器只有一个线程在执行，又不支持内核抢占，因此资源不可能会被其他的线程访问到。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">2.系统支持内核抢占</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">这种情况下，自旋锁加锁仅仅是禁止了内核抢占，解锁则是启用了内核抢占。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">在上述两种情况下，在获取自旋锁后可能会发生中断，若中断处理程序去访问自旋锁所保护的资源，则会发生死锁。因此，linux内核又提供了spin_lock_irq()和spin_lock_irqsave()，这两个函数会在获取自旋锁的同时（同时禁止内核抢占），禁止本地外部可屏蔽中断，从而保证自旋锁的原子操作。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; "></div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">多核处理器：</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">多核处理器意味着有多个线程可以同时在不同的处理器上并行执行。举个例子：</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">四核处理器，若A处理器上的线程1获取了锁,B、C两个处理器恰好这个时候也要访问这个锁保护的资源，因此他俩CPU就一直自旋忙等待。D并不需要这个资源，因此它可以正常处理其他事情。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; "></div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">自旋锁的几个特点：</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">1.被自旋锁保护的临界区代码执行时不能睡眠。单核处理器下，获取到锁的线程睡眠，若恰好此时CPU调度的另一个执行线程也需要获取这个锁，则会造成死锁；多核处理器下，若想获取锁的线程在同一个处理器下，同样会造成死锁，若位于另外的处理器，则会长时间占用CPU等待睡眠的线程释放锁，从而浪费CPU资源。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">2.被自旋锁保护的临界区代码执行时不能被其他中断打断。原因同上类似。</div><div style="word-break: break-all; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; color: #333333; font-family: Arial, Helvetica, sans-serif; font-size: 12px; text-align: left; background-color: #ececec; ">3.被自旋锁保护的临界区代码在执行时，内核不能被抢占，亦同上类似。</div><img src ="http://www.cppblog.com/mysileng/aggbug/194982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-11-09 16:34 <a href="http://www.cppblog.com/mysileng/archive/2012/11/09/194982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>9.Linux内核设计与实现 P91---中断和中断处理程序 (转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/26/193914.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Fri, 26 Oct 2012 08:49:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/26/193914.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193914.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/26/193914.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193914.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193914.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; &nbsp; &nbsp; 中断还是中断，我讲了很多次的中断了，今天还是要讲中断，为啥呢？因为在操作系统中，中断是必须要讲的..&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么什么叫中断呢， 中断还是打断，这样一说你就不明白了。唉，中断还真是有点像打断。我们知道linux管理所有的硬件设备，要做的第一件事先是通信。然后，我们天天在说一句话：处理器的速度跟...&nbsp;&nbsp;<a href='http://www.cppblog.com/mysileng/archive/2012/10/26/193914.html'>阅读全文</a><img src ="http://www.cppblog.com/mysileng/aggbug/193914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-26 16:49 <a href="http://www.cppblog.com/mysileng/archive/2012/10/26/193914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>进程上下文 与 中断上下文</title><link>http://www.cppblog.com/mysileng/archive/2012/10/22/193681.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 22 Oct 2012 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/22/193681.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193681.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/22/193681.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193681.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193681.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; "><div><span style="font-family: verdana, sans-serif; font-size: 16px; ">进程上下文和中断上下文是操作系统中很重要的两个概念，这两个概念在操作系统课程中不断被提及，是最经常接触、看上去很懂但又说不清楚到底怎么回事。造成这种局面的原因，可能是原来接触到的操作系统课程的教学总停留在一种浅层次的理论层面上，没有深入去研究。</span><br /><span style="font-family: verdana, sans-serif; font-size: 16px; ">处理器总处于以下状态中的一种：</span><br /><span style="font-family: verdana, sans-serif; font-size: 16px; ">１、内核态，运行于进程上下文，内核代表进程运行于内核空间；</span><br /><span style="font-family: verdana, sans-serif; font-size: 16px; ">２、内核态，运行于中断上下文，内核代表硬件运行于内核空间；</span><br /><span style="font-family: verdana, sans-serif; font-size: 16px; ">３、用户态，运行于用户空间。</span></div><div><span style="font-family: verdana, sans-serif; font-size: 16px; ">用户空间的应用程序，通过系统调用，进入内核空间。这个时候用户空间的进程要传递很多变量、参数的值给内核，内核态运行的时候也要保存用户进程的一些寄存器值、变量等。所谓的&#8220;进程上下文&#8221;，可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。</span></div><div><span style="font-family: verdana, sans-serif; font-size: 16px; ">硬件通过触发信号，导致内核调用中断处理程序，进入内核空间。这个过程中，硬件的一些变量和参数也要传递给内核，内核通过这些参数进行中断处理。所谓的&#8220;中断上下文&#8221;，其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境（主要是当前被打断执行的进程环境）。</span></div><div><br /><span style="font-family: verdana, sans-serif; font-size: 16px; ">关于进程上下文LINUX完全注释中的一段话：</span></div><div><span style="font-family: verdana, sans-serif; font-size: 16px; ">&nbsp;&nbsp; 当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。当内核需要切换到另一个进程时，它需要保存当前进程的所有状态，即保存当前进程的上下文，以便在再次执行该进程时，能够必得到切换时的状态执行下去。在LINUX中，当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中，在内核态下执行中断服务例程。但同时会保留所有需要用到的资源，以便中断服务结束时能恢复被中断进程的执行。</span></div></span><img src ="http://www.cppblog.com/mysileng/aggbug/193681.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-22 20:40 <a href="http://www.cppblog.com/mysileng/archive/2012/10/22/193681.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>6.Linux内核设计与实现 P57---系统调用(转) </title><link>http://www.cppblog.com/mysileng/archive/2012/10/22/193680.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 22 Oct 2012 12:03:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/22/193680.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193680.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/22/193680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193680.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193680.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #ffffff; "><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">在Linux中，系统调用是用户空间访问内核的唯一手段，它们是内核唯一的合法入口。实际上，其他的像设备文件和/proc之类的方式，最终也还是要通过系统调用进</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">行的。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一般情况下，应用程序通过应用编程接口(API)而不是直接通过系统调用来编程，而且这种编程接口实际上并不需要和内核提供的系统调用对应。一个API定义了一组</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">应用程序使用的编程接口。它们可以实现成一个系统调用，也可以通过调用多个系统调用来实现，即使不使用任何系统调用也不存在问题。实际上，API可以在各种不同</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">的操作系统上实现，给应用程序提供完全相同的接口，而它们本身在这些系统上的实现却可能迥异。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Unix世界中，最流行的应用编程接口是基于POSIX标准的，Linux是与POSIX兼容的。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从程序员的角度看，他们只需要给API打交道就可以了，而内核只跟系统调用打交道；库函数及应用程序是怎么使用系统调用不是内核关心的。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 系统调用(在linux中常称作syscalls)通常通过函数进行调用。它们通常都需要定义一个或几个参数(输入)而且可能产生一些副作用。这些副作用通过一个long类型的返</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">回值来表示成功(0值)或者错误(负值)。在系统调用出现错误的时候会把错误码写入errno全局变量。通过调用perror()函数，可以把该变量翻译成用户可以理解的错误字</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">符串。</span></p><p align="left" style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 系统调用的实现有两个特别之处：1）函数声明中都有asmlinkage限定词，用于通知编译器仅从栈中提取该函数的参数。2）系统调用getXXX()在内核中被定义为sys</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">_getXXX()。这是Linux中所有系统调用都应该遵守的命名规则。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 系统调用号：在linux中，每个系统调用都赋予一个系统调用号，通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候，这个系统</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">调用号就被用来指明到底要执行哪个系统调用；进程不会提及系统调用的名称。系统调用号一旦分配就不能再有任何变更(否则编译好的应用程序就会崩溃)，如果一个系</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">统调用被删除，它所占用的系统调用号也不允许被回收利用。Linux有一个"未使用"系统调用sys_ni_syscall(),它除了返回-ENOSYS外不做任何其他工作，这个错误号</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">就是专门针对无效的系统调用而设的。虽然很罕见，但如果有一个系统调用被删除，这个函数就要负责&#8220;填补空位&#8221;。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内核记录了系统调用表中所有已注册过的系统调用的列表，存储在sys_call_table中。它与体系结构有关，一般在entry.s中定义。这个表中为每一个有效的系统调用指</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">定了唯一的系统调用号。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间的函数，因为内核驻留在受保护的地址空间上，应用程序应该以某种方式通知系统，告诉内核自</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">己需要执行一个系统调用，系统系统切换到内核态，这样内核就可以代表应用程序来执行该系统调用了。这种通知内核的机制是通过软中断实现的。x86系统上的软中断</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">由int$0x80指令产生。这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序，而该程序正是系统调用处理程序，名字叫system_call().它与硬件</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">体系结构紧密相关，通常在entry.s文件中通过汇编语言编写。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有的系统调用陷入内核的方式都是一样的，所以仅仅是陷入内核空间是不够的。因此必须把系统调用号一并传给内核。在x86上，这个传递动作是通过在触发软中</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">断前把调用号装入eax寄存器实现的。这样系统调用处理程序一旦运行，就可以从eax中得到数据。上述所说的system_call()通过将给定的系统调用号与NR_syscalls做比</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">较来检查其有效性。如果它大于或者等于NR_syscalls，该函数就返回-ENOSYS.否则，就执行相应的系统调用：call *sys_call_table(, %eax, 4);</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于系统调用表中的表项是以32位(4字节)类型存放的，所以内核需要将给定的系统调用号乘以4，然后用所得到的结果在该表中查询器位置。如图图一所示：</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img height="186" width="486" src="http://images.cnblogs.com/cnblogs_com/hanyan225/201107/201107080929038569.png" alt="结构" border="0" title="结构" style="border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; display: inline; border-style: initial; border-color: initial; " />&nbsp;</p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">上面已经提到，除了系统调用号以外，还需要一些外部的参数输入。最简单的办法就是像传递系统调用号一样把这些参数也存放在寄存器里。在x86系统上ebx,ecx,</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">edx,esi和edi按照顺序存放前5个参数。需要六个或六个以上参数的情况不多见，此时，应该用一个单独的寄存器存放指向所有这些参数在用户空间地址的指针。给用户空间的返回值也通过寄存器传递。在x86系统上，它存放在eax寄存器中。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 系统调用必须仔细检查它们所有的参数是否合法有效。系统调用在内核空间执行。如果任由用户将不合法的输入传递给内核，那么系统的安全和稳定将面临极大的考验。最重要的一种检查就是检查用户提供的指针是否有效，内核在接收一个用户空间的指针之前，内核必须要保证：</span></p><table width="673" cellpadding="2" cellspacing="0" border="1" style="border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; margin-top: auto; margin-right: 20px; margin-bottom: auto; margin-left: 20px; "><tbody><tr><td width="671" valign="top" style="font-size: 12px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; padding-top: 3px; padding-right: 3px; padding-bottom: 3px; padding-left: 3px; word-break: normal !important; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">1)指针指向的内存区域属于用户空间<br />2)指针指向的内存区域在进程的地址空间里<br />3)如果是读，读内存应该标记为可读。如果是写，该内存应该标记为可写。</span></td></tr></tbody></table><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内核提供了两种方法来完成必须的检查和内核空间与用户空间之间数据的来回拷贝。这两个方法必须有一个被调用。</span></p><table width="674" cellpadding="2" cellspacing="0" border="1" style="border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; margin-top: auto; margin-right: 20px; margin-bottom: auto; margin-left: 20px; "><tbody><tr><td width="672" valign="top" style="font-size: 12px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; padding-top: 3px; padding-right: 3px; padding-bottom: 3px; padding-left: 3px; word-break: normal !important; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">copy_to_user():向用户空间写入数据,需要3个参数。第一个参数是进程空间中的目的内存地址。第二个是内核空间内的源地址<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .第三个是需要拷贝的数据长度(字节数)。<br /><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">copy_from_user():向用户空间读取数据,需要3个参数。第一个参数是进程空间中的目的内存地址。第二个是内核空间内的源地<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 址.第三个是需要拷贝的数据长度(字节数)。<br />注意：这两个都有可能引起阻塞。当包含用户数据的页被换出到硬盘上而不是在物理内存上的时候，这种情况就会发生。此时，进程就会休眠，直到缺页处理程序将该页从硬盘重新换回到物理内存。</span></span></td></tr></tbody></table><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内核在执行系统调用的时候处于进程上下文，current指针指向当前任务，即引发系统调用的那个进程。在进程上下文中，内核可以休眠(比如在系统调用阻塞或显式</span><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">调用schedule()的时候)并且可以被抢占。当系统调用返回的时候，控制权仍然在system_call()中，它最终会负责切换到用户空间并让用户进程继续执行下去。</span></p><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 给linux添加一个系统调用时间很简单的事情，怎么设计和实现一个系统调用是难题所在。实现系统调用的第一步是决定它的用途，这个用途是明确且唯一的，不要尝试编写多用途的系统调用。ioctl则是一个反面教材。新系统调用的参数，返回值和错误码该是什么，这些都很关键。一旦一个系统调用编写完成后，把它注册成为一个正式的系统调用是件琐碎的工作，一般下面几步：</span></p><table width="672" cellpadding="2" cellspacing="0" border="1" style="border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; margin-top: auto; margin-right: 20px; margin-bottom: auto; margin-left: 20px; "><tbody><tr><td width="670" valign="top" style="font-size: 12px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; padding-top: 3px; padding-right: 3px; padding-bottom: 3px; padding-left: 3px; word-break: normal !important; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">1)在系统调用表(一般位于entry.s)的最后加入一个表项。从0开始算起，系统表项在该表中的位置就是它的系统调用号。如第<br />&nbsp;&nbsp; 10个系统调用分配到系统调用号为9<br />2)任何体系结构，系统调用号都必须定义于include/asm/unistd.h中<br />3)系统调用必须被编译进内核映像(不能编译成模块)。这只要把它放进kernel/下的一个相关文件就可以。</span></td></tr></tbody></table><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通常，系统调用靠C库支持，用户程序通过包含标准头文件并和C库链接，就可以使用系统调用(或者使用库函数，再由库函数实际调用)。庆幸的是linux本身提供了一组宏用于直接对系统调用进行访问。它会设置好寄存器并调用int $0x80指令。这些宏是_syscalln(),其中n的范围是从0到6.代表需要传递给系统调用的参数个数。这是由于该宏必须了解到底有多少参数按照什么次序压入寄存器。以open系统调用为例：</span></p><table width="670" cellpadding="2" cellspacing="0" border="1" style="border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; margin-top: auto; margin-right: 20px; margin-bottom: auto; margin-left: 20px; "><tbody><tr><td width="668" valign="top" style="font-size: 12px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #c0c0c0; border-right-color: #c0c0c0; border-bottom-color: #c0c0c0; border-left-color: #c0c0c0; border-collapse: collapse; padding-top: 3px; padding-right: 3px; padding-bottom: 3px; padding-left: 3px; word-break: normal !important; "><span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">open()系统调用定义如下是：<br />long open(const char *filename, int flags, int mode)<br />直接调用此系统调用的宏的形式为：<br />#define NR_open 5<br />_syscall3(long, open, const char *, filename, int , flags, int, mode)</span></td></tr></tbody></table><p style="font-size: 13px; line-height: 1.4; margin-top: 10px; margin-bottom: 10px; ">&nbsp;&nbsp;&nbsp;&nbsp;<span face="微软雅黑" style="line-height: 1.5; font-family: 微软雅黑; ">这样，应用程序就可以直接使用open().调用open()系统调用直接把上面的宏放置在应用程序中就可以了。对于每个宏来说，都有2+2*n个参数。每个参数的意义简单明了，这里就不详细说明了。</span></p></span><img src ="http://www.cppblog.com/mysileng/aggbug/193680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-22 20:03 <a href="http://www.cppblog.com/mysileng/archive/2012/10/22/193680.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>8.Linux内核设计与实现 P77---list_for_each()与list_for_each_safe()的区别 (转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/22/193660.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 22 Oct 2012 02:45:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/22/193660.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193660.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/22/193660.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193660.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193660.html</trackback:ping><description><![CDATA[<p><span style="font-size: 16px">list_for_each()的定义：</span><span style="font-size: 16px"></span></p><br />
<ol><li class="alt"><span>/**&nbsp;&nbsp;</span></li><li><span>&nbsp;*&nbsp;list_for_each&nbsp;&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;iterate&nbsp;over&nbsp;a&nbsp;list&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;*&nbsp;@pos:&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;&amp;struct&nbsp;list_head&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;loop&nbsp;counter.&nbsp;&nbsp;</span></li><li><span>&nbsp;*&nbsp;@head:&nbsp;&nbsp;&nbsp;the&nbsp;head&nbsp;for&nbsp;your&nbsp;list.&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;*/&nbsp;&nbsp;</span></li><li><span>#define&nbsp;list_for_each(pos,&nbsp;head)&nbsp;\&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(pos&nbsp;=&nbsp;(head)-&gt;next,&nbsp;prefetch(pos-&gt;next);&nbsp;pos&nbsp;!=&nbsp;(head);&nbsp;\&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos&nbsp;=&nbsp;pos-&gt;next,&nbsp;prefetch(pos-&gt;next))&nbsp;&nbsp;</span></li></ol>
<p><span>list_for_each_safe()的定义：<br /></span></p>
<ol><li class="alt"><span>/**&nbsp;&nbsp;</span></li><li><span>&nbsp;*&nbsp;list_for_each_safe&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;iterate&nbsp;over&nbsp;a&nbsp;list&nbsp;safe&nbsp;against&nbsp;removal&nbsp;of&nbsp;list&nbsp;entry&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;*&nbsp;@pos:&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;&amp;struct&nbsp;list_head&nbsp;to&nbsp;use&nbsp;as&nbsp;a&nbsp;loop&nbsp;counter.&nbsp;&nbsp;</span></li><li><span>&nbsp;*&nbsp;@n:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;another&nbsp;&amp;struct&nbsp;list_head&nbsp;to&nbsp;use&nbsp;as&nbsp;temporary&nbsp;storage&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;*&nbsp;@head:&nbsp;&nbsp;&nbsp;the&nbsp;head&nbsp;for&nbsp;your&nbsp;list.&nbsp;&nbsp;</span></li><li><span>&nbsp;*/&nbsp;&nbsp;</span></li><li class="alt"><span>#define&nbsp;list_for_each_safe(pos,&nbsp;n,&nbsp;head)&nbsp;\&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(pos&nbsp;=&nbsp;(head)-&gt;next,&nbsp;n&nbsp;=&nbsp;pos-&gt;next;&nbsp;pos&nbsp;!=&nbsp;(head);&nbsp;\&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos&nbsp;=&nbsp;n,&nbsp;n&nbsp;=&nbsp;pos-&gt;next)&nbsp;&nbsp;</span></li></ol>
<p>由上面两个对比来看，list_for_each_safe()函数比list_for_each()多了一个中间变量n </p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">当在遍历的过程中需要删除结点时，来看一下会出现什么情况：</span></p>
<p><span style="font-size: 16px">list_for_each()：list_del(pos)将pos的前后指针指向undefined state,导致kernel panic，另如果list_del_init(pos)将pos前后指针指向自身，导致死循环。</span></p>
<p><span style="font-size: 16px">list_for_each_safe()：首先将pos的后指针缓存到n，处理一个流程后再赋回pos，避免了这种情况发生。</span></p>
<p><span style="font-size: 16px"><br /></span></p>
<p><span style="font-size: 16px">因此之遍历链表不删除结点时，可以使用list_for_each()，而当由删除结点操作时，则要使用list_for_each_safe()。</span></p>
<p><span style="font-size: 16px">其他带safe的处理也是基于这个原因。</span></p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/mysileng/aggbug/193660.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-22 10:45 <a href="http://www.cppblog.com/mysileng/archive/2012/10/22/193660.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>7.Linux内核设计与实现 P69---深入分析 Linux 内核链表(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/22/193657.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 22 Oct 2012 02:31:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/22/193657.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193657.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/22/193657.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193657.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193657.html</trackback:ping><description><![CDATA[<p sizset="89" sizcache="23"><a name="N10038"><span class="atitle">一、 链表数据结构简介&nbsp;</span></a></p>
<p>链表是一种常用的组织有序数据的数据结构，它通过指针将一系列数据节点连接成一条数据链，是线性表的一种重要实现方式。相对于数组，链表具有更好的动态性，建立链表时无需预先知道数据总量，可以随机分配空间，可以高效地在链表中的任意位置实时插入或删除数据。链表的开销主要是访问的顺序性和组织链的空间损失。</p>
<p>通常链表数据结构至少应包含两个域：数据域和指针域，数据域用于存储数据，指针域用于建立与下一个节点的联系。按照指针域的组织以及各个节点之间的联系形式，链表又可以分为单链表、双链表、循环链表等多种类型，下面分别给出这几类常见链表类型的示意图：</p>
<p sizset="90" sizcache="23"><a name="N10044"><span class="smalltitle">1． 单链表</span></a></p><br /><a name="N1004C"><strong>图1 单链表</strong></a><br /><img border="0" alt="图1 单链表" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image001.gif" width="494" height="168" /> <br />
<p>单链表是最简单的一类链表，它的特点是仅有一个指针域指向后继节点（next），因此，对单链表的遍历只能从头至尾（通常是NULL空指针）顺序进行。</p>
<p sizset="92" sizcache="23"><a name="N10060"><span class="smalltitle">2． 双链表</span></a></p><br /><a name="N10068"><strong>图2 双链表</strong></a><br /><img border="0" alt="图2 双链表" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image002.gif" width="482" height="178" /> <br />
<p>通过设计前驱和后继两个指针域，双链表可以从两个方向遍历，这是它区别于单链表的地方。如果打乱前驱、后继的依赖关系，就可以构成"二叉树"；如果再让首节点的前驱指向链表尾节点、尾节点的后继指向首节点（如图2中虚线部分），就构成了循环链表；如果设计更多的指针域，就可以构成各种复杂的树状数据结构。</p>
<p sizset="94" sizcache="23"><a name="N1007C"><span class="smalltitle">3． 循环链表</span></a></p>
<p>循环链表的特点是尾节点的后继指向首节点。前面已经给出了双循环链表的示意图，它的特点是从任意一个节点出发，沿两个方向的任何一个，都能找到链表中的任意一个数据。如果去掉前驱指针，就是单循环链表。</p>
<p>在Linux内核中使用了大量的链表结构来组织数据，包括设备列表以及各种功能模块中的数据组织。这些链表大多采用在[include/linux/list.h]实现的一个相当精彩的链表数据结构。本文的后继部分就将通过示例详细介绍这一数据结构的组织和使用。</p><br />
<p sizset="96" sizcache="23"><a name="N10088"><span class="atitle">二、 Linux 2.6内核链表数据结构的实现</span></a></p>
<p>尽管这里使用2.6内核作为讲解的基础，但实际上2.4内核中的链表结构和2.6并没有什么区别。不同之处在于2.6扩充了两种链表数据结构：链表的读拷贝更新（rcu）和HASH链表（hlist）。这两种扩展都是基于最基本的list结构，因此，本文主要介绍基本链表结构，然后再简要介绍一下rcu和hlist。</p>
<p>链表数据结构的定义很简单（节选自[include/linux/list.h]，以下所有代码，除非加以说明，其余均取自该文件）：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">struct list_head {
	struct list_head *next, *prev;
};
</pre></td></tr></tbody></table><br />
<p>list_head结构包含两个指向list_head结构的指针prev和next，由此可见，内核的链表具备双链表功能，实际上，通常它都组织成双循环链表。</p>
<p>和第一节介绍的双链表结构模型不同，这里的list_head没有数据域。在Linux内核链表中，不是在链表结构中包含数据，而是在数据结构中包含链表节点。</p>
<p>在数据结构课本中，链表的经典定义方式通常是这样的（以单链表为例）：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">struct list_node {
	struct list_node *next;
	ElemType	data;
};
</pre></td></tr></tbody></table><br />
<p>因为ElemType的缘故，对每一种数据项类型都需要定义各自的链表结构。有经验的C++程序员应该知道，标准模板库中的&lt;list&gt;采用的是C++ Template，利用模板抽象出和数据项类型无关的链表操作接口。</p>
<p>在Linux内核链表中，需要用链表组织起来的数据通常会包含一个struct list_head成员，例如在[include/linux/netfilter.h]中定义了一个nf_sockopt_ops结构来描述Netfilter为某一协议族准备的getsockopt/setsockopt接口，其中就有一个（struct list_head list）成员，各个协议族的nf_sockopt_ops结构都通过这个list成员组织在一个链表中，表头是定义在[net/core/netfilter.c]中的nf_sockopts（struct list_head）。从下图中我们可以看到，这种通用的链表结构避免了为每个数据项类型定义自己的链表的麻烦。Linux的简捷实用、不求完美和标准的风格，在这里体现得相当充分。</p><br /><a name="N100AD"><strong>图3 nf_sockopts链表示意图</strong></a><br /><img border="0" alt="图3 nf_sockopts链表示意图" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image003.gif" width="520" height="313" /><br /><br />
<p sizset="99" sizcache="23"><a name="N100BE"><span class="atitle">三、 链表操作接口</span></a></p>
<p sizset="100" sizcache="23"><a name="N100C4"><span class="smalltitle">1. 声明和初始化</span></a></p>
<p>实际上Linux只定义了链表节点，并没有专门定义链表头，那么一个链表结构是如何建立起来的呢？让我们来看看LIST_HEAD()这个宏：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">#define LIST_HEAD_INIT(name) { &amp;(name), &amp;(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
</pre></td></tr></tbody></table><br />
<p>当我们用LIST_HEAD(nf_sockopts)声明一个名为nf_sockopts的链表头时，它的next、prev指针都初始化为指向自己，这样，我们就有了一个空链表，因为Linux用头指针的next是否指向自己来判断链表是否为空：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline int list_empty(const struct list_head *head)
{
		return head-&gt;next == head;
}
</pre></td></tr></tbody></table><br />
<p>除了用LIST_HEAD()宏在声明的时候初始化一个链表以外，Linux还提供了一个INIT_LIST_HEAD宏用于运行时初始化链表：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">#define INIT_LIST_HEAD(ptr) do { \
	(ptr)-&gt;next = (ptr); (ptr)-&gt;prev = (ptr); \
} while (0)
</pre></td></tr></tbody></table><br />
<p>我们用INIT_LIST_HEAD(&amp;nf_sockopts)来使用它。</p>
<p sizset="101" sizcache="23"><a name="N100E2"><span class="smalltitle">2. 插入/删除/合并</span></a></p>
<p><strong>a) 插入</strong> </p>
<p>对链表的插入操作有两种：在表头插入和在表尾插入。Linux为此提供了两个接口：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline void list_add(struct list_head *new, struct list_head *head);
static inline void list_add_tail(struct list_head *new, struct list_head *head);
</pre></td></tr></tbody></table><br />
<p>因为Linux链表是循环表，且表头的next、prev分别指向链表中的第一个和最末一个节点，所以，list_add和list_add_tail的区别并不大，实际上，Linux分别用</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">__list_add(new, head, head-&gt;next);
</pre></td></tr></tbody></table><br />
<p>和</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">__list_add(new, head-&gt;prev, head);
</pre></td></tr></tbody></table><br />
<p>来实现两个接口，可见，在表头插入是插入在head之后，而在表尾插入是插入在head-&gt;prev之后。</p>
<p>假设有一个新nf_sockopt_ops结构变量new_sockopt需要添加到nf_sockopts链表头，我们应当这样操作：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">list_add(&amp;new_sockopt.list, &amp;nf_sockopts);
</pre></td></tr></tbody></table><br />
<p>从这里我们看出，nf_sockopts链表中记录的并不是new_sockopt的地址，而是其中的list元素的地址。如何通过链表访问到new_sockopt呢？下面会有详细介绍。</p>
<p><strong>b) 删除</strong> </p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline void list_del(struct list_head *entry);
</pre></td></tr></tbody></table><br />
<p>当我们需要删除nf_sockopts链表中添加的new_sockopt项时，我们这么操作：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">list_del(&amp;new_sockopt.list);
</pre></td></tr></tbody></table><br />
<p>被剔除下来的new_sockopt.list，prev、next指针分别被设为LIST_POSITION2和LIST_POSITION1两个特殊值，这样设置是为了保证不在链表中的节点项不可访问--对LIST_POSITION1和LIST_POSITION2的访问都将引起页故障。与之相对应，list_del_init()函数将节点从链表中解下来之后，调用LIST_INIT_HEAD()将节点置为空链状态。</p>
<p><strong>c) 搬移</strong> </p>
<p>Linux提供了将原本属于一个链表的节点移动到另一个链表的操作，并根据插入到新链表的位置分为两类：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline void list_move(struct list_head *list, struct list_head *head);
static inline void list_move_tail(struct list_head *list, struct list_head *head);
</pre></td></tr></tbody></table><br />
<p>例如list_move(&amp;new_sockopt.list,&amp;nf_sockopts)会把new_sockopt从它所在的链表上删除，并将其再链入nf_sockopts的表头。</p>
<p><strong>d) 合并</strong> </p>
<p>除了针对节点的插入、删除操作，Linux链表还提供了整个链表的插入功能：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline void list_splice(struct list_head *list, struct list_head *head);
</pre></td></tr></tbody></table><br />
<p>假设当前有两个链表，表头分别是list1和list2（都是struct list_head变量），当调用list_splice(&amp;list1,&amp;list2)时，只要list1非空，list1链表的内容将被挂接在list2链表上，位于list2和list2.next（原list2表的第一个节点）之间。新list2链表将以原list1表的第一个节点为首节点，而尾节点不变。如图（虚箭头为next指针）：</p><br /><a name="N10146"><strong>图4 链表合并list_splice(&amp;list1,&amp;list2)</strong></a><br /><img border="0" alt="图4 链表合并list_splice(&amp;list1,&amp;list2)" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image004.gif" width="516" height="167" /> <br />
<p>当list1被挂接到list2之后，作为原表头指针的list1的next、prev仍然指向原来的节点，为了避免引起混乱，Linux提供了一个list_splice_init()函数：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">static inline void list_splice_init(struct list_head *list, struct list_head *head);
	</pre></td></tr></tbody></table><br />
<p>该函数在将list合并到head链表的基础上，调用INIT_LIST_HEAD(list)将list设置为空链。</p>
<p sizset="103" sizcache="23"><a name="N10161"><span class="smalltitle">3. 遍历</span></a></p>
<p>遍历是链表最经常的操作之一，为了方便核心应用遍历链表，Linux链表将遍历操作抽象成几个宏。在介绍遍历宏之前，我们先看看如何从链表中访问到我们真正需要的数据项。</p>
<p><strong>a) 由链表节点到数据项变量</strong> </p>
<p>我们知道，Linux链表中仅保存了数据项结构中list_head成员变量的地址，那么我们如何通过这个list_head成员访问到作为它的所有者的节点数据呢？Linux为此提供了一个list_entry(ptr,type,member)宏，其中ptr是指向该数据中list_head成员的指针，也就是存储在链表中的地址值，type是数据项的类型，member则是数据项类型定义中list_head成员的变量名，例如，我们要访问nf_sockopts链表中首个nf_sockopt_ops变量，则如此调用：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">list_entry(nf_sockopts-&gt;next, struct nf_sockopt_ops, list);
</pre></td></tr></tbody></table><br />
<p>这里"list"正是nf_sockopt_ops结构中定义的用于链表操作的节点成员变量名。</p>
<p>list_entry的使用相当简单，相比之下，它的实现则有一些难懂：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">#define list_entry(ptr, type, member) container_of(ptr, type, member)
container_of宏定义在[include/linux/kernel.h]中：
#define container_of(ptr, type, member) ({			\
        const typeof( ((type *)0)-&gt;member ) *__mptr = (ptr);	\
        (type *)( (char *)__mptr - offsetof(type,member) );})
offsetof宏定义在[include/linux/stddef.h]中：
#define offsetof(TYPE, MEMBER) ((size_t) &amp;((TYPE *)0)-&gt;MEMBER)
</pre></td></tr></tbody></table><br />
<p>size_t最终定义为unsigned int（i386）。</p>
<p>这里使用的是一个利用编译器技术的小技巧，即先求得结构成员在与结构中的偏移量，然后根据成员变量的地址反过来得出属主结构变量的地址。</p>
<p>container_of()和offsetof()并不仅用于链表操作，这里最有趣的地方是((type *)0)-&gt;member，它将0地址强制"转换"为type结构的指针，再访问到type结构中的member成员。在container_of宏中，它用来给typeof()提供参数（typeof()是gcc的扩展，和sizeof()类似），以获得member成员的数据类型；在offsetof()中，这个member成员的地址实际上就是type数据结构中member成员相对于结构变量的偏移量。</p>
<p>如果这么说还不好理解的话，不妨看看下面这张图：</p><br /><a name="N1018F"><strong>图5 offsetof()宏的原理</strong></a><br /><img border="0" alt="图5 offsetof()宏的原理" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image005.gif" width="432" height="178" /> <br />
<p>对于给定一个结构，offsetof(type,member)是一个常量，list_entry()正是利用这个不变的偏移量来求得链表数据项的变量地址。</p>
<p><strong>b) 遍历宏</strong> </p>
<p>在[net/core/netfilter.c]的nf_register_sockopt()函数中有这么一段话：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">		&#8230;&#8230;
struct list_head *i;
&#8230;&#8230;
	list_for_each(i, &amp;nf_sockopts) {
		struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i;
		&#8230;&#8230;
	}
	&#8230;&#8230;
	</pre></td></tr></tbody></table><br />
<p>函数首先定义一个(struct list_head *)指针变量i，然后调用list_for_each(i,&amp;nf_sockopts)进行遍历。在[include/linux/list.h]中，list_for_each()宏是这么定义的：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">        	#define list_for_each(pos, head) \
	for (pos = (head)-&gt;next, prefetch(pos-&gt;next); pos != (head); \
        	pos = pos-&gt;next, prefetch(pos-&gt;next))
        	</pre></td></tr></tbody></table><br />
<p>它实际上是一个for循环，利用传入的pos作为循环变量，从表头head开始，逐项向后（next方向）移动pos，直至又回到head（prefetch()可以不考虑，用于预取以提高遍历速度）。</p>
<p>那么在nf_register_sockopt()中实际上就是遍历nf_sockopts链表。为什么能直接将获得的list_head成员变量地址当成struct nf_sockopt_ops数据项变量的地址呢？我们注意到在struct nf_sockopt_ops结构中，list是其中的第一项成员，因此，它的地址也就是结构变量的地址。更规范的获得数据变量地址的用法应该是：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">struct nf_sockopt_ops *ops = list_entry(i, struct nf_sockopt_ops, list);
</pre></td></tr></tbody></table><br />
<p>大多数情况下，遍历链表的时候都需要获得链表节点数据项，也就是说list_for_each()和list_entry()总是同时使用。对此Linux给出了一个list_for_each_entry()宏：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">#define list_for_each_entry(pos, head, member)		&#8230;&#8230;
</pre></td></tr></tbody></table><br />
<p>与list_for_each()不同，这里的pos是数据项结构指针类型，而不是(struct list_head *)。nf_register_sockopt()函数可以利用这个宏而设计得更简单：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">&#8230;&#8230;
struct nf_sockopt_ops *ops;
list_for_each_entry(ops,&amp;nf_sockopts,list){
	&#8230;&#8230;
}
&#8230;&#8230;
</pre></td></tr></tbody></table><br />
<p>某些应用需要反向遍历链表，Linux提供了list_for_each_prev()和list_for_each_entry_reverse()来完成这一操作，使用方法和上面介绍的list_for_each()、list_for_each_entry()完全相同。</p>
<p>如果遍历不是从链表头开始，而是从已知的某个节点pos开始，则可以使用list_for_each_entry_continue(pos,head,member)。有时还会出现这种需求，即经过一系列计算后，如果pos有值，则从pos开始遍历，如果没有，则从链表头开始，为此，Linux专门提供了一个list_prepare_entry(pos,head,member)宏，将它的返回值作为list_for_each_entry_continue()的pos参数，就可以满足这一要求。</p>
<p sizset="105" sizcache="23"><a name="N101D5"><span class="smalltitle">4. 安全性考虑</span></a></p>
<p>在并发执行的环境下，链表操作通常都应该考虑同步安全性问题，为了方便，Linux将这一操作留给应用自己处理。Linux链表自己考虑的安全性主要有两个方面：</p>
<p><strong>a) list_empty()判断</strong> </p>
<p>基本的list_empty()仅以头指针的next是否指向自己来判断链表是否为空，Linux链表另行提供了一个list_empty_careful()宏，它同时判断头指针的next和prev，仅当两者都指向自己时才返回真。这主要是为了应付另一个cpu正在处理同一个链表而造成next、prev不一致的情况。但代码注释也承认，这一安全保障能力有限：除非其他cpu的链表操作只有list_del_init()，否则仍然不能保证安全，也就是说，还是需要加锁保护。</p>
<p><strong>b) 遍历时节点删除</strong> </p>
<p>前面介绍了用于链表遍历的几个宏，它们都是通过移动pos指针来达到遍历的目的。但如果遍历的操作中包含删除pos指针所指向的节点，pos指针的移动就会被中断，因为list_del(pos)将把pos的next、prev置成LIST_POSITION2和LIST_POSITION1的特殊值。</p>
<p>当然，调用者完全可以自己缓存next指针使遍历操作能够连贯起来，但为了编程的一致性，Linux链表仍然提供了两个对应于基本遍历操作的"_safe"接口：list_for_each_safe(pos, n, head)、list_for_each_entry_safe(pos, n, head, member)，它们要求调用者另外提供一个与pos同类型的指针n，在for循环中暂存pos下一个节点的地址，避免因pos节点被释放而造成的断链。</p><br />
<p sizset="107" sizcache="23"><a name="N101F3"><span class="atitle">四、 扩展</span></a></p>
<p sizset="108" sizcache="23"><a name="N101F9"><span class="smalltitle">1. hlist</span></a></p><br /><a name="N10201"><strong>图6 list和hlist</strong></a><br /><img border="0" alt="图6 list和hlist" src="http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/image006.gif" width="421" height="303" /> <br />
<p>精益求精的Linux链表设计者（因为list.h没有署名，所以很可能就是Linus Torvalds）认为双头（next、prev）的双链表对于HASH表来说"过于浪费"，因而另行设计了一套用于HASH表应用的hlist数据结构--单指针表头双循环链表，从上图可以看出，hlist的表头仅有一个指向首节点的指针，而没有指向尾节点的指针，这样在可能是海量的HASH表中存储的表头就能减少一半的空间消耗。</p>
<p>因为表头和节点的数据结构不同，插入操作如果发生在表头和首节点之间，以往的方法就行不通了：表头的first指针必须修改指向新插入的节点，却不能使用类似list_add()这样统一的描述。为此，hlist节点的prev不再是指向前一个节点的指针，而是指向前一个节点（可能是表头）中的next（对于表头则是first）指针（struct list_head **pprev），从而在表头插入的操作可以通过一致的"*(node-&gt;pprev)"访问和修改前驱节点的next（或first）指针。</p>
<p sizset="110" sizcache="23"><a name="N10218"><span class="smalltitle">2. read-copy update</span></a></p>
<p>在Linux链表功能接口中还有一系列以"_rcu"结尾的宏，与以上介绍的很多函数一一对应。RCU（Read-Copy Update）是2.5/2.6内核中引入的新技术，它通过延迟写操作来提高同步性能。</p>
<p>我们知道，系统中数据读取操作远多于写操作，而rwlock机制在smp环境下随着处理机增多性能会迅速下降（见参考资料4）。针对这一应用背景，IBM Linux技术中心的Paul E. McKenney提出了"读拷贝更新"的技术，并将其应用于Linux内核中。RCU技术的核心是写操作分为写-更新两步，允许读操作在任何时候无阻访问，当系统有写操作时，更新动作一直延迟到对该数据的所有读操作完成为止。Linux链表中的RCU功能只是Linux RCU的很小一部分，对于RCU的实现分析已超出了本文所及，有兴趣的读者可以自行参阅本文的参考资料；而对RCU链表的使用和基本链表的使用方法基本相同。</p><br /><br />
<p sizset="112" sizcache="23"><a name="N10224"><span class="atitle">五、 示例</span></a></p>
<p sizset="113" sizcache="23"><a href="">附件</a>中的程序除了能正向、反向输出文件以外，并无实际作用，仅用于演示Linux链表的使用。 </p>
<p>为了简便，例子采用的是用户态程序模板，如果需要运行，可采用如下命令编译：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">gcc -D__KERNEL__ -I/usr/src/linux-2.6.7/include pfile.c -o pfile
</pre></td></tr></tbody></table><br />
<p>因为内核链表限制在内核态使用，但实际上对于数据结构本身而言并非只能在核态运行，因此，在笔者的编译中使用"-D__KERNEL__"开关"欺骗"编译器。</p><!-- CMA ID: 23090 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-document-html-6.0.xsl --><br />
<p sizset="114" sizcache="23"><a name="resources"><span class="atitle">参考资料 </span></a></p>
<ol sizset="115" sizcache="23"><li sizset="115" sizcache="23">维基百科 <a href="">http://zh.wikipedia.org</a>，一个在GNU Documentation License下发布的网络辞典，自由软件理念的延伸，本文的"链表"概念即使用它的版本。</li><li>《Linux内核情景分析》，毛德操先生的这本关于Linux内核的巨著几乎可以回答绝大部分关于内核的问题，其中也包括内核链表的几个关键数据结构。</li><li>Linux内核2.6.7源代码，所有不明白的问题，只要潜心看代码，总能清楚。</li><li sizset="116" sizcache="23">Kernel Korner: Using RCU in the Linux 2.5 Kernel，RCU主要开发者Paul McKenney 2003年10月发表于Linux Journal上的一篇介绍RCU的文章。在 <a href="">http://www.rdrop.com/users/paulmck/rclock/</a>上可以获得更多关于RCU的帮助。 </li></ol>
<p sizset="117" sizcache="23"><a name="author"><span class="atitle">关于作者</span></a></p>
<div class="ibm-container ibm-portrait-module ibm-alternate-two" sizset="118" sizcache="23">
<div class="ibm-container-body" sizset="118" sizcache="23">
<p sizset="118" sizcache="23"><a name="author1"></a>杨沙洲，目前在国防科技大学计算机学院攻读软件方向博士学位。对文中存在的技术问题，欢迎向 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#112;&#117;&#98;&#98;&#64;&#49;&#54;&#51;&#46;&#110;&#101;&#116;&#63;&#99;&#99;&#61;&#112;&#117;&#98;&#98;&#64;&#49;&#54;&#51;&#46;&#110;&#101;&#116;">pubb@163.net</a>质疑。 </p></div></div><img src ="http://www.cppblog.com/mysileng/aggbug/193657.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-22 10:31 <a href="http://www.cppblog.com/mysileng/archive/2012/10/22/193657.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>5.Linux内核设计与实现 P39---linux2.6 CFS调度算法分析(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/16/193380.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Tue, 16 Oct 2012 08:30:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/16/193380.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193380.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/16/193380.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193380.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193380.html</trackback:ping><description><![CDATA[<strong style="word-wrap: break-word; ">1.概述</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS（completely fair schedule）是最终被内核采纳的调度器。它从RSDL/SD中吸取了完全公平的思想，不再跟踪进程的睡眠时间，也不再企图区分交互式进程。它将所有的进程都统一对待，这就是公平的含义。CFS的算法和实现都相当简单，众多的测试表明其性能也非常优越。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 背后的主要想法是维护为</span>任务提供处理器时间方面的平衡（公平性）<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">。这意味着应给进程分配相当数量的处理器。分给某个任务的时间失去平衡时（意味着一个或多个任务相对于其他任务而言未被给予相当数量的时间），应给失去平衡的任务分配时间，让其执行。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS抛弃了时间片，抛弃了复杂的算法，从一个新的起点开始了调度器的新时代，最开始的2.6.23版本，CFS提供一个</span>虚拟的时钟<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">，所有进程复用这个虚拟时钟的时间，</span>CFS将时钟的概念从底层体系相关的硬件中抽象出来，进程调度模块直接和这个虚拟的时钟接口 而不必再为硬件时钟操作而操心<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">，如此一来，整个进程调度模块就完整了，从时钟到调度算法，到不同进程的不同策略，全部都由虚拟系统提供，也正是在这个新的内核，引入了调度类。因此新的调度器就是不同特性的进程在统一的虚拟时钟下按照不同的策略被调度。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按照作者Ingo Molnar的说法："CFS百分之八十的工作可以用一句话概括：</span>CFS在真实的硬件上模拟了完全理想的多任务处理器<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">"。在&#8220;完全理想的多任务处理器 &#8220;下，</span>每个进程都能同时获得CPU的执行时间<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">。当系统中有两个进程时，CPU的计算时间被分成两份，每个进程获得50%。然而在实际的硬件上，当一个进程占用CPU时，其它进程就必须等待。这就产生了不公平。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">2.相关概念</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " />调度实体(sched entiy)<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">：就是调度的对象，可以理解为进程。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " />虚拟运行时间(vruntime)<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">：即每个调度实体的运行时间。任务的虚拟运行时间越小， 意味着任务被允许访问服务器的时间越短 &#8212; 其对处理器的需求越高。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " />公平调度队列(cfs_rq)<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">：采取公平调度的调度实体的运行队列。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">3.CFS的核心思想</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 全公平调度器（CFS）的设计思想是：在一个真实的硬件上模型化一个理想的、精确的多任务CPU。该理想CPU模型运行在100%的负荷、在精确 平等速度下并行运行每个任务，每个任务运行在1/n速度下，即理想CPU有n个任务运行，每个任务的速度为CPU整个负荷的1/n。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于真实硬件上，每次只能运行一个任务，这就得引入"虚拟运行时间"（virtual runtime）的概念，虚拟运行时间为一个任务在理想CPU模型上执行的下一个时间片(timeslice)。实际上，一个任务的虚拟运行时间为考虑到运行任务总数的实际运行时间。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 背后的主要想法是维护为</span>任务提供处理器时间方面的平衡（公平性）。<span style="font-family: 宋体, Arial; background-color: #fffcf3; ">CFS为了体现的公平表现在2个方面</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">(1)进程的运行时间相等</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 在叫做</span><em style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; ">虚拟运行时</em><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;的地方维持提供给某个任务的时间量。任务的虚拟运行时越小， 意味着任务被允许访问服务器的时间越短 &#8212; 其对处理器的需求越高。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假设runqueue中有n个进程，当前进程运行了 10ms。在&#8220;完全理想的多任务处理器&#8221;中，10ms应该平分给n个进程(不考虑各个进程的nice值)，因此当前进程应得的时间是(10/n)ms，但 是它却运行了10ms。所以CFS将惩罚当前进程，使其它进程能够在下次调度时尽可能取代当前进程。最终实现所有进程的公平调度。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">(2)睡眠的进程进行补偿</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 还包含睡眠公平概念以便确保那些目前没有运行的任务（例如，等待 I/O）在其最终需要时获得相当份额的处理器。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS调度器的运行时间是O(logN)，而以前的调度器的运行时间是O(1)，这是不是就是说CFS的效率比O(1)的更差呢？</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 答案并不是那样，我们知道 CFS调度器下的运行队列是基于红黑树组织的，找出下一个进程就是截下左下角的节点，固定时间完成，所谓的O(logN)指的是插入时间，可是红黑树的统 计性能是不错的，没有多大概率真的用得了那么多时间，因为红节点和黑节点的特殊排列方式既保证了树的一定程度的平衡，又不至于花太多的时间来维持这种平 衡，插入操作大多数情况下都可以很快的完成，特别是对于组织得相当好的数据。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">4.CFS的实现</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">4.1 2.6.23 VS 2.6.25</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在2.6.23内核中，刚刚实现的CFS调度器显得很淳朴，每次的时钟滴答中都会将当前进程先出队，推进其虚拟时钟和系统虚拟时钟后再入队，然后判断红黑 树的左下角的进程是否还是当前进程而抉择是否要调度，这种调度器的key的计算是用当前的虚拟时钟减去待计算进程的等待时间，如果该计算进程在运行，那么其等待时间就是负值，这样，等待越长的进程key越小，从而越容易被选中投入运行；</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在2.6.25内核以后实现了一种更为简单的方式，就是设置一个运行队列的虚拟时钟，它单调增长并且跟踪该队列的最小虚拟时钟的进程，key值由进程的vruntime和队列的虚拟时钟的差值计算，这种方式就是真正的追赶， 比2.6.23实现的简单，但是很巧妙，不必在每次时钟滴答中都将当前进程出队，入队，而是根据当前进程实际运行的时间和理想应该运行的时间判断是否应该调度。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">4.2红黑树</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 与之前的 Linux 调度器不同，它没有将任务维护在运行队列中，CFS 维护了一个以时间为顺序的红黑树（参见下图）。&nbsp;</span><em style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; ">红黑树</em><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;是一个树，具有很多有趣、有用的属性。首先，它是自平衡的，这意味着树上没有路径比任何其他路径长两倍以上。 第二，树上的运行按 O(log&nbsp;</span><em style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; ">n</em><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">) 时间发生（其中&nbsp;</span><em style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; ">n</em><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;是树中节点的数量）。这意味着您可以快速高效地插入或删除任务。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><a href="http://blog.chinaunix.net/attachment/201109/19/122937_1316415385i84g.gif" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #fffcf3; "><img src="http://blog.chinaunix.net/attachment/201109/19/122937_1316415385i84g.gif"  .="" load="imgResize(this, 650);"  ;="" border="0" style="word-wrap: break-word; border: 0px; max-width: 650px; " alt="" /></a><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 任务存储在以时间为顺序的红黑树中（由 sched_entity 对象表示），对处理器需求最多的任务 （最低虚拟运行时）存储在树的左侧，处理器需求最少的任务（最高虚拟运行时）存储在树的右侧。 为了公平，调度器先选取红黑树最左端的节点调度为下一个以便保持公平性。任务通过将其运行时间添加到虚拟运行时， 说明其占用 CPU 的时间，然后如果可运行，再插回到树中。这样，树左侧的任务就被给予时间运行了，树的内容从右侧迁移到左侧以保持公平。 因此，每个可运行的任务都会追赶其他任务以维持整个可运行任务集合的执行平衡。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">4.3 CFS内部原理</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux 内的所有任务都由称为 task_struct 的任务结构表示。该结构完整地描述了任务并包括了任务的当前状态、其堆栈、进程标识、优先级（静态和动态）等等。您可以在 ./linux/include/linux/sched.h 中找到这些内容以及相关结构。 但是因为不是所有任务都是可运行的，您在 task_struct 中不会发现任何与 CFS 相关的字段。 相反，会创建一个名为 sched_entity 的新结构来跟踪调度信息（参见下图）。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><a href="http://blog.chinaunix.net/attachment/201109/19/122937_1316416769MTZY.gif" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #fffcf3; "><img src="http://blog.chinaunix.net/attachment/201109/19/122937_1316416769MTZY.gif"  .="" load="imgResize(this, 650);"  ;="" border="0" style="word-wrap: break-word; border: 0px; max-width: 650px; " alt="" /></a><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 树的根通过 rb_root 元素通过 cfs_rq 结构（在 ./kernel/sched.c 中）引用。红黑树的叶子不包含信息，但是内部节点代表一个或多个可运行的任务。红黑树的每个节点都由 rb_node 表示，它只包含子引用和父对象的颜色。 rb_node 包含在 sched_entity 结构中，该结构包含 rb_node 引用、负载权重以及各种统计数据。最重要的是， sched_entity 包含 vruntime（64 位字段），它表示任务运行的时间量，并作为红黑树的索引。 最后，task_struct 位于顶端，它完整地描述任务并包含 sched_entity 结构。&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 调度函数非常简单。 在 ./kernel/sched.c 中的 schedule() 函数中，它会先抢占当前运行任务（除非它通过 yield() 代码先抢占自己）。注意 CFS 没有真正的时间切片概念用于抢占，因为抢占时间是可变的。 当前运行任务（现在被抢占的任务）通过对 put_prev_task 调用（通过调度类）返回到红黑树。 当 schedule 函数开始确定下一个要调度的任务时，它会调用 pick_next_task 函数。此函数也是通用的（在 ./kernel/sched.c 中），但它会通过调度器类调用 CFS 调度器。 CFS 中的 pick_next_task 函数可以在 ./kernel/sched_fair.c（称为 pick_next_task_fair()）中找到。 此函数只是从红黑树中获取最左端的任务并返回相关 sched_entity。通过此引用，一个简单的 task_of() 调用确定返回的 task_struct 引用。通用调度器最后为此任务提供处理器。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><strong style="word-wrap: break-word; ">4.4 CFS的优先级</strong><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #fffcf3; " /><span style="font-family: 宋体, Arial; background-color: #fffcf3; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFS 不直接使用优先级而是将其用作允许任务执行的时间的衰减系数。 低优先级任务具有更高的衰减系数，而高优先级任务具有较低的衰减系数。 这意味着与高优先级任务相比，低优先级任务允许任务执行的时间消耗得更快。 这是一个绝妙的解决方案，可以避免维护按优先级调度的运行队列。</span><img src ="http://www.cppblog.com/mysileng/aggbug/193380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-16 16:30 <a href="http://www.cppblog.com/mysileng/archive/2012/10/16/193380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>4.Linux内核设计与实现 P31---浅析进程终结关键do_exit(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/15/193299.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 15 Oct 2012 03:52:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/15/193299.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193299.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/15/193299.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193299.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193299.html</trackback:ping><description><![CDATA[<p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">进程在退出时，必须释放它所拥有的资源，并通过某种方式告诉父进程。进程的退出一般是显示或隐式地调用了eixt(),或者接受了某种信号。不过什么原因退出，最终都调用了do_exit。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; "><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; "><img src="http://my.csdn.net/uploads/201204/20/1334931530_6886.png" alt="" style="border: none; " /><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; "><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">用于进程退出的系统调用有两个exit和exit_group，exit只是终止某个进程，而exit_group整个线程中的进程。它们在内核中的服务函数分别为sys_exit和sys_exit_group，它们又分别调用了do_exit和do_group_exit。而do_group最终又调用了do_exit。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; ">&nbsp;</p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">do_exit定义在kernel/exit.c中：</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">僵死进程：僵死进程是一个进程已经退出，它的内存和资源已经释放掉了，但是位了时系统在它退出后能够获得它的退出状态等信息，它的进程描述符仍然保留。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">一个进程退出时，它的父进程会接收到一个SIGCHLD信号，一般情况下这个信号的处理函数会执行wait系列函数等待子进程的结束。从子进程退出到父进程调用wait(子进程结束)的这段时间，子进程称为僵死进程。执行ps &#8211;ef命令以&#8220;z&#8221;结尾的为僵死进程。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; ">&nbsp;</p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">僵死进程很特殊，它没有任何可执行代码，不会被调度，只有一个进程描述符用来记录退出等状态，除此之外不再占用其他任何资源。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; ">&nbsp;</p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">如果僵死进程的父进程没有调用wait，则该进程会一直处于僵死状态。如果父进程结束，内核会在当前线程组里为其找一个父进程，如果没找到则把init作为其父进程，此时新的父进程将负责清楚其进程。如果父进程一直不结束，该进程会一直僵死。在root下用kill -9 也不能将其杀死。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; "><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff; "><span style="font-family: 'Courier New'; font-size: 18px; ">下面只对do_exit重点地方解析下：</span>&nbsp;</p><br /><ol start="1" style="margin: 0px 0px 1px 45px !important; padding: 0px; border: none; list-style-position: initial; list-style-image: initial; background-color: #ffffff; color: #5c5c5c; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 26px; "><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; "><span style="margin: 0px; padding: 0px; border: none; background-color: inherit; "><p style="margin: 0px; font-size: 14px; padding: 0px; font-family: Arial; line-height: 26px; "><span style="margin: 0px; padding: 0px; border: none; color: #006699; font-weight: bold; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 24px; ">struct</span><span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 24px; ">&nbsp;task_struct&nbsp;*tsk&nbsp;=&nbsp;current;</span><span style="margin: 0px; padding: 0px; border: none; color: #008200; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 24px; ">//获取当前要释放进程的进程描述符</span><span style="margin: 0px; padding: 0px; border: none; font-family: Consolas, 'Courier New', Courier, mono, serif; font-size: 12px; line-height: 24px; ">&nbsp;&nbsp;</span>&nbsp;<span style="font-family: 'Courier New'; font-size: 18px; "><br /></span></p><br style="font-family: verdana, 'courier new'; font-size: 14px; line-height: 21px; " /><span style="margin: 0px; padding: 0px; border: none; ">exit_signals(tsk);&nbsp;&nbsp;</span><span style="margin: 0px; padding: 0px; border: none; color: #008200; ">/*&nbsp;sets&nbsp;PF_EXITING&nbsp;以免内和其他部分访问该进程*/</span><span style="margin: 0px; padding: 0px; border: none; ">&nbsp;&nbsp;</span>&nbsp;<br /><br />exit_mm(tsk);&nbsp;&nbsp;</span></span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;<span style="margin: 0px; padding: 0px; border: none; color: #006699; background-color: inherit; font-weight: bold; ">if</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit; ">&nbsp;(group_dead)&nbsp;&nbsp;</span></span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acct_process();&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;trace_sched_process_exit(tsk);&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;exit_sem(tsk);&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;exit_files(tsk);&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;exit_fs(tsk);&nbsp;&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;check_stack_usage();&nbsp;&nbsp;<br /><br /></span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; "><span style="margin: 0px; padding: 0px; border: none; color: #008200; background-color: inherit; ">/*更新父子关系，并告诉父进程正在退出*/</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit; ">&nbsp;&nbsp;</span></span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;exit_notify(tsk,&nbsp;group_dead);</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; "><span style="margin: 0px; padding: 0px; border: none; color: #008200; background-color: inherit; ">/*切换到其他进程*/</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit; ">&nbsp;&nbsp;</span></span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; background-color: #f8f8f8; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;schedule();&nbsp;</span></li><li style="margin: 0px !important; padding: 0px 3px 0px 10px !important; border-style: none none none solid; border-left-width: 3px; border-left-color: #6ce26c; list-style: decimal-leading-zero outside; color: inherit; line-height: 24px; "><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit; ">&nbsp;&nbsp;&nbsp;&nbsp;exit_thread(); &nbsp;<br /><br /><br /></span></li></ol><img src ="http://www.cppblog.com/mysileng/aggbug/193299.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-15 11:52 <a href="http://www.cppblog.com/mysileng/archive/2012/10/15/193299.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>3.Linux内核设计与实现 P27---浅析进程创建的写时拷贝(转) </title><link>http://www.cppblog.com/mysileng/archive/2012/10/15/193297.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 15 Oct 2012 03:18:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/15/193297.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193297.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/15/193297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193297.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193297.html</trackback:ping><description><![CDATA[<span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">今天看到写时拷贝这个概念，当时一下没有理解，后来查看一些网上的资料，找到了这篇文章，里面的那份个小程序能够很好的说明进程创建写时拷贝的概念。怕以后找不到就转载了。嘿嘿。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">下面是那篇文章的原文：</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><a href="http://whatis.ctocio.com.cn/searchwhatis/210/5948710.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">Linux</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">进程创建，子</span><a href="http://whatis.ctocio.com.cn/searchwhatis/292/7333792.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">进程</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">对 父进程资源&#8220;写时拷贝&#8221;的证明&nbsp; &nbsp;&nbsp;&nbsp;传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下，因为它拷贝的数据或许可以共享(This approach is significantly na?ve and inefficient in that it copies much&nbsp;</span><a href="http://whatis.ctocio.com.cn/searchwhatis/99/5947599.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">data</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp;that might otherwise be shared.)。更糟糕的是，如果新进程打算立即执行一个新的映像，那么所有的拷贝都将前功尽弃。</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; Linux的fork()使用写时拷贝 (copy- on-write)页实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。</span><a href="http://www.xxlinux.com/linux/article/development/kernel/" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">内核</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">此 时并不复制整个进程的地址空间，而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间，从而使各个进行拥有各自的地址空间。也就是 说，资源的复制是在需要写入的时候才会进行，在此之前，只有以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候。在页根本不会被 写入的情况下---例如，fork()后立即执行exec()，地址空间就无需被复制了。fork()的实际开销就是复制父进程的页表以及给子进程创建一 个进程描述符。下列程序可证明写时拷贝：<br /></span><span style="font-family: 宋体, Arial; background-color: #f5f7f8; "><br />#include &lt;stdio.h&gt;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">#include &lt;sched.h&gt;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><a href="http://whatis.ctocio.com.cn/searchwhatis/69/6025569.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">int</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp;data = 10;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">int child_process()</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">{</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; printf("Child&nbsp;</span><a href="http://whatis.ctocio.com.cn/searchwhatis/312/6026312.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; font-family: 宋体, Arial; background-color: #f5f7f8; ">process</a><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp;%d, data %dn",getpid(),data);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; data = 20;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; printf("Child process %d, data %dn",getpid(),data);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; while(1);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">}</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">int main(int argc, char* argv[])</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">{</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; if(fork()==0) {</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; &nbsp; child_process();&nbsp; &nbsp;&nbsp;</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; }</span><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">else{</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;sleep(1);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;printf("Parent process %d, data %dn",getpid(), data);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;while(1);</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; }</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">}</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">运行结果</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">Child process 6427, data 10</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">Child process 6427, data 20</span><br style="word-wrap: break-word; font-family: 宋体, Arial; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; background-color: #f5f7f8; ">Parent process 6426, data 10</span>&nbsp;<br /><br /><p style="word-wrap: break-word; margin: 0px; padding: 5px 0px 0px; line-height: 1.5; font-family: 宋体, Arial; background-color: #f5f7f8; ">&nbsp; &nbsp; 第1个Child process 6427, data 10是因为子进程创建时task_struct的mm直接拷贝自parent的mm；第2个Child process 6427, data 20是因为子进程进行了&#8220;写时拷贝&#8221;，有了自己的dataa；第3个Parent process 6426, data 10输出10是因为子进程的data和父进程的data不是同一份。<br style="word-wrap: break-word; " />&nbsp; &nbsp; 如果把上述程序改为：<br style="word-wrap: break-word; " /></p><table bgcolor="#ffffff" cellspacing="0" style="word-wrap: break-word; color: #000000; font-family: 宋体, Arial; font-size: 12px; line-height: 15px; text-align: start; width: 537px; "><tbody style="word-wrap: break-word; "><tr style="word-wrap: break-word; "><td style="word-wrap: break-word; line-height: 1.5; ">#include &lt;stdio.h&gt;<br style="word-wrap: break-word; " />#include &lt;sched.h&gt;<br style="word-wrap: break-word; " />#include &lt;stdlib.h&gt;<br style="word-wrap: break-word; " /><br style="word-wrap: break-word; " />int data = 10;<br style="word-wrap: break-word; " /><br style="word-wrap: break-word; " />int child_process()<br style="word-wrap: break-word; " />{<br style="word-wrap: break-word; " />&nbsp; &nbsp; printf("Child process %d, data %dn",getpid(),data);<br style="word-wrap: break-word; " />&nbsp; &nbsp; data = 20;<br style="word-wrap: break-word; " />&nbsp; &nbsp; printf("Child process %d, data %dn",getpid(),data);<br style="word-wrap: break-word; " />&nbsp; &nbsp; while(1);<br style="word-wrap: break-word; " />}<br style="word-wrap: break-word; " /><br style="word-wrap: break-word; " />int main(int argc, char* argv[])<br style="word-wrap: break-word; " />{<br style="word-wrap: break-word; " />&nbsp; &nbsp; void **child_stack;<br style="word-wrap: break-word; " />&nbsp; &nbsp; child_stack = (void **) malloc(16384);<br style="word-wrap: break-word; " />&nbsp; &nbsp;&nbsp;<a href="http://whatis.ctocio.com.cn/searchwhatis/241/5947241.shtml" target="_blank" style="word-wrap: break-word; text-decoration: none; color: #63401b; ">clone</a>(child_process, child_stack, CLONE_VM|CLONE_FILES|CLONE_SIGHAND, NULL);<br style="word-wrap: break-word; " /><br style="word-wrap: break-word; " />&nbsp; &nbsp; sleep(1);<br style="word-wrap: break-word; " />&nbsp; &nbsp; printf("Parent process %d, data %dn",getpid(), data);<br style="word-wrap: break-word; " />&nbsp; &nbsp; while(1);<br style="word-wrap: break-word; " />}<br style="word-wrap: break-word; " /></td></tr></tbody></table><br style="word-wrap: break-word; font-family: 宋体, Arial; font-size: 12px; line-height: 15px; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; font-size: 12px; line-height: 15px; background-color: #f5f7f8; ">运行结果将是</span><br style="word-wrap: break-word; font-family: 宋体, Arial; font-size: 12px; line-height: 15px; background-color: #f5f7f8; " /><table bgcolor="#ffffff" cellspacing="0" style="word-wrap: break-word; color: #000000; font-family: 宋体, Arial; font-size: 12px; line-height: 15px; text-align: start; width: 537px; "><tbody style="word-wrap: break-word; "><tr style="word-wrap: break-word; "><td style="word-wrap: break-word; line-height: 1.5; ">Child process 6443, data 10<br style="word-wrap: break-word; " />Child process 6443, data 20<br style="word-wrap: break-word; " />Parent process 6442, data 20<br style="word-wrap: break-word; " /></td></tr></tbody></table><br style="word-wrap: break-word; font-family: 宋体, Arial; font-size: 12px; line-height: 15px; background-color: #f5f7f8; " /><span style="font-family: 宋体, Arial; font-size: 12px; line-height: 15px; background-color: #f5f7f8; ">&nbsp; &nbsp; 由于使用了CLONE_VM创建进程，子进程的mm实际直接指向父进程的mm，所以data是同一份。改变父子进程的data都会互相看到。</span>&nbsp;<img src ="http://www.cppblog.com/mysileng/aggbug/193297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-15 11:18 <a href="http://www.cppblog.com/mysileng/archive/2012/10/15/193297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.Linux内核设计与实现 P25---浅析遍历子进程方法（利用list_for_each）(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/15/193294.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 15 Oct 2012 02:57:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/15/193294.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193294.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/15/193294.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193294.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193294.html</trackback:ping><description><![CDATA[<span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">list_for_each遍历子进程方法，顺便分析下container_of宏的实现过程</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">Linux系统中的每个进程都有一个父进程（init进程除外）；每个进程还有0个或多个子进程。在进程描述符中parent指针指向其父进程，还有一个名为children的子进程链表（父进程task_struct中的children相当于链表的表头）。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">而我们可以使用list_for_each(/include/linux/list.h)来依次遍历访问子进程：</span><div style="word-wrap: break-word; overflow: hidden; margin: 10px 0px; padding-top: 10px; padding-bottom: 5px; padding-left: 10px; background-image: url(http://cu.img168.net/static/image/common//codebg.gif); background-color: #f7f7f7; zoom: 1; border: 1px solid #cccccc; font-family: song, Verdana; line-height: 22.383333206176758px; background-position: 0px 0px; background-repeat: no-repeat repeat; "><div id="code_9p5" style="word-wrap: break-word; "><ol style="word-wrap: break-word; margin: 0px 0px 0px 10px !important; padding: 0px !important; "><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">struct task_struct *task;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">struct list_head *list;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">list_for_each(list, &amp;current-&gt;children) {<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp;&nbsp; &nbsp;task = list_entry(list, struct task_struct, sibling);<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">}</li></ol></div></div><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">其中task即为某个子进程的地址</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; color: red; ">首先需要说明一点task_struct中的children指针指向其某个子进程的进程描述符task_struct中children的地址，而非直接指向某个子进程的地址，也就是说子进程链表中存放的仅仅是各个task_struct成员children的地址。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">我们查看源文件找到list_for_each的定义：</span><div style="word-wrap: break-word; overflow: hidden; margin: 10px 0px; padding-top: 10px; padding-bottom: 5px; padding-left: 10px; background-image: url(http://cu.img168.net/static/image/common//codebg.gif); background-color: #f7f7f7; zoom: 1; border: 1px solid #cccccc; font-family: song, Verdana; line-height: 22.383333206176758px; background-position: 0px 0px; background-repeat: no-repeat repeat; "><div id="code_DbC" style="word-wrap: break-word; "><ol style="word-wrap: break-word; margin: 0px 0px 0px 10px !important; padding: 0px !important; "><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#define list_for_each(pos, head) \<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; for (pos = (head)-&gt;next; prefetch(pos-&gt;next), pos != (head); \<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; pos = pos-&gt;next)</li></ol></div></div><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">从上可以看出list_for_each其实就是一个for循环，在网上看到prefetch()是一个预抓取的函数，我并不理解它（哪位大牛知道的讲下哦</span><img src="http://bbs.chinaunix.net/static/image/smiley/grapeman/09.gif" smilieid="190" border="0" alt="" style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">），不过这个对for（）并没有多大的影响。for()实现的就是一个children链表的遍历，而由children的地址如何取到task_struct的地址呢，它是由list_entry宏来实现的。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">我们先给出所需函数或宏的源代码</span><div style="word-wrap: break-word; overflow: hidden; margin: 10px 0px; padding-top: 10px; padding-bottom: 5px; padding-left: 10px; background-image: url(http://cu.img168.net/static/image/common//codebg.gif); background-color: #f7f7f7; zoom: 1; border: 1px solid #cccccc; font-family: song, Verdana; line-height: 22.383333206176758px; background-position: 0px 0px; background-repeat: no-repeat repeat; "><div id="code_9M2" style="word-wrap: break-word; "><ol style="word-wrap: break-word; margin: 0px 0px 0px 10px !important; padding: 0px !important; "><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">list_entry(/include/linux/list.h)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#define list_entry(ptr, type, member) \<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; container_of(ptr, type, member)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">---------------------------------------------------<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">container_of(include/linux/kernel.h)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#define container_of(ptr, type, member) ({&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; const typeof( ((type *)0)-&gt;member ) *__mptr = (ptr);&nbsp; &nbsp; &nbsp; &nbsp; \<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; (type *)( (char *)__mptr - offsetof(type,member) );})<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">-------------------------------------------<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">offsetof(/include/linux/stddef.h)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#define offsetof(TYPE, MEMBER) ((size_t) &amp;((TYPE *)0)-&gt;MEMBER)</li></ol></div></div><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">对于list_entry宏来说ptr在这里为指向children链表的指针，type为task_struct结构体的类型，member为链表成员的变量名，即children。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">container_of()思路为先求出结构体成员member(即children)在结构体(即task_struct)中的偏移量，然后再根据member的地址(即ptr)来求出结构体(即task_struct)的地址。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">哇哈哈</span><img src="http://bbs.chinaunix.net/static/image/smiley/grapeman/05.gif" smilieid="186" border="0" alt="" style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">&nbsp;&nbsp;下面是我觉得最经典的地方((type *)0)-&gt;member，他将地址0强制转换为type类型的指针，然后再指向成员member，此时((type *)0)-&gt;member的地址即为member成员相对于结构体的位移。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">其中typeof()相当于C的sizeof()，(char *)__mptr这个强制转换用来计算偏移字节量，size_t被定义为unsigned int 类型。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">&nbsp;&nbsp;</span><img src="http://bbs.chinaunix.net/static/image/smiley/default/icon_mrgreen.gif" smilieid="69" border="0" alt="" style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " />&nbsp;<img src="http://bbs.chinaunix.net/static/image/smiley/default/icon_mrgreen.gif" smilieid="69" border="0" alt="" style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " />&nbsp;<img src="http://bbs.chinaunix.net/static/image/smiley/default/icon_mrgreen.gif" smilieid="69" border="0" alt="" style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">&nbsp; &nbsp;这样这个过程就不难理解了吧</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">PS：网上找到的list_entry宏定义的另一个版本（有人说是老版本kernel里面的），其实是一样的，大家自己理解吧。^_^</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">#define list_entry(ptr, type, member) \&nbsp;</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">((type *)((char *)(ptr)-(unsigned long)(&amp;((type *)0)-&gt;member)))</span>&nbsp;<img src ="http://www.cppblog.com/mysileng/aggbug/193294.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-15 10:57 <a href="http://www.cppblog.com/mysileng/archive/2012/10/15/193294.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>1.Linux内核设计与实现 P23---浅析current寻找task_struct的过程(转)</title><link>http://www.cppblog.com/mysileng/archive/2012/10/15/193293.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 15 Oct 2012 02:37:00 GMT</pubDate><guid>http://www.cppblog.com/mysileng/archive/2012/10/15/193293.html</guid><wfw:comment>http://www.cppblog.com/mysileng/comments/193293.html</wfw:comment><comments>http://www.cppblog.com/mysileng/archive/2012/10/15/193293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mysileng/comments/commentRss/193293.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mysileng/services/trackbacks/193293.html</trackback:ping><description><![CDATA[<span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">Linux通过slab分配器动态分配task_struct结构，该结构定义在了&lt;include/linux/sched.h&gt;文件中，进程描述符中包含一个具体进程的所有信息，各个进程的task_struct存放在它们内核栈的尾端。在栈底（对于向下增长的栈）或栈顶（对于向上增长的栈）创建一个新的结构struct thread_info。利用这个新的机构来迅速的找到task_struct的位置。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">&nbsp;&nbsp;下面是kernel2.6.32.10里task_struct的定义（对于x86类型的CPU来说文件位于：arch/x86/include/asm /include/asm/thread_info.h）：</span><div style="word-wrap: break-word; overflow: hidden; margin: 10px 0px; padding-top: 10px; padding-bottom: 5px; padding-left: 10px; background-image: url(http://cu.img168.net/static/image/common//codebg.gif); background-color: #f7f7f7; zoom: 1; border: 1px solid #cccccc; font-family: song, Verdana; line-height: 22.383333206176758px; background-position: 0px 0px; background-repeat: no-repeat repeat; "><div id="code_56C" style="word-wrap: break-word; "><ol style="word-wrap: break-word; margin: 0px 0px 0px 10px !important; padding: 0px !important; "><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">struct thread_info {<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; struct task_struct&nbsp; &nbsp; &nbsp; &nbsp; *task;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* main task structure */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; struct exec_domain&nbsp; &nbsp; &nbsp; &nbsp; *exec_domain;&nbsp; &nbsp; &nbsp; &nbsp; /* execution domain */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; __u32&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; flags;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* low level flags */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; __u32&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; status;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* thread synchronous flags */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; __u32&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cpu;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* current CPU */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; int&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; preempt_count;&nbsp; &nbsp; &nbsp; &nbsp; /* 0 =&gt; preemptable, &lt;0 =&gt; BUG */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; mm_segment_t&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addr_limit;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; struct restart_block&nbsp; &nbsp; restart_block;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; void __user&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; *sysenter_return;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#ifdef CONFIG_X86_32<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; unsigned long&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;previous_esp;&nbsp; &nbsp;/* ESP of the previous stack in&nbsp; &nbsp;case of nested (IRQ) stacks*/<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; __u8&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; supervisor_stack[0];<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">#endif<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; int&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uaccess_err;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">};</li></ol></div></div><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">其中的task的值就为task_struct的位置。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">kernel利用current宏寻找task_struct的位置，假设栈的大小为8k(13个二进制位)，我们可以将进程栈的地址的后13位屏蔽掉，这样得到的刚好就是进程栈的起始地址，而thread_info刚好就是位于进程栈的底部，所以进程栈的起始地址就是struct thread_info的地址，得到了thread_info的地址，我们就很容易找到task_struct的地址了。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">汇编实现过程为</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">movl&nbsp;&nbsp;%-8192 ,%eax</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">andl&nbsp; &nbsp;%esp ,%eax</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">寄存器esp存放进程栈的当前地址，eax最后存放的就是进程栈的起始地址。current使用current_thread_info来实现这个过程。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">kernel源码（对于x86类型的CPU来说文件位于arch/x86/include/asm //include/asm/thread_info.h）</span><div style="word-wrap: break-word; overflow: hidden; margin: 10px 0px; padding-top: 10px; padding-bottom: 5px; padding-left: 10px; background-image: url(http://cu.img168.net/static/image/common//codebg.gif); background-color: #f7f7f7; zoom: 1; border: 1px solid #cccccc; font-family: song, Verdana; line-height: 22.383333206176758px; background-position: 0px 0px; background-repeat: no-repeat repeat; "><div id="code_HE9" style="word-wrap: break-word; "><ol style="word-wrap: break-word; margin: 0px 0px 0px 10px !important; padding: 0px !important; "><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">/* how to get the current stack pointer from C */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">register unsigned long current_stack_pointer asm("esp") __used;<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; "><br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">/* how to get the thread information struct from C */<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">static inline struct thread_info *current_thread_info(void)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">{<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; return (struct thread_info *)<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (current_stack_pointer &amp; ~(THREAD_SIZE - 1));<br style="word-wrap: break-word; " /></li><li style="word-wrap: break-word; margin: 0px 0px 0px 2em; padding: 0px 0px 0px 10px; list-style-type: decimal-leading-zero; font-family: Monaco, Consolas, '&quot;Lucida Console&quot;', '&quot;Courier New&quot;', serif; font-size: 12px; line-height: 1.8em; ">};</li></ol></div></div><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">其中current_stack_pointer为进程栈的当前地址,THREAD_SIZE为进程栈的大小。</span><br style="word-wrap: break-word; font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; " /><span style="font-family: song, Verdana; line-height: 22.383333206176758px; background-color: #f0f3fa; ">所以current_thread_info()-&gt;task即为task_struct()的地址。</span>&nbsp;<img src ="http://www.cppblog.com/mysileng/aggbug/193293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mysileng/" target="_blank">鑫龙</a> 2012-10-15 10:37 <a href="http://www.cppblog.com/mysileng/archive/2012/10/15/193293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>