﻿<?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++博客-桃源谷-随笔分类-Unix/Linux</title><link>http://www.cppblog.com/lymons/category/7270.html</link><description>&lt;h1&gt;&lt;b&gt;心灵的旅行&lt;/b&gt;&lt;/h1&gt;&lt;/p&gt;
人生就是一场旅行，不在乎旅行的目的地，在乎的是沿途的风景和看风景的心情 !</description><language>zh-cn</language><lastBuildDate>Mon, 15 Nov 2010 18:40:16 GMT</lastBuildDate><pubDate>Mon, 15 Nov 2010 18:40:16 GMT</pubDate><ttl>60</ttl><item><title>C++ 局部静态初始化不是线程安全的！</title><link>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Aug 2010 05:19:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/120638.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/08/01/120638.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/120638.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/120638.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 14px;">
<h3 class="post-name" style="margin: 7px 0px 18px; padding: 0px; font-weight: bold; color: #333333; font-size: 1.2em; text-decoration: none;">请注意,C++ 局部静态初始化不是线程安全!</h3>
http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
<br><br>
<div class="post-date" style="padding: 6px 0px; font-size: 12px; color: #666666; font-weight: bold; position: relative; display: inline;"><span class="value" style="margin-left: 3px;">8 Mar 2004 7:00 AM&nbsp;</span></div>
<div class="post-attributes" style="padding: 6px 0px; display: inline; border-left-color: #cccccc; margin-left: 8px;">
<ul class="attribute-list" style="margin: 0px 0px 0px 8px; padding: 0px; list-style-type: none; display: inline; background-image: url(&quot;http://blogs.msdn.com/themes/wireframe/Images/icon-sprite.gif&quot;); background-color: transparent; background-position: 0px -380px;">
    <li class="attribute-item post-reply-count" style="margin: 0px 3px 0px 0px; padding: 0px; display: inline;"><span class="attribute-value" style="font-size: 16px; font-weight: bold; color: #005ae1; margin-left: 18px;"><a href="http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx#comments" class="internal-link view-replies" style="outline-style: none; color: #770000; text-decoration: underline; font-weight: inherit;"><span></span>49</a></span></li>
</ul>
</div>
<div class="post-content user-defined-markup" style="margin: 8px 0px 10px; padding: 10px 0px; overflow: hidden; max-width: 100%; position: relative; line-height: 1.5em;">
<p style="margin-top: 12px;">在块作用域中的静态变量的规则 (与之相对的是全局作用域的静态变量) 是, 程序第一次执行到他的声明的时候进行初始化.</p>
<p style="margin-top: 12px;">察看下面的竞争条件:</p>
<pre>int ComputeSomething()
{
&nbsp; &nbsp;static int cachedResult = ComputeSomethingSlowly();
&nbsp; &nbsp;return cachedResult;
}
</pre>
<p style="margin-top: 12px;">这段代码的意图是在该函数第一次被调用的时候去计算一些费用, 并且把结果缓冲起来待函数将来再被调用的时候则直接返回这个值即可.</p>
<p style="margin-top: 12px;">这个基本技巧的变种,在网络上也被叫做 <a href="http://users.utu.fi/~sisasa/oasis/cppfaq/ctors.html#[10.9]" style="outline-style: none; color: #770000; text-decoration: underline; font-weight: inherit;">避免 "static initialization order fiasco"</a>. ( fiasco这个词 在这个网页上有非常棒的描述,因此我建议大家去读一读然后去理解它.)</p>
<p style="margin-top: 12px;">这段代码的问题是非线程安全的. 在局部作用域中的静态变量是编译时会在编译器内部转换成下面的样子:</p>
<pre>int ComputeSomething()
{
<font color="blue">  static bool cachedResult_computed = false;
&nbsp; static int cachedResult;
&nbsp; if (!cachedResult_computed) {
&nbsp; &nbsp; &nbsp;cachedResult_computed = true;
&nbsp; &nbsp; &nbsp;cachedResult = ComputeSomethingSlowly();
&nbsp; }</font>
&nbsp; return cachedResult;
}
</pre>
<p style="margin-top: 12px;">现在竞争条件就比较容易看到了.</p>
<p style="margin-top: 12px;">假设两个线程在同一时刻都调用这个函数. 第一个线程在执行 cachedResult_computed&nbsp;=&nbsp;true 后, 被抢占. 第二个线程现在看到的 cachedResult_computed 是一个真值( true ),然后就略过了if分支的处理,最后该函数返回的是一个未初始化的变量.</p>
<p style="margin-top: 12px;">现在你看到的东西并不是一个编译器的bug, 这个行为 <strong>C++ 标准所要求的.</strong></p>
<p style="margin-top: 12px;">你也能写一个变体来产生一个更糟糕的问题:</p>
<pre>class Something { ... };
int ComputeSomething()
{
&nbsp; &nbsp;static Something s;
&nbsp; &nbsp;return s.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">同样的在编译器内部它会被重写 (这次, 我们使用C++伪代码):</p>
<pre>class Something { ... };
int ComputeSomething()
{
<font color="blue">  static bool s_constructed = false;
&nbsp; static uninitialized Something s;
&nbsp; if (!s_constructed) {
&nbsp; &nbsp; &nbsp; s_constructed = true;
&nbsp; &nbsp; &nbsp; new(&amp;s) Something; // construct it
&nbsp; &nbsp; &nbsp; atexit(DestructS);
&nbsp; }</font>
&nbsp; return s.ComputeIt();
}
<font color="blue">// Destruct s at process termination
void DestructS()
{
&nbsp; &nbsp;ComputeSomething::s.~Something();
}</font>
</pre>
<p style="margin-top: 12px;">注意这里有多重的竞争条件. 就像前面所说的, 一个线程很可能在另一个线程之前运行并且在"s"还没有被构造前就使用它. <br></p>
<p style="margin-top: 12px;">甚至更糟糕的情况, 第一个线程很可能在s_contructed 条件判定 之后,在他被设置成"true"<span style="font-weight: bold;">之前</span>被抢占. 在这种场合下, 对象s就会被<span style="font-weight: bold;">双重构造</span>和<span style="font-weight: bold;">双重析构</span>.&nbsp;</p>
<p style="margin-top: 12px;">这样就不是很好.</p>
<p style="margin-top: 12px;">但是等等, 这并不是全部, 现在(原文是Not,我认为是Now的笔误)看看如果有<span style="font-style: italic;">两个</span>运行期初始化局部静态变量的话会发生什么: <br></p>
<pre>class Something { ... };
int ComputeSomething()
{
static Something s(0);
static Something t(1);
return s.ComputeIt() + t.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">上面的代码会被编译器转化为下面的伪C++代码:</p>
<pre>class Something { ... };
int ComputeSomething()
{
<font color="blue">  static char constructed = 0;
static uninitialized Something s;
if (!(constructed &amp; 1)) {
constructed |= 1;
new(&amp;s) Something; // construct it
atexit(DestructS);
}
static uninitialized Something t;
if (!(constructed &amp; 2)) {
constructed |= 2;
new(&amp;t) Something; // construct it
atexit(DestructT);
}</font>
return s.ComputeIt() + t.ComputeIt();
}
</pre>
<p style="margin-top: 12px;">为了节省空间, 编译器会把两个"x_constructed" 变量放到一个 bitfield 中. 现在这里在变量"construted"上就有多个<span style="font-weight: bold;">无内部锁定</span>的读-改-存操作.</p>
<p style="margin-top: 12px;">现在考虑一下如果一个线程尝试去执行 "constructed |= 1", 而在同一时间另一个线程尝试执行 "constructed |= 2".</p>
<p style="margin-top: 12px;">在x86平台上, 这条语句会被汇编成</p>
<pre>  or constructed, 1
...
or constructed, 2
</pre>
并没有 "lock" 前缀. 在多处理机器上, 很有可能发生两个存储都去读同一个旧值并且互相使用冲突的值进行碰撞(clobber).
<p style="margin-top: 12px;">在 ia64 和 alpha平台上, 这个碰撞将更加明显,因为它们么没有这样的<span style="font-weight: bold;">读-改-存</span>的单条指令; 而是被编码成三条指令:</p>
<pre>  ldl t1,0(a0)     ; load
addl t1,1,t1     ; modify
stl t1,1,0(a0)   ; store
</pre>
<p style="margin-top: 12px;">如果这个线程在 load 和 store之间被抢占, 这个存储的值可能将不再是它曾经要写入的那个值.</p>
<p style="margin-top: 12px;">因此,现在考虑下面这个有问题的执行顺序:</p>
<ul>
    <li>线程A 在测试 "constructed" 条件后发现他是零, 并且正要准备把这个值设定成1， 但是它被抢占了.</li>
    <li>线程B 进入同样的函数, 看到 "constructed" 是零并继续去构造 "s" 和 "t", 离开时 "constructed" 等于3.</li>
    <li>线程A 继续执行并且完成它的 读-改-存 的指令序列, 设定 "constructed" 成 1, 然后构造 "s" (第二次).</li>
    <li>线程A 然后继续去构造 "t" (第二次) 并设定 "constructed" (最终) 成 3.</li>
</ul>
<p style="margin-top: 12px;">现在, 你可能会认为你能用临界区 (critical section) 来封装这个运行期初始化动作:</p>
<pre>int ComputeSomething()
{
EnterCriticalSection(...);
static int cachedResult = ComputeSomethingSlowly();
LeaveCriticalSection(...);
return cachedResult;
}
</pre>
<p style="margin-top: 12px;">因为你现在把这个一次初始化放到了临界区里面,而使它线程安全.</p>
<p style="margin-top: 12px;">但是如果从同一个线程再一次调用这个函数会怎样? ("我们跟踪了这个调用; 它确实是来自这个线程!") 如果 ComputeSomethingSlowly() 它自己间接地调用 ComputeSomething()就会发生这个状况.</p>
<p style="margin-top: 12px;">结论: 当你看见一个局部静态变量在运行期初始化时, 你一定要小心.</p>
</div>
</span><img src ="http://www.cppblog.com/lymons/aggbug/120638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-08-01 13:19 <a href="http://www.cppblog.com/lymons/archive/2010/08/01/120638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C++编写synchronized method比较难</title><link>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sat, 17 Jul 2010 04:17:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/118502.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/07/17/118502.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/118502.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/118502.html</trackback:ping><description><![CDATA[<h3>在C++下编写synchronized method比较难 (1)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>在Java中有叫做synchronized这样一个方便的关键字。使用这个关键字的话,就可以像下面那样能够简单的进行"同步"method. 然而,被同步的method并不表示它就能在多线程中同时被执行. <br></p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Foo&nbsp;{&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;&nbsp;<br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;synchronized&nbsp;boolean&nbsp;getFoo()&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</span></div>
<br></pre>
<p>那么、在C++ (with pthread)中如何能实现同样的功能呢? 首先,有一个最简单的方法就是下面这个.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;a</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;</span><span id="Codehighlighter1_37_182_Closed_Text" style="border: 1px solid #808080; display: none; background-color: #ffffff;"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_37_182_Open_Text"><span style="color: #000000;">{&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_INITIALIZER;&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex);&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;临界区处理&nbsp;&nbsp;</span><span style="color: #008000;"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #000000;">pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex);&nbsp;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></span></div>
<br></pre>
<p><br>这个方法, 暂且不说C语言, 就是在C++中下面的若干问题 <br></p>
<ul>
    <li>在临界区中间被return出来
    </li>
    <li>在临界区中间发生异常exception<br></li>
</ul>
<p>发生的场合, mutex没有被解锁unlock。我们可以像下面代码那样对这点进行改进.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: #eeeeee;"><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;b</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ScopedLock&nbsp;:&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;boost::noncopyable&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:&nbsp;&nbsp;</span><span style="color: #0000ff;">explicit</span><span style="color: #000000;">&nbsp;ScopedLock(pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;:&nbsp;m_(m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #000000;">~</span><span style="color: #000000;">ScopedLock(pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;pthread_mutex_t</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m_;<br>};<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_INITIALIZER;<br>&nbsp;&nbsp;{<br>&nbsp;</span><span style="color: #008000;">   //</span><span style="color: #008000;">&nbsp;虽然不加这个括号程序也没有问题。</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;在此处添加处理</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>OK。return和异常的问题就可以解决了. 但是, 上面并没有完全解决这个问题,仍然有下面这个问题.</p>
<ol>
    <li>使用这个pthread_mutex_t并不是C++的.特别是存在下面的问题:
    <ul>
        <li>不能和其他的Mutex类型做同样的处理
        </li>
        <li>与其他的Mutex类型使用同一个ScopedLock类,则不能lock </li>
    </ul>
    </li>
    <li>Java的synchronized方法虽然可以"递归lock", 但是上面的代码并不是这样. 在临界区中递归调用自己的话就会发生死锁. </li>
</ol>
<p>特别是,第2点的递归lock的问题是很重要的. 这里好好地使用glibc扩展的话就可以象下面那样解决.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><span style="color: #008000;">/</span><span style="color: #008000;">&nbsp;方法&nbsp;c</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;pthread_mutex_t&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;</span></div>
<br></pre>
<p>从NP<span class="footnote"><a title="non portable の意" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f1" name="20040713fn1">*1</a>这个后缀名就知道, 这个方法没有可移植性. 必须使用</span>pthread_mutex_init来初始化递归mutex,而pthread_mutex_init函数在一个线程中只能被调用一次. 如果想要用synchronized method的方法来实现这个的话,就变成了"是先有鸡还是先有蛋"的话题了. 所以,用叫做pthread_once的函数来实现它,这也是<a href="http://www.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_init.html#tag_03_537_08_04">在SUSv3中被记载的定则</a>。</p>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法&nbsp;d</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;</span><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;anonymous&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;pthread_once_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;once&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;PTHREAD_ONCE_INIT;<br>&nbsp;&nbsp;pthread_mutex_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mutex;<br>&nbsp;&nbsp;pthread_mutexattr_t&nbsp;attr;<br><br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;mutex_init()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutexattr_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutexattr_settype(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr,&nbsp;PTHREAD_MUTEX_RECURSIVE);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex,&nbsp;</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">attr);<br>&nbsp;&nbsp;}<br>}<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;pthread_once(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">once,&nbsp;mutex_init);<br><br><br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;処理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br>
<p>上面的代码就OK了。</p>
<p><br></p>
<p>这就能够解决递归lock的问题了.但是..., 这个方法</p>
<ul>
    <li>这越来越不像C++的代码了.。对每一个想要同步(synchronize)的方法都像这么样写代码的话,效率变得非常低下.
    </li>
    <li>随机成本大。速度慢。 </li>
</ul>
<p>就会产生上面那样的新问题.</p>
<br>
<div class="section">
<h3><a href="http://d.hatena.ne.jp/yupo5656/20040713/p2" name="p2"><span class="sanchor">■</span></a>[<a class="sectioncategory" href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2A%5BC%2B%2B%5D">C++</a>] 在C++下编写synchronized method比较难(2)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>"不, 方法的同步应该是经常必需的, 并不是没有方便的办法",这样的说法也有吧. 是的, 有. 一般的办法是下面那样,</p>
<ul>
    <li>做成一个Mutex,作为(non-POD型的, 即普通的)C++类
    </li>
    <li>做成一个Mutex类的实例,作为类变量, 或者是全局变量, 来同步化方法</li>
</ul>
<br>
<p>来看看它的具体实现吧. 首先做成的Mutex类是下面那样<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f2" title="再帰mutexにする例は冗長なので略します。pthread_mutex_initによる初期化を行っていますので、再帰mutex化す
るのは容易です。" name="20040713fn2">*2</a></span>。</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Mutex&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;Mutex()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_init(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_,&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Lock(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Unlock(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">m_);<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;pthread_mutex_t&nbsp;m_;<br>};</span></div>
<br></pre>
<p>现在的Mutex类,被作为抽象基类(接口类)的场合也比较多. 在这里就不说了. ScopedLock类也需要有若干的修改. 想下面那样写就好.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">template</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">typename&nbsp;T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ScopedLock&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;ScopedLock(T</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;m)&nbsp;:&nbsp;_m(m)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;_m.Lock();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #000000;">~</span><span style="color: #000000;">ScopedLock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;_m.Unlock();<br>&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">private</span><span style="color: #000000;">:<br>&nbsp;&nbsp;T</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">&nbsp;_m;<br>};</span></div>
<br></pre>
<p>用这个Mutex类来同步方法, 就可以像下面那样写. 首先是看看一个明显的有错误的例子.</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法e</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;Mutex&nbsp;mutex;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Mutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;処理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>这是... 代码虽然简单易懂,但是很遗憾,它不能很好工作. NG!. Foo::need_to_sync函数第一次被执行的时候如果恰好是多个线程同时执行的话, mutex 的构造函数就有被多次调用的可能性.关于理由，可以参考微软中比较有名气的blog文章The Old New Thing、<a href="http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx">"C++ scoped static initialization is not thread-safe, on purpose!"</a>在这里面有详尽的描述，所以就我们就不在详细叙述了<span class="footnote"><a title="そのうちg++ -s結果とその解説でも載せます" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f3" name="20040713fn3">*3</a></span>。在这篇blog里使用了VC++的代码作为例子，但是g++也是差不多的。所以<strong>&#8220;动态的初始化局部的静态变量&#8221;是， 在线程所完全意识不到的情况下进行的<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f4" title="2005/12追記:
最近のg++では例外アリ、http://d.hatena.ne.jp/yupo5656/20051215/p2 を参照のこと" name="20040713fn4">*4</a></span></strong>。</p>
<br>
<p>接下来，要介绍一个在目前做的比较好的方法。 为了简单我们使用了全局变量，但是即使作为类变量（类中的static成员变量）也是一样的。这个方法就是使用&#8220;非局部的静态变量&#8221;来做成Mutex。</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法f</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">namespace</span><span style="color: #000000;">&nbsp;</span><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;anonymous&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;Mutex&nbsp;mutex;<br>}<br><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Mutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;"> 处理</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>这个是最流行的方法，而且<span style="font-weight: bold;">基本</span>上可以没有问题就能工作得很好。</p>
<p>在一个全局的类对象x存在，且在x的构造函数中直接或者绕弯间接的调用Foo::need_to_sync函数的场合，会引起一些问题。也就是静态的对象的初始化顺序的问题，这个问题一般也被叫做"static initialization order fiasco" 。在执行到mutex的构造函数之前， mutex.Lock()有可能会被执行。</p>
<br>
<p><a href="http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12">这里的FAQ</a>的10.12～10.16<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f5" title="和書 asin:489471194x に邦訳が載っています。また static
initialization order の問題については此処にもそのうち何か書きます" name="20040713fn5">*5</a></span>、在里面对自己的代码的初始化顺序已经证明了没有问题，而且将来也不会出现问题，所以上面的方法是OK的。</p>
<br>
<p>如果， 初始化顺序的问题不能保证他没有问题的话， 只好使用pthread_once的&#8220;方法d&#8221;，或者移植性低的&#8220;方法c&#8221;。我的个人感觉是方法c还是比较不错的选择。</p>
<br>
<p>在最后我们尝试考虑一下如何把方法c变成C++的代码。</p>
<pre>// 方法c (重新讨论)<br><br>void Foo::need_to_sync(void) {<br>  static pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;<br></pre>
<p>目标是、</p>
<ul>
    <li>隐藏pthread_mutex_t类型、让自己写的类的类型可见。
    </li>
    <li>在方法e,f中像使用ScopedLock模板那样进行修改。 </li>
</ul>
<p>当然，不让它发生初始化顺序的问题。<br></p>
<br></div>
<div class="section">
<h3><a href="http://d.hatena.ne.jp/yupo5656/20040713/p3" name="p3"><span class="sanchor">■</span></a>[<a class="sectioncategory" href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2A%5BC%2B%2B%5D">C++</a>] 用C++编写synchronized method比较难 (3)<span class="hatena-star-comment-container"><img class="hatena-star-comment-button" title="Comments" style="border: medium none ; margin: 0pt; padding: 0pt; display: none; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Comments" src="http://s.hatena.ne.jp/images/comment.gif"></span><span class="hatena-star-star-container"><img class="hatena-star-add-button" title="Add star" style="border: medium none ; margin: 0pt 3px; padding: 0pt; vertical-align: middle; cursor: pointer;" tabindex="0" alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<p>这是方法c的改良。 首先， 为了避免发生初始化顺序的问题， 必须是不允许调用构造函数就能完成对象的初始化。因此，必须像下面那样初始化mutex对象</p>
<pre>// 方法c' (假设)<br><br>void Foo::need_to_sync(void) {<br>  static StaticMutex mutex = { PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP, ........ };<br></pre>
<p>一般的不允许像这样初始化C++类。为了实现上面那样的初始化，StaticMutex类必须是POD型的。所谓POD型就是， <br></p>
<ul>
    <li>不允许有构造函数
    </li>
    <li>不允许有析构函数
    </li>
    <li>不允许编译器生成拷贝构造函数， 赋值构造函数。
    </li>
    <li>不允许有private, protected 的成员
    </li>
    <li>不允许有虚函数 </li>
</ul>
<p>满足以上规格的类型<span class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f6" title="詳しくは iso/iec 14882:2003 あるいは jis x 3014:2003
の「&#167;3.9/10 c互換型」「&#167;8.5.1/14 静的記憶期間をもつc互換型の集成体の波括弧で囲んだ初期化子並びによる静的な初期化」「&#167;9/4
c互換構造体」を参照のこと" name="20040713fn6">*6</a></span>。</p>
<br>
<p>大概有严格的制约,但是利用"定义非虚成员函数是没有问题的"这个特性, 我们尝试改良方法c的方案. <br></p>
<br>
<p>...像下面那样如何?</p>
<pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;方法c'</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC&nbsp;0xdeadbeef</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;STATIC_MUTEX_INITIALIZER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;PTHREAD_INITIALIZER,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POD_MUTEX_MAGIC&nbsp;}</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">#define</span><span style="color: #000000;">&nbsp;STATIC_RECURSIVE_MUTEX_INITIALIZER&nbsp;{&nbsp;PTHREAD_RECURSIVE_INITIALIZER_NP,&nbsp;POD_MUTEX_MAGIC&nbsp;}</span><span style="color: #000000;"><br><br></span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;PODMutex&nbsp;{<br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Lock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;assert(magic_&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_lock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Unlock()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;assert(magic_&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;POD_MUTEX_MAGIC);<br>&nbsp;&nbsp;&nbsp;&nbsp;pthread_mutex_unlock(</span><span style="color: #000000;">&amp;</span><span style="color: #000000;">mutex_);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;typedef&nbsp;ScopedLock</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">PODMutex</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;ScopedLock;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">:<br>&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;虽然编程了POD型,&nbsp;但是不定义成public就是无效的</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;pthread_mutex_t&nbsp;mutex_;<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">const</span><span style="color: #000000;">&nbsp;unsigned&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;magic_;<br>};<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;ScopedLock类模板是留用了在方法e,f中做成的代码.</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Foo::need_to_sync(</span><span style="color: #0000ff;">void</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;PODMutex&nbsp;mutex&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;STATIC_RECURSIVE_MUTEX_INITIALIZER;<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;PODMutex::ScopedLock&nbsp;</span><span style="color: #0000ff;">lock</span><span style="color: #000000;">(mutex);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;处理.</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>}</span></div>
<br></pre>
<p>上面的代码满足了"隐藏了pthread_mutex_t型,留用了ScopedLock&lt;&gt;"这两个目的. 这不就是有点儿像C++的代码了吗? 还有,PODMutex类型是即使在上记例子中那样的局部静态变量以外,也能放心的使用全局变量,类变量了.</p>
<p>而且, 成员变量 magic_ 是, 一个const成员变量, 所以当使用编译器自动生成的构造函数来创建一个对象时就会发生错误. 因此,在构建release版程序时把它剔除就好了.</p>
<br>
<p>使用g++ -S来编译上面的代码, 生成汇编代码. 我们就能看见下面那样的局部的静态变量. <br></p>
<pre>$ g++ -S sample.cpp<br>$ c++filt &lt; sample.s | lv<br>(略)<br>        .size   Foo::need_to_sync()::mutex, 28<br>Foo::need_to_sync()::mutex:<br>        .long   0<br>        .long   0<br>        .long   0<br>        .long   1<br>        .long   0<br>        .long   0<br>        .long   -559038737<br></pre>
<p>0,0,0,1,0,0 这样的东西是 PTHREAD_RECURSIVE_INITIALIZER_NP , -559038737 则是 POD_MUTEX_MAGIC 。即使没有进行动态的初始化(不调用构造函数)、仅仅是在目标文件上生成的目标代码那样的进行静态初始化, mutex对象也能被正常的初始化, 所以这段代码是OK的.</p>
<br>
<p>随便, 在使用boost库的场合, 方法f之外的选择余地几乎没有(至少是现在). 一看见ML等, (当然!!)就知道可能会出现 order顺序的问题. 但是, 就目前来讲, 既要保证既要保证可移植性<a title="Windowsはどうする？という命題を解決できないのかもしれません - 推測" href="http://d.hatena.ne.jp/yupo5656/20040713#20040713f7" name="20040713fn7">*7</a>和速度,又要能做成与方法c相当的PODMutex的方法好像还没有出现吧.</p>
<br>
<p>完结</p>
</div>
<div class="footnote">
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn1" name="20040713f1">*1</a>：non portable 的意思</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn2" name="20040713f2">*2</a>：递归mutex的例子的代码太冗长了,这里就省略 了. 根据pthread_mutex_init来进行初始化,就使得做成递归mutex变得比较容易了.</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn3" name="20040713f3">*3</a>：这里记载了 g++ -S的结果和解说</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn4" name="20040713f4">*4</a>：2005/12追记: 在最近的g++中发生异常、参照这里 <a href="http://d.hatena.ne.jp/yupo5656/20051215/p2" target="_blank">http://d.hatena.ne.jp/yupo5656/20051215/p2</a> <br></p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn5" name="20040713f5">*5</a>：日语文献 <a href="http://d.hatena.ne.jp/asin/489471194X/casactdiary-22">ASIN:489471194X</a> 中记载着翻译版. 还有 static initialization order 的问题,在此处也有一些记载.</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn6" name="20040713f6">*6</a>：详细是参看 ISO/IEC 14882:2003 或者是 JIS X 3014:2003 的「&#167;3.9/10 C互換型」「&#167;8.5.1/14 静的記憶期間をもつC互換型の集成体の波括弧で囲んだ初期化子並びによる静的な初期化」「&#167;9/4 C互換構造体」这几个章节</p>
<p class="footnote"><a href="http://d.hatena.ne.jp/yupo5656/20040713#20040713fn7" name="20040713f7">*7</a>：Windows是如何做的? 可能不能解决这个命题 - 推测</p>
<p class="footnote"><br></p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20051215/p2</p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20041011#p1</p>
<p class="footnote">http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx</p>
<p class="footnote">http://d.hatena.ne.jp/yupo5656/20071008/p1<br></p>
</div><img src ="http://www.cppblog.com/lymons/aggbug/118502.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-07-17 12:17 <a href="http://www.cppblog.com/lymons/archive/2010/07/17/118502.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>W2K信号(Signals)的设备驱动</title><link>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Tue, 13 Apr 2010 02:29:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/112426.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2010/04/13/112426.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/112426.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/112426.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: W2K信号(Signals)的设备驱动 <br><br>     Unix下的信号提供了一个简单的IPC机制，也就是当进程收到一个信号后会异步(asynchronous) 地调用你的信号处理函数(也叫做句柄)，不管你的代码是否已经处在执行的过程之中。 而在Windows 2000(译者注：版本高于W2k的Windows平台)下就需要用到一个设备驱动，以便你能使用异步过程调用(asynchronous procedure calls , 简称APCs或者APC) 来达成同样的效果.<br><br>By Panagiotis E. <br>August 01, 2001 <br>URL:http://www.ddj.com/windows/184416344 <br><br>翻译：Lymons (lymons@gmail.com)<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2010/04/13/112426.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/112426.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2010-04-13 10:29 <a href="http://www.cppblog.com/lymons/archive/2010/04/13/112426.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式在Linux文件系统中的简单实现  </title><link>http://www.cppblog.com/lymons/archive/2009/02/02/72798.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 02 Feb 2009 05:08:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/02/72798.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72798.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/02/72798.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72798.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72798.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: From 2008精选            设计模式在Linux文件系统中的简单实现注：这边文章是为了学习设计模式而写的，并不具有实际的意义。仅仅作为学习设计模式的一个参考。Linux中的文件系统，设备驱动，和实际设备间的结构关系如下图所示：&nbsp;用户对设备上的数据进行I/O访问，都要先把I/O请求传递到VFS层，然后再通过实际的文件系统，最终传递到设备的驱...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/02/72798.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72798.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-02 13:08 <a href="http://www.cppblog.com/lymons/archive/2009/02/02/72798.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ATA Over Ethernet: 把硬盘放到局域网上</title><link>http://www.cppblog.com/lymons/archive/2009/02/02/72796.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 02 Feb 2009 04:54:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/02/72796.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72796.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/02/72796.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72796.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72796.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Kernel Korner - ATA Over Ethernet: 把硬盘放到局域网上ATA Over Ethernet: 把硬盘放到局域网上 By Ed Cashin 翻译:Lymons LauCreated 2005-04-28 01:00 &nbsp;随着ATA硬盘比起磁带这样的存储设备来是越来越便宜，一些简单的新存储技术能使你为存档，备份或者现场应用建...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/02/72796.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-02 12:54 <a href="http://www.cppblog.com/lymons/archive/2009/02/02/72796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用lex/yacc来解析配置文件  </title><link>http://www.cppblog.com/lymons/archive/2009/02/02/72793.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 02 Feb 2009 04:31:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/02/72793.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72793.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/02/72793.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72793.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72793.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: From 2008精选            在linux平台下的较为庞大的命令一般都带有一个配置文件，用于存储该命令启动时要设置的参数，用户还可以变更该配置文件中的某些域的值。因此，在命令中就要考虑怎么来存取这些文件里的值。一般情况下，大多数程序员都愿意自己编写一段程序来解析配置文件里内容，在配置文件比较小的情况下，该中方法也非常方便适用，我平时也喜欢这么作。但是，当在配置文...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/02/72793.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-02 12:31 <a href="http://www.cppblog.com/lymons/archive/2009/02/02/72793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RX-RPC的客户端与服务器端的通信解析</title><link>http://www.cppblog.com/lymons/archive/2009/02/02/72790.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 02 Feb 2009 03:58:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/02/72790.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72790.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/02/72790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72790.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72790.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: From 长城            RX-RPC的客户端与服务器端的通信解析&nbsp;Rx是扩展远程过程调用（Extended Remote Procedure Call）的缩写，顾名思义，它就是RPC协议的一个扩展。与之不同的是，它允许任意大小的数据请求以及给端到端（end-to-end）的认证和安全提供了通用支持。另外，这个协议能使允许客户端和服务器端自适应它们关联性能...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/02/72790.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-02 11:58 <a href="http://www.cppblog.com/lymons/archive/2009/02/02/72790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux中处理来自共享对象的同步事件</title><link>http://www.cppblog.com/lymons/archive/2009/02/02/72784.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 02 Feb 2009 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/02/72784.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72784.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/02/72784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72784.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72784.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: September 28, 2007Linux中处理来自共享对象的同步事件By lymons                                     Linux中处理来自共享对象的同步事件            &nbsp;            怎么利用设计模式来更有效的使用共享内存             级别：中等       ...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/02/72784.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-02 11:28 <a href="http://www.cppblog.com/lymons/archive/2009/02/02/72784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跟着开源/来学习源C语言代码的读法</title><link>http://www.cppblog.com/lymons/archive/2009/02/01/72764.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Feb 2009 07:54:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/01/72764.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/72764.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/01/72764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/72764.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/72764.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 「Code Reading―从开源里学习软件开发的技法」有这样一本书。我是这本书日本版的译者， 呵呵有点王婆卖瓜---自卖自夸的感觉，但是把读源代码作为主题的在市面还没有其他的书。我认为这是一本网罗了从技法到工具，数据结构，构架，还有利用代码的实际读法的实例的好书。<br><br>照片1●「Code Reading―跟着开源来学习软件的开发技法」<br>　这本书的前言里，编程高手Dave Thomas写下了下面的话。<br>　没有研读过其他作家作品的伟大作家，没有研究过其他画家笔法的伟大画家，没有盗取过并肩作战的同事的技术的技巧高明的外科医生，没有在副驾驶的位置积累实际经验的波音767的机长，在现实生活中真的会存在他们这样的人吗？&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2009/02/01/72764.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/72764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-01 15:54 <a href="http://www.cppblog.com/lymons/archive/2009/02/01/72764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则6：遵守多线程编程的常识（下）</title><link>http://www.cppblog.com/lymons/archive/2009/02/01/70835.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Feb 2009 07:41:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2009/02/01/70835.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/70835.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2009/02/01/70835.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/70835.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/70835.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/u7vsT3OkaMXO8oJAU-QnHw?feat=embedwebsite"><img src="http://lh3.ggpht.com/_tDmtFJ6XjL4/SOfE7oipccI/AAAAAAAACwA/YI4pvNiYo0o/s400/DSC_0031.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<h3>UNIX上C++程序设计守则(6)<span class=hatena-star-comment-container><img class=hatena-star-comment-button title=Comments style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; DISPLAY: none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt=Comments src="http://s.hatena.ne.jp/images/comment.gif"></span><span class=hatena-star-star-container><img class=hatena-star-add-button title="Add star" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt 3px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<blockquote>
<p><strong>准则6: 遵守多线程编程的常识</strong></p>
<br>
<ol>
    <li>要准确把握在POSIX标准的函数中，那些函数是非线程安全的，一定不要使用
    <li>要让自己编写的函数符合线程安全
    <ul>
        <li>在访问共享数据/变量之前一定要先锁定
        <li>如果使用C++的话，一定要注意函数的同步方法 </li>
    </ul>
    </li>
</ol>
</blockquote>
<p>说明: (2) 要让自己编写的函数符合线程安全</p>
<br>
<p>在写多线程的应用程序时,在多个线程里共享的变量要先锁定然后在更新它.。那么在多线程里共享的变量主要有全局变量和函数内的静态变量。而且,即使是short型和int型的共享变量也要先锁定后更新才能保证其安全。</p>
<br>
<p>※ 详细的是参考 <a href="http://d.hatena.ne.jp/yupo5656/20040618">id:yupo5656:20040618</a> "[C++] 多线程和共享变量"</p>
<br>
<p>还有,在使用C++编程的场合要注意函数的方步方法。一般的说来下面的写法是错误的。<strong>Mutex在函数内被声明成静态变量是不允许的</strong>。</p>
<pre>int incr_counter(void) {<br>  static Mutex m;  // 这么写不行<br>  m.Lock();<br><br>  static int counter = 0;<br>  int ret = ++counter;<br><br>  m.Unlock();<br>  return ret;<br>}<br></pre>
<p>应该用下面的方式来代替,</p>
<pre>Mutex m;<br><br>int incr_counter(void) {<br>  m.Lock();<br>  // ...<br></pre>
<p>把Mutex声明成全局变量的话比较好(稍微比上一个好)。</p>
<br>
<p>※ 详细是参考 <a href="http://d.hatena.ne.jp/yupo5656/20040713">id:yupo5656:20040713</a> "[C++] C++中写出synchronized method比较难" 。</p>
<br>
<p><span style="FONT-SIZE: 18pt"><strong style="FONT-SIZE: 14pt">UNIX上C++程序设计守则(6)-- 补记</strong></span><span class=hatena-star-comment-container><img class=hatena-star-comment-button title=Comments style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0px; BORDER-TOP: medium none; DISPLAY: none; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0px; BORDER-BOTTOM: medium none" tabIndex=0 alt=Comments src="http://s.hatena.ne.jp/images/comment.gif"></span><span class=hatena-star-star-container><img class=hatena-star-add-button title="Add Star" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0px; BORDER-TOP: medium none; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px 3px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0px; BORDER-BOTTOM: medium none" tabIndex=0 height=16 alt="Add Star" src="http://s.hatena.ne.jp/images/add.gif" width=16></span></p>
<p>线程安全函数是像下面那样</p>
<ol>
    <li>不要操作局部的静态变量(函数内的static型的变量)和非局部的静态数据(全局变量)。并且,其它的非线程安全函数不要调用
    <li>要操作这样的变量的话, 就要使用mutex进行同步处理,来限制多个线程同时对它进行操作 </li>
</ol>
<p>被定义的,但是</p>
<ul>
    <li>特别是前者, 和被叫做可重入的(reentrant)函数有区别
    <li>反之, 后者特别是和叫做"Serializable"(不单单是MT-Safe)"Safe"的函数有区别 </li>
</ul>
<p>也有以上的情况。在Solaris的man手册里, 用后者的方式进行区别. 从多线程程序里安全调用的话,就叫做"Safe", 而且, 在多线程中能够并发(concurrency)地执行这个函数的处理的话,<a href="http://docs.sun.com/db/doc/806-6867/6jfpgdco6?a=view"><u><font color=#0000ff>好像</font></u></a>就叫做"MT-Safe"。</p>
<p>&nbsp;</p>
<p>嗯, 因为比较详细的, 如果不是在对于执行速度要求比较苛刻的环境中编写代码的话, 单单地意识到「是否线程安全」就足够了,不是吗。</p><img src ="http://www.cppblog.com/lymons/aggbug/70835.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2009-02-01 15:41 <a href="http://www.cppblog.com/lymons/archive/2009/02/01/70835.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则6：遵守多线程编程的常识（上）</title><link>http://www.cppblog.com/lymons/archive/2008/12/29/70678.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 29 Dec 2008 09:11:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/12/29/70678.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/70678.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/12/29/70678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/70678.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/70678.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/-JxHmob-mMtyqalN4ZMd6Q?feat=embedwebsite"><img src="http://lh5.ggpht.com/_tDmtFJ6XjL4/SKgpAIswdJI/AAAAAAAAByQ/e9rjYUbjkyI/s400/DSC06746.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<h3>UNIX上C++程序设计守则(6)<span class=hatena-star-comment-container><img class=hatena-star-comment-button title=Comments style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; DISPLAY: none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt=Comments src="http://s.hatena.ne.jp/images/comment.gif"></span><span class=hatena-star-star-container><img class=hatena-star-add-button title="Add star" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt 3px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<blockquote>
<p><strong>准则6: 遵守多线程编程的常识</strong></p>
<br>
<ol>
    <li>要准确把握在POSIX标准的函数中，那些函数是非线程安全的，一定不要使用
    <li>要让自己编写的函数符合线程安全
    <ul>
        <li>在访问共享数据/变量之前一定要先锁定
        <li>如果使用C++的话，一定要注意函数的同步方法 </li>
    </ul>
    </li>
</ol>
</blockquote>
<p>说明: (1) 要准确把握那些非线程安全的函数，一定不要使用</p>
<br>
<p>如果在POSIX平台上进行多线程编程时，有几个最基本的知识，也就是所说的&#8220;常识&#8221;，希望大家一定要严格遵守。</p>
<br>
<p>...首先、我们要理解&#8220;线程安全&#8221;的意思。线程安全的函数就是指，&#8220;一个能被在多个线程同时调用也不会发生问题的函数&#8221;。这样的函数通常要满足以下几个的特质。</p>
<ol>
    <li>不要操作局部的静态变量(函数内的static变量)和全局静态数据(全局变量，函数外的静态变量)。而且，也不要调用其他的非线程安全的函数
    <li>如果要操作这样的变量的话，事先必须使用互斥锁mutex进行同步，否则一定要限制多个线程同时对它的访问 </li>
</ol>
<p>那么、在POSIX标准的函数里面，也有不满足上述条件的。由于历史遗留问题，一些函数的识别标识(signature)的定义没有考虑到线程安全的问题，所以不管怎么做都不能满足上述的条件。例如，看看 <a href="http://www.opengroup.org/onlinepubs/009695399/functions/localtime.html">localtime函数</a>吧。它的定义的识别标识（signature）如下：</p>
<blockquote>
<p>struct tm *localtime(const time_t *timer);</p>
</blockquote>
<p>localtime 函数是，把一个用整数形式表示的时刻(从1970/1/1到现在为止的秒数)、转换成一个能让人容易明白的年月日形式表示出来的tm结构体并返回给调用者的函数。根据规格说明、返回出来的tm结构体是不需要free()掉，也不能释放的。这个函数典型的实现就像下面的代码那样：</p>
<pre>struct tm *localtime(const time_t *timer) {<br>  static struct tm t;<br>  <br>  /* ... 从timer参数里算出年月日等数值 ... */<br><br>  t.tm_year = XXX;<br>  /* ...把它们填入到结构体内... */<br>  t.tm_hour = XXX;<br>  t.tm_min  = XXX;<br>  t.tm_sec  = XXX;<br><br>  return &amp;t;<br>}<br></pre>
<p>这个函数如果被像下面那样使用的话，就会有漏洞：</p>
<blockquote>
<ol>
    <li>在线程A里执行 ta = localtime(x); <br>
    <li>在线程B里执行 tb = localtime(y); <br>
    <li>线程A参照ta结构体里的数据 &#8594; 就发现这些数据是一些奇怪的值！ </li>
</ol>
</blockquote>
<p>...在函数的说明手册里对这个问题也没有做过详细的说明。关于这个漏洞，在localtime函数即使使用了mutex锁也不能被回避掉。所以，这个函数定义的识别标识是不行滴。<br>[<span style="COLOR: #2058ff; FONT-STYLE: italic">译者lymons注：在多个线程里调用localtime函数之所以有问题的原因是，localtime函数里返回的tm构造体是一个静态的结构体，所以在线程A里调用localtime函数时，该结构体被赋予正确的值；而在线程A参照这个结构体之前，线程B又调用localtime的话，这个静态的结构体又被赋予新的一个值。因此在线程A对这个结构体的访问都是基于一个错误的值进行的]</span><br></p>
<br>
<p>正因为如此，就像上面说过的POSIX规格(SUSv3)里整齐的<a href="http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html">定义了</a>一些&#8220;非线程安全的函数&#8221;。在"&#167;2.9.1 Thread-Safety" 这里登载了的非线程安全的函数有如下所示。</p>
<blockquote>
<p>asctime, basename, catgets, crypt, ctime, dbm_clearerr, dbm_close, dbm_delete, dbm_error, dbm_fetch, dbm_firstkey, dbm_nextkey, dbm_open, dbm_store, dirname, dlerror, drand48, ecvt, encrypt, endgrent, endpwent, endutxent, fcvt, ftw, gcvt, getc_unlocked, getchar_unlocked, getdate, getenv, getgrent, getgrgid, getgrnam, </p>
<p>(省略)</p>
</blockquote>
<p>对于在规格中被定义为非线程安全的函数，应该制定一个避免使用它们的规则出来，并且制作一个能够自动检查出是否使用了这些函数的开发环境，应该是比较好的。</p>
<br>
<p>反之，在这里没有被登载的POSIX标准函数都被假定为 "shall be thread-safe" 的、所以在实际的使用中可以认为在多线程环境里是没有问题的(而且在使用的平台上没有特别地说明它是非线程安全的话)。</p>
<br>
<p>另外，有几个非线程安全的函数，都准备了一个备用的线程安全版本的函数(仅仅是变更了函数的识别标识)。像这些函数为了与原版进行区别都在其函数名后面添加了 _r 这个后缀<span class=footnote><a title=C言語にゃ関数のオーバーロードはありません href="http://d.hatena.ne.jp/yupo5656/20040809/p1#20040809f1" name=20040809fn1>*1</a></span>。例如，asctime函数就有线程安全版本的函数asctime_r。在规格说明中是否定义了备用函数，可以试着点击刚才的<a href="http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html">那个网页</a>里面的函数名就可以看到。点击 <a href="http://www.opengroup.org/onlinepubs/009695399/functions/rand.html">rand函数</a>就可以看到，</p>
<blockquote>
<p>[TSF] int rand_r(unsigned *seed);</p>
</blockquote>
<p>用[TSF]这样的文字标记出来的函数吧。这就是备用函数。在一览中没有记载出来的函数(备注： 稍微有点儿出入。请参照<a href="http://d.hatena.ne.jp/yupo5656/20040821#p3">这里</a>)、据我所知还有下面的备用函数。</p>
<blockquote>
<p>asctime_r, ctime_r, getgrgid_r, getgrnam_r, getpwnam_r, getpwuid_r, gmtime_r, localtime_r, rand_r, readdir_r, strerror_r, strtok_r</p>
</blockquote>
<p>还有，在规格以外，还准备了很多的下面那样的函数。</p>
<blockquote>
<p>gethostbyname_r, gethostbyname2_r</p>
</blockquote>
<p>在最近的操作系统中、也使用 <a href="http://www.opengroup.org/onlinepubs/009695399/functions/getaddrinfo.html">getaddrinfo</a> API函数来解决IPv6名字对应的问题。gethostbyname系列的API都是比较陈旧的函数了、所以使用前面的函数还是比较好吧<span class=footnote><a title="ネットワーク関係のAPIの古い/新しいについては、ASIN:4756142362:title という良書があります" href="http://d.hatena.ne.jp/yupo5656/20040809/p1#20040809f2" name=20040809fn2>*2</a></span>。根据规格SUSv3，getaddrinfo也是线程安全的：</p>
<blockquote>
<p>The freeaddrinfo() and getaddrinfo() functions shall be thread-safe.</p>
</blockquote>
<p>在多线程编程中，不要使用非线程安全的函数，而他们的备用函数可以放心地积极的去使用。</p>
<br>
<p>&#8594;<a href="http://d.hatena.ne.jp/yupo5656/20040809/p2" target=_blank>后续</a></p>
<div class=footnote>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040809/p1#20040809fn1" name=20040809f1>*1</a>：在C言語里函数不能重载，所以只能添加一个新的函数</p>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040809/p1#20040809fn2" name=20040809f2>*2</a>：跟网络有关的API哪些是新的哪些是旧的，可以参考 <a href="http://d.hatena.ne.jp/asin/4756142362/casactdiary-22">IPv6网络编程 (network technology series)</a> 这本好书<br></p>
</div><img src ="http://www.cppblog.com/lymons/aggbug/70678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-12-29 17:11 <a href="http://www.cppblog.com/lymons/archive/2008/12/29/70678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则5：尽可能避免线程的延迟撤销处理</title><link>http://www.cppblog.com/lymons/archive/2008/12/25/70227.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Thu, 25 Dec 2008 06:08:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/12/25/70227.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/70227.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/12/25/70227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/70227.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/70227.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/P_CgzJmZxGiqyXKDtvI7AQ?feat=embedwebsite"><img src="http://lh4.ggpht.com/_tDmtFJ6XjL4/SKgo9ZPKBWI/AAAAAAAAByI/kMV02kSprhc/s400/DSC06743.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<h3>[<a class=sectioncategory href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2a%5bC%2b%2b%5d">C++</a>] UNIX上C++程序设计守则(5)<span class=hatena-star-comment-container><img class=hatena-star-comment-button title=Comments style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; DISPLAY: none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt=Comments src="http://s.hatena.ne.jp/images/comment.gif"></span><span class=hatena-star-star-container><img class=hatena-star-add-button title="Add star" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt 3px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<br>
<blockquote>
<p><strong>准则5: 尽可能避免线程中做延迟撤销的处理<br></strong></p>
<ul>
    <li>线程的异步撤消是指：一个线程发出中断其他线程的处理的一个动作
    <li>延迟撤消因为是规格自由度比较高、所以根据OS和C库函数的版本它也有各式各样的动作
    <ul>
        <li>要想在不同的环境下都能稳定的动作的话，就必须要详细调查运行环境和，对C库函数进行抽象化，做必要的条件编译
        <li>在C++中、「撤消发生时的对象释放」的实现不具有可移植性 </li>
    </ul>
    </li>
</ul>
<ul>
    <li>线程撤销要慎重使用。在C++里不要使用 </li>
</ul>
</blockquote><br>
<p>说明:</p>
<br>
<p>在前面我们已经讲过，线程的撤消分为「异步」「延迟」这两种类型、并且「异步撤消」也是非常容易引起各种复杂问题的元凶。</p>
<br>
<p>那么，现在要在程序中除掉「延迟撤消」。延迟撤消虽然不会像异步撤消那样会引起各种各样的问题、但是、注意事项还是有很多的。只有把下面的这些注意事项全部都把握之后才能放心使用。</p>
<br>
<p><strong>注意事项1: 要好好把握撤消点</strong></p>
<br>
<p>和异步撤消不一样的是、撤消处理一直会被延迟到在代码上明示出来的撤消点之后才会被执行。如果编写了一个具有延迟撤消可能的代码、代码中的那条语句是撤消点、必须要正确的把握。</p>
<br>
<p>首先、调用过pthread_testcancel函数的地方就变成撤消点了。当然这个函数是、仅仅为了「变成延迟撤消」的目的而设置出来的函数。除此之外、某些标准库函数被调用后会不会变成撤消点是在规格(SUSv3)中决定的。<a href="http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html#tag_02_09_05_02">请参照规格说明</a>、有下面的函数一览。</p>
<br>
<pre>下面的函数<span style="FONT-WEIGHT: bold">是</span>撤消点<strong></strong><br><br>accept, aio_suspend, clock_nanosleep, close, connect, creat, fcntl, fdatasync,<br>fsync, getmsg, getpmsg, lockf, mq_receive, mq_send, mq_timedreceive,<br>mq_timedsend, msgrcv, msgsnd, msync, nanosleep, open, pause, poll, pread,<br>pselect, pthread_cond_timedwait, pthread_cond_wait, pthread_join,<br>pthread_testcancel, putmsg, putpmsg, pwrite, read, readv, recv, recvfrom,<br>(略)<br></pre>
<pre>下面的函数<span style="FONT-WEIGHT: bold">不是</span>撤消点<strong></strong><br><br>access, asctime, asctime_r, catclose, catgets, catopen, closedir, closelog,<br>ctermid, ctime, ctime_r, dbm_close, dbm_delete, dbm_fetch, dbm_nextkey, dbm_open,<br>dbm_store, dlclose, dlopen, endgrent, endhostent, endnetent, endprotoent,<br>endpwent, endservent, endutxent, fclose, fcntl, fflush, fgetc, fgetpos, fgets,<br>fgetwc, fgetws, fmtmsg, fopen, fpathconf, fprintf, fputc, fputs, fputwc, fputws,<br>(略)<br></pre>
<br>
<p>看到这些我想已经明白了、但是在规格中也说明了「能否成为撤消点跟具体的实现相关的函数」也是多数存在的。原因是、为了可移植性、保证「在一定的时间内让线程的延迟撤消完成」是很困难的事情<span class=footnote><a title="よく問題になるのは gethostbyname() 関数" href="http://d.hatena.ne.jp/yupo5656/20040725/p2#20040725f1" name=20040725fn1>*1</a></span>。做的不好的话、只要稍微一提升OS的版本就可能让做出来的程序产品不能动作。</p>
<br>
<p>即使是这样那还想要使用延迟撤消吗？</p>
<br>
<p><strong>注意事项2: 实现要知道cleanup函数的必要性</strong></p>
<br>
<p>可能被延迟撤销的线程在运行的过程中，要申请资源的场合，一定要考虑到以下的几点，否则就会编制出含有资源丢失和死锁的软件产品。</p>
<br>
<p>例如编写的下面的函数就不能被安全的延迟撤销掉。</p>
<pre>void* cancel_unsafe(void*) {<br>    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;<br>    pthread_mutex_lock(&amp;mutex);                     // 此处不是撤消点<br>    struct timespec ts = {3, 0}; nanosleep(&amp;ts, 0); // 经常是撤消点<br>    pthread_mutex_unlock(&amp;mutex);                   // 此处不是撤消点<br>    return 0;<br>}<br>int main(void) {<br>    pthread_t t;<br>    // pthread_create后马发上收到一个有效的延迟撤消的要求<br>    pthread_create(&amp;t, 0, cancel_unsafe, 0);<br>    pthread_cancel(t);<br>    pthread_join(t, 0);<br>    cancel_unsafe(0); // 发生死锁！<br>    return 0;<br>}<br></pre>
<p>在上面的样例代码中、nanosleep执行的过程中经常会触发延迟撤销的最终动作，但是这个时候的mutex锁还处于被锁定的状态。而且、线程一被延迟撤消的话就意味着没有人去释放掉这个互斥锁了<span class=footnote><a title=非同期キャンセルとmalloc関数の例に似ていますね href="http://d.hatena.ne.jp/yupo5656/20040725/p2#20040725f2" name=20040725fn2>*2</a></span>。因此、在下面的main函数中调用同样的cancel_unsafe函数时就会引起死锁了。</p>
<br>
<p>为了回避这个问题、利用pthread_cleanup_push函数在撤消时释放掉互斥锁的话就OK了，也就不会死锁了。</p>
<pre>// 新增清除函数<br>void cleanup(void* mutex) { <br>    pthread_mutex_unlock((pthread_mutex_t*)mutex);<br>}<br><br>// 粗体字部分是新增的语句<br>void* cancel_unsafe(void*) {<br>    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;<br>    <strong>pthread_cleanup_push(cleanup, &amp;mutex);</strong><br>    pthread_mutex_lock(&amp;mutex);<br>    struct timespec ts = {3, 0}; nanosleep(&amp;ts, 0);<br>    pthread_mutex_unlock(&amp;mutex);<br>    <strong>pthread_cleanup_pop(0);</strong><br>    return 0;<br>}<br></pre>
<br>
<p><strong>注意事项3: 实现要清楚延迟撤消和C++之间的兼容度</strong></p>
<br>
<p>使用C语言的场合，利用上面的pthread_cleanup_push/pop函数就能安全地执行延迟撤消的动作，但是在C++语言的场合就会出现其他的问题。<strong>C++与延迟撤消之间的兼容度是非常差的</strong>。具体的表现有以下两个问题:</p>
<br>
<ol>
    <li>执行延迟撤消的时候，内存栈上的对象的析构函数会不会被调用跟具体的开发环境有关系
    <ul>
        <li>GCC3版本就不会调用。
        <li>Solaris和Tru64 UNIX下的原生编译器的场合，就调用析构函数(好像) </li>
    </ul>
    <li>pthread_cleanup_push/pop函数和C++的异常处理机制之间有着怎样的相互影响也能具体环境有关 </li>
</ol>
<p>不调用析构函数，或者在抛出异常的时候不能做cleanup处理，经常是发生内存泄漏，资源丢失，程序崩溃，死锁等现象的原因。令人意外的是对于这个深层次的问题，就连<a href="http://www.boost.org/">Boost C++库</a>都束手无策。</p>
<blockquote>
<p>[Q] Why isn't thread cancellation or termination provided?</p>
<p>[A] There's a valid need for thread termination, so at some point Boost.Threads probably will include it, but only after we can find a truly safe (and portable) mechanism for this concept.</p>
</blockquote>
<p>先必须确保对象的自由存储，而后全都让cleanup函数去释放对象的方法也有，但是这次是牺牲了异常安全性。<br>（原文没有看明白：オブジェクトを必ずフリーストア上に確保し、解体を全て、クリーンナップハンドラに行わせる手もありますが、今度は例外安全性が犠牲になるでしょう。）<br></p>
<br>
<p>应该说的是，在使用C++的工程里不对线程进行延迟撤消处理还是比较实际的。</p>
<div class=footnote>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040725/p2#20040725fn1" name=20040725f1>*1</a>：好的问题是 gethostbyname()函数<br></p>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040725/p2#20040725fn2" name=20040725f2>*2</a>：异步撤消跟malloc函数的例子很相似</p>
<p class=footnote>原文地址：http://d.hatena.ne.jp/yupo5656/20040725/p2<br></p>
</div><img src ="http://www.cppblog.com/lymons/aggbug/70227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-12-25 14:08 <a href="http://www.cppblog.com/lymons/archive/2008/12/25/70227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则4：请不要做线程的异步撤消的设计</title><link>http://www.cppblog.com/lymons/archive/2008/12/19/69810.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Fri, 19 Dec 2008 03:01:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/12/19/69810.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/69810.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/12/19/69810.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/69810.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/69810.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/U59EFbF-K5HYiFxmWMTVOw?feat=embedwebsite"><img src="http://lh4.ggpht.com/_tDmtFJ6XjL4/SKgplycktEI/AAAAAAAABzw/FTSqL8FaB2I/s400/DSC_0240.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<div class=section>
<h3>原文地址：http://d.hatena.ne.jp/yupo5656/20040724/p1<br></h3>
<h3><a href="http://d.hatena.ne.jp/yupo5656/20040724/p1" name=p1><span class=sanchor>■</span></a>[<a class=sectioncategory href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2a%5bC%2b%2b%5d">C++</a>] <strong><font size=5>UNIX上的C++程序设计守则</font></strong> (4)<span class=hatena-star-comment-container><img class=hatena-star-comment-button title=Comments style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; DISPLAY: none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt=Comments src="http://s.hatena.ne.jp/images/comment.gif"></span><span class=hatena-star-star-container><img class=hatena-star-add-button title="Add star" style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0pt; BORDER-TOP: medium none; PADDING-LEFT: 0pt; PADDING-BOTTOM: 0pt; MARGIN: 0pt 3px; VERTICAL-ALIGN: middle; BORDER-LEFT: medium none; CURSOR: pointer; PADDING-TOP: 0pt; BORDER-BOTTOM: medium none" tabIndex=0 alt="Add star" src="http://s.hatena.ne.jp/images/add.gif"></span></h3>
<blockquote>
<p><strong>铁则4: 请不要做线程的异步撤消的设计</strong></p>
<ul>
    <li>线程的异步撤销是指： 某个线程的执行<span style="FONT-WEIGHT: bold">立刻</span>被其他线程给强制终止了
    <li>请不要单单为了让&#8220;设计更简单&#8221;或者&#8220;看起了更简单&#8221;而使用线程的异步撤消 </li>
</ul>
<p>咋一看还是挺简单的。但是搞不好可能会引起各种各样的问题。请不要在不能把握问题的实质就做出使用线程的异步撤消的设计！</p>
</blockquote>
<p>在pthread的规格说明中，允许一个线程可以强制中断某个线程的执行。这就是所说的异步撤消。</p>
<br>
<p>线程的撤消有下面的两种方式。</p>
<ul>
    <li>方式1： 异步撤消(PTHREAD_CANCEL_ASYNCHRONOUS)
    <ul>
        <li>撤销动作是马上进行的 </li>
    </ul>
    <li>方式2： 延迟撤销(PTHREAD_CANCEL_DEFERRED)
    <ul>
        <li>撤消动作，是让线程的处理一直被延迟到撤消点才会去执行 </li>
    </ul>
    </li>
</ul>
<p>还有，到底是用哪种撤消方式，不是撤消侧，而是被撤销侧能够决定的<span class=footnote><a title=pthread_setcanceltype関数 href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724f1" name=20040724fn1>*1</a></span>。另外，在被撤销侧也能够选择完全禁止撤消的这种方式 <span class=footnote><a title=pthread_setcancelstate関数 href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724f2" name=20040724fn2>*2</a></span>。</p>
<br>
<h4>会造成什么问题呢</h4>
<p>那么，让我看看乱用线程的异步撤消会引起什么问题呢。看过准则3的人可能会知道，在下面的脚本里，被撤销线程以外的任意一个线程会被死锁。</p>
<blockquote>
<ol>
    <li>线程1中调用malloc函数正在做内存分配的过程中，线程2异步撤消了线程1的处理
    <li>线程1马上被撤销，但是malloc函数中的互斥锁就没有线程去解除了
    <li>后面的任意一个线程如果再次调用malloc函数的话就会马上导致该线程死锁 </li>
</ol>
</blockquote>
<p>在这个例子中使用了malloc函数，但是其他的危险函数还有很多。</p>
<br>
<p>反之，即使做了异步撤消也没有问题的函数也有少数存在的、我们把它们叫做「async-cancel safe函数」或者「异步撤消安全函数」。在一些商用UNIX<span class=footnote><a title=SolarisやHP-UXなど href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724f3" name=20040724fn3>*3</a></span>中、OS提供的api函数的文档说明中有async-cancel safety的记载、但是在Linux(glibc)里就很遗憾，几乎没有相关的说明。</p>
<br>
<p>在这儿，参看规格(SUSv3)的话，会发现，、<a href="http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html#tag_02_09_05_04">描述异步撤消安全的函数只有3个</a>。</p>
<ol>
    <li>pthread_cancel
    <li>pthread_setcancelstate
    <li>pthread_setcanceltype </li>
</ol>
<p>而且、里面还有"No other functions are required to be async-cancel-safe"这样的记载。因此，Linux的场合，如果在文档里没有记载成async-cancel safety的函数，我们还是把它假定成不安全的函数为好!</p>
<br>
<h4>如何避免这些问题呢</h4>
<p>在多线程编程中为了安全的使用异步撤消处理、有没有回避死锁的方法呢？我们试着想了几个。他们与准则3里的线程+fork的场合的回避策很相似。</p>
<br>
<p><strong>回避方法1: 被撤销线程中，只能使用异步撤消安全函数</strong></p>
<br>
<p>首先，被撤销线程中，只能使用异步撤消安全函数。但是这个方法</p>
<ul>
    <li>在规格说明中只有3个异步撤消安全的函数
    <li>这些以外的函数是不是异步撤消安全(商用UNIX)、因为没有说明文档我们不清楚(Linux) </li>
</ul>
<p>中有以上的两点，所以这个回避方法几乎不现实。</p>
<br>
<p><strong>回避方法2: 被撤销线程中，在做非异步撤消安全处理的过程中，再把撤消方式设置成「延迟」或者是「禁止」</strong></p>
<br>
<p>第二个是，被撤销线程在做非异步撤消安全处理的过程中，把撤消方式再设定成「延迟」或者「禁止」。对于这个方法</p>
<ul>
    <li>就像方法1写的那样、要把我那个函数是异步撤消安全的一时还是挺麻烦的
    <li>在任意的场所并不能保证撤消动作会被马上执行
    <ul>
        <li>例如，再设定成「延迟」后的一段时间内如果撤消发生时、某个正在阻塞的I/O函数是否能够被解除阻塞还是挺微妙的
        <li>如果设定成撤消禁止的话，则撤消会被屏蔽掉 </li>
    </ul>
    </li>
</ul>
<p>有上面样的问题、会导致「一精心设计撤消方式的替换，从一开始就使用延迟撤消还不够好」这样的结果。所以这几乎是不好的一个回避策。</p>
<br>
<p><strong>回避方法3： 使用pthread_cleanup_push函数，登录异步撤消时的线程数据清除的回调函数</strong></p>
<br>
<p>第三种则是，用pthread_cleanup_push函数、登录一个在异步撤消发生时的数据清除的回调函数。这和在准则3中介绍的pthread_atfork函数有点儿类似。用这个函数登录的回调函数来清除线程的数据和锁，就可以回避死锁了。</p>
<br>
<p>...但是，pthread_cleanup_push函数登录的回调函数，在「延迟撤消」的场合是不能被调用的。因此、这个回避方法对于异步撤消没有什么大的作用。</p>
<br>
<p><strong>回避方法４： 不要执行异步撤消处理</strong></p>
<br>
<p>最后是、不要执行异步撤消处理。反而代之的是、</p>
<ul>
    <li>设计成不依赖使用异步撤消那样的处理 </li>
</ul>
<p>或者<br></p>
<ul>
    <li>不得不使用线程撤消的话、不做异步撤消而作延迟撤消的处理 </li>
</ul>
<p>这是比较实际的做法，是我们值得推荐的。</p>
</div>
<div class=footnote>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724fn1" name=20040724f1>*1</a>：pthread_setcanceltype函数</p>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724fn2" name=20040724f2>*2</a>：pthread_setcancelstate函数</p>
<p class=footnote><a href="http://d.hatena.ne.jp/yupo5656/20040724/p1#20040724fn3" name=20040724f3>*3</a>：Solaris和HP-UX等</p>
</div><img src ="http://www.cppblog.com/lymons/aggbug/69810.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-12-19 11:01 <a href="http://www.cppblog.com/lymons/archive/2008/12/19/69810.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎么利用设计模式来更有效的使用共享内存</title><link>http://www.cppblog.com/lymons/archive/2008/06/01/51842.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Jun 2008 13:41:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/06/01/51842.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/51842.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/06/01/51842.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/51842.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/51842.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Linux中处理来自共享对象的同步事件 怎么利用设计模式来更有效的使用共享内存&nbsp;级别：中等&nbsp;Sachin Agrawal (sachin_agrawal@in.ibm.com), Senior Software Engineer, IBM Software Labs, IndiaSwati P. Udas (swatudas@in.ibm.com), Softw...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2008/06/01/51842.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/51842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-06-01 21:41 <a href="http://www.cppblog.com/lymons/archive/2008/06/01/51842.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则1：不依赖于信号收发的设计</title><link>http://www.cppblog.com/lymons/archive/2008/06/01/51838.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Jun 2008 12:27:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/06/01/51838.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/51838.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/06/01/51838.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/51838.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/51838.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/Cu3u8ToMIpTlyvy2BZ38CQ?feat=embedwebsite"><img src="http://lh3.ggpht.com/_tDmtFJ6XjL4/SKgpsaWlkCI/AAAAAAAAB0A/JjI0ZDf-YKI/s400/DSC_0256.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<p><a href="http://d.hatena.ne.jp/yupo5656/20040712/p1"><strong><font color=#0000ff size=5><u>■</u></font></strong></a><strong><font size=5>[</font></strong><a href="http://d.hatena.ne.jp/yupo5656/searchdiary?word=%2a%5bC%2b%2b%5d"><strong><font color=#0000ff size=5><u>C++</u></font></strong></a><strong><font size=5>] UNIX上的C++程序设计守则(1) </font></strong></p>
<p>原文:http://d.hatena.ne.jp/yupo5656/20040712/p1</p>
<p>Unix跟Windows等那些&#8221;对于开发者易于使用&#8221;的OS比起来，在信号和线程的利用方面有诸多的限制。但是即使不知道这些知识就做构架设计和实现的情况也随处可见。这个就是那些经常不能再现的bug的温床吧。</p>
<p>因此，我想分成几回来写一些准则来防止陷入到这些圈套里。<strong></strong></p>
<p><strong>准则</strong><strong>1</strong><strong>：不依赖于信号收发的设计</strong></p>
<p>&#183;给其他进程以及自己发送异步信号并改变处理流程的设计不要做</p>
<ul>
    <li>&nbsp;异步信号是值用kill系统调用来创建?发送的信号、例如SIGUSR1,SIGUSR2,SIGINT,SIGTERM 等
    <li>简单的使用忽略信号(SIG_IGN)则没有问题 </li>
</ul>
<p>&#183;不要把线程和信号一起使用&nbsp;</p>
<ul>
    <li>这将使程序动作的预测和调试变得很困难 </li>
</ul>
<p>&nbsp;</p>
<p>说明：</p>
<p>同步信号是指，因为某些特定的操作<a title=例えばヌルポインタ参照 href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712f1"><u><font color=#0000ff>*1</font></u></a>而引起向自身进程发送某些特定的信号，例如SIGSEGV,SIGBUS,SIGPIPE,SIGSYS,SIGILL,SIGFPE。异步信号就是这些以外的信号。在什么时机发送异步信号并不能被预测出来。我们会在程序里追加收到某些信号时做一些特殊处理(信号处理函数)的函数。那么根据收到的信号就跳到信号处理函数的程序就叫做&#8221;在任意代码处都能发生跳转&#8221;的程序。这样的程序往往隐藏这下面的那些问题：</p>
<ol>
    <li><strong>容易引入</strong><strong>BUG</strong>。&#8221;任意的代码&#8221;虽然也包含&#8221;执行C/C++里面的一条语句的过程中&#8221;的意思，但这很容易跳出程序员的正常思维以及默认的假定条件。编写程序的时候往往需要考虑比C++异常分支还要多得多的分支情况。
    <li><strong>使测试项目激增</strong>。即使根据白盒测试达成100%的分支覆盖,也不能网罗到因为接受信号而发生的跳转分支处理。也就是说做到100%的网罗信号跳转分支的测试是不能全部实现的。一般的，加上要考虑&#8221; 在实行某个特定代码时因为接受到信号而发生的误操作&#8221;这样的BUG会经常发生<a title="id:yupo5656:20040703 のsigsafeの記事を参照" href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712f2"><u><font color=#0000ff>*2</font></u></a>的这种情况，测试困难往往就是导致软件的品质低下的诱因。 </li>
</ol>
<p>&nbsp;</p>
<p>根据经验，&#8221;当检查到子进程结束(接收到SIGCHLD信号)时，要做必要的处理&#8221;像这样的信号处理不管做什么都是有必要的情况会有，但是除此以外的信号处理，例如</p>
<ul>
    <li>把自己的状态用信号告诉其他进程
    <li>主线程在输入输出函数里发送信号给被阻塞的子线程，并解除阻塞 </li>
</ul>
<p>等，是应该事先好好好好考虑过后再去做实际的实现。前者的话，如果不强制在&#8221;普通的&#8221;进程间进行通信的话可能会很好，后者是特意要使用线程，也要应该按照即使阻塞了也不能发生问题那样再设计。</p>
<p>不管怎么样，如果必须要使用信号的话，也要先全部<a title="とりあえず「鉄則２」を :-) " href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712f3"><u><font color=#0000ff>*3</font></u></a>理解这些陷阱以及，和多线程软件设计的场合一样或者说比它更严格的制约.注意事项都需要铭记在心里。</p>
<p><a href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712fn1"><u><font color=#0000ff>*1</font></u></a>：例如，引用空指针</p>
<p><a href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712fn2"><u><font color=#0000ff>*2</font></u></a>：参照 <a href="http://d.hatena.ne.jp/yupo5656/20040703"><u><font color=#0000ff>id:yupo5656:20040703</font></u></a> 的sigsafe说明</p>
<p><a href="http://d.hatena.ne.jp/yupo5656/20040712/p1#20040712fn3"><u><font color=#0000ff>*3</font></u></a>：暂时先掌握&#8221;准则2&#8221;:-) </p><img src ="http://www.cppblog.com/lymons/aggbug/51838.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-06-01 20:27 <a href="http://www.cppblog.com/lymons/archive/2008/06/01/51838.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则2：要知道信号处理函数中可以做那些处理</title><link>http://www.cppblog.com/lymons/archive/2008/06/01/51837.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Jun 2008 12:22:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/06/01/51837.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/51837.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/06/01/51837.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/51837.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/51837.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/9Wx9xHxVzU9NEPuVrXA2_g?feat=embedwebsite"><img src="http://lh5.ggpht.com/_tDmtFJ6XjL4/SLEyEeVOZuI/AAAAAAAAB8Q/_NtjhrtjdVY/s400/DSC_0307.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
<p><span><font size=5><strong><span>UNIX</span></strong><strong><span>上</span></strong><strong><span>C++程序</span></strong><strong><span>设计守则</span></strong><strong><span> (2)</span></strong><strong><span> </span></strong></font><strong><span><br></span></strong><strong><span>原文地址：</span></strong><strong><span><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2"><u><font color=#0000ff>http://d.hatena.ne.jp/yupo5656/20040712/p2</font></u></a><br><br></span></strong><strong><span></span></strong></span></p>
<p><span><strong><span>准则</span></strong><strong><span>2: </span></strong><strong><span>要知道信号处理函数中可以做那些处理</span></strong><span><br></span><span><span>&#183;<span> </span></span></span><span>在用</span><span>sigaction</span><span>函数登记的信号处理函数中可以做的处理是被严格限定的</span><span> <br></span><span><span>&#183;<span> </span></span></span><span>仅仅允许做下面的三种处理</span><span> <br></span><span><span>&nbsp;&nbsp; 1.<span>&nbsp;</span></span></span><span>局部变量的相关处理</span><span> <br></span><span><span>&nbsp;&nbsp; 2.<span>&nbsp;</span></span></span><span>&#8220;volatile sig_atomic_t&#8221;</span><span>类型的全局变量的相关操作</span><span> <br></span><span><span>&nbsp;&nbsp; 3.<span>&nbsp;</span></span></span><span>调用异步信号安全的相关函数</span><span> <br></span><span><span>&#183;<span> </span></span></span><span>以外的其他处理不要做</span><span>！</span></span></p>
<p><span><span></span><span>&nbsp;<br></span><strong><font size=4><span>说明</span><span>：</span></font></strong><span><br></span><span>因为在收到信号时要做一些处理，那通常是准备一个信号处理函数并用</span><span>sigaction</span><span>函数把它和信号名进行关联的话就</span><span>OK</span><span>了。但是，在这个信号处理函数里可以做的处理是像上面那样被严格限定的。没有很好掌握这些知识就随便写一些代码的话就会引起下面那样的问题</span><span>：</span><span><br></span><span><span>&#183;<span>&nbsp;</span></span></span><strong><span>问题</span></strong><strong><span>1: </span></strong><strong><span>有程序死锁的危险</span></strong><span> <br></span><span><span>&nbsp;&nbsp; o<span>&nbsp; </span></span></span><span>这是那些依赖于某一时刻，而且错误再现比较困难的</span><span>BUG</span><span>产生的真正原因</span><span> <br></span><span><span>&nbsp;&nbsp; o<span>&nbsp; </span></span></span><span>死锁是一个比较典型的例子，除此之外还能引起函数返回值不正确，以及在某一函数内执行时突然收到</span><span>SEGV</span><span>信号等的误操作。</span><span><br></span><span>&nbsp;&nbsp; <font color=#000099 size=3><em>◆</em></font></span><font size=3><em><font color=#000099><span>译者注</span><span>1</span><span>：</span><strong><span>SEGV</span></strong><span>通常</span><span>发</span><span>生在</span><span>进</span><span>程</span><span>试图访问</span><span>无效内存区域</span><span>时</span><span>（可能是个</span><span>NULL</span><span>指</span><span>针</span><span>，或超出</span><span>进</span><span>程空</span><span>间</span><span>之外的内存地址）。当</span><span>bug</span><span>原因和</span><strong><span>SEGV</span></strong><span>影响在不同</span><span>时间</span><span>呈</span><span>现时</span><span>，它</span><span>们</span><span>特</span><span>别难</span><span>于捕</span><span>获</span><span>到。</span></font></em></font></span></p>
<p><span><font size=3><em><font color=#000099><span></span></font></em></font><span><br></span><span><span>&#183;<span>&nbsp;</span></span></span><strong><span>问题</span></strong><strong><span>2: </span></strong><strong><span>由于编译器无意识的优化操作，有导致程序紊乱的危险</span></strong><span> <br></span><span><span>&nbsp;&nbsp; o<span>&nbsp; </span></span></span><span>这是跟编译器以及编译器优化级别有关系的</span><span>bug</span><span>。它也是&#8220;编译器做了优化处理而不能正常动作&#8221;，&#8220;因为</span><span>inline</span><span>化了程序不能动作了&#8221;，&#8220;变换了</span><span>OS</span><span>了程序也不能动作&#8221;等这些解析困难</span><span>bug</span><span>产生的原因。</span></span></p>
<p><span><span></span><span>&nbsp;<br></span><span>还是一边看具体的代码一边解说吧。在下面的代码里至少有三个问题，根据环境的不同很可能引起不正确的动作</span><a target=_blank name=20040712fn1></a><a title=sigaction関数を呼ぶ前にSIGINTを受けるとプログラムが終了してしまう、というのはとりあえず除外しましょう href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712f1" target=_blank><span><sup><span><u><font color=#0000ff>*1</font></u></span></sup></span></a><span>、</span><span>按照次序来说明里面的错误</span><span>。</span></span></p>
<p><span><span>&nbsp;</span></span></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;gSignaled;<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img id=Codehighlighter1_43_114_Open_Image onclick="this.style.display='none'; codehighlighter1_43_114_open_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top codehighlighter1_43_114_closed_text.style.display="'inline';" codehighlighter1_43_114_closed_image.style.display="'inline';" ;="" none=""><img id=Codehighlighter1_43_114_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; codehighlighter1_43_114_closed_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top ;="" none="" codehighlighter1_43_114_open_text.style.display="'inline';" codehighlighter1_43_114_open_image.style.display="'inline';"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;sig_handler(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;signo)&nbsp;</span><span id=Codehighlighter1_43_114_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_43_114_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;std::printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">signal&nbsp;%d&nbsp;received!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;signo);<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;gSignaled&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img id=Codehighlighter1_131_336_Open_Image onclick="this.style.display='none'; codehighlighter1_131_336_open_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top ;="" none="" codehighlighter1_131_336_closed_text.style.display="'inline';" codehighlighter1_131_336_closed_image.style.display="'inline';"><img id=Codehighlighter1_131_336_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; codehighlighter1_131_336_closed_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top ;="" none="" codehighlighter1_131_336_open_text.style.display="'inline';" codehighlighter1_131_336_open_image.style.display="'inline';"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)&nbsp;</span><span id=Codehighlighter1_131_336_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_131_336_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sigaction&nbsp;sa;<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>　　</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;(省略)</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">　　sigaction(SIGINT,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">sa,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;gSignaled&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img id=Codehighlighter1_238_334_Open_Image onclick="this.style.display='none'; codehighlighter1_238_334_open_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top ;="" none="" codehighlighter1_238_334_closed_text.style.display="'inline';" codehighlighter1_238_334_closed_image.style.display="'inline';"><img id=Codehighlighter1_238_334_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; codehighlighter1_238_334_closed_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top ;="" none="" codehighlighter1_238_334_open_text.style.display="'inline';" codehighlighter1_238_334_open_image.style.display="'inline';">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">gSignaled)&nbsp;</span><span id=Codehighlighter1_238_334_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_238_334_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>　　</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">std::printf("waiting<img src="http://www.cppblog.com/Images/dot.gif">\n");</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #008000"><img id=Codehighlighter1_302_309_Open_Image onclick="this.style.display='none'; codehighlighter1_302_309_open_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top ;="" none="" codehighlighter1_302_309_closed_text.style.display="'inline';" codehighlighter1_302_309_closed_image.style.display="'inline';"><img id=Codehighlighter1_302_309_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; codehighlighter1_302_309_closed_text.style.display=" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top ;="" none="" codehighlighter1_302_309_open_text.style.display="'inline';" codehighlighter1_302_309_open_image.style.display="'inline';"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;timespec&nbsp;t&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_302_309_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_302_309_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;}</span></span><span style="COLOR: #000000">;&nbsp;nanosleep(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">t,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;</p>
<p><strong><span>错误</span></strong><strong><span>1: </span></strong><strong><span>竞争条件</span></strong><span><br></span><span>&nbsp;&nbsp;&nbsp; 在上面的代码里有竞争条件</span><span>。</span><span>在</span><span>sigaction</span><span>函数被调用后</span><span>、</span><span>在</span><span>gSignaled</span><span>还未被赋值成</span><span>0</span><span>值之前，如果接受到</span><span>SIGINT</span><span>信号了那会变得怎么样呢</span><span>? </span><span>在信号处理函数中被覆写成</span><span>1</span><span>后的</span><span>gSignaled</span><span>会在信号处理函数返回后被初始化成</span><span>0</span><span>、</span><span>在后面的</span><span>while</span><span>循环里可能会变成死循环</span><span>。</span></p>
<span></span><span>
<p><br></p>
</span><strong><span>错误</span></strong><strong><span>2: </span></strong><strong><span>全局变量</span></strong><strong><span>gSignaled </span></strong><strong><span>声明的类型不正确</span></strong><span><br></span><span>&nbsp;&nbsp;&nbsp;&nbsp; 在信号处理函数里使用的全局变数</span><span>gSignaled</span><span>的类型没有声明成</span><span>volatile sig_atomic_t </span><span>。</span><span>这样的话</span><span>、</span><span>在执行</span><span>while</span><span>循环里的代码的时候接收到了了</span><span>SIGINT</span><span>信号时</span><span>、</span><span>有可能引起</span><span>while</span><span>的死循环</span><span>。</span><span>那为什么能引起这样的情况呢</span><span>：<span><br></span></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>信号处理函数里，把内存上</span><span>gSignaled</span><span>的值变更成</span><span>1</span><span> </span><span>，它的汇编代码如下：</span>
<p>&nbsp;</p>
<p><span></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face=Georgia> </font></span></span><span><font face=Georgia>movl<span>&nbsp;&nbsp;&nbsp; </span>$1, gSignaled</font></span></p>
<span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>但是，就像下面的代码描述的那样，</span><span>main</span><span>函数是把</span><span>gSignaled</span><span>的值存放到了寄存器里</span><span>。</span><span>在</span><span>w</span><span>hile</span><span>循环之前，仅仅是做了一次拷贝变量</span><span>gSignaled</span><span>内存上的值到寄存器里</span><span>、</span><span>而在</span><span>while</span><span>循环里只是参照这个寄存器里的值</span><span>。</span><span> <br></span>
<div><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><a target=_blank name=OLE_LINK3></a><a target=_blank name=OLE_LINK2></a><span><span><font face=Georgia>movl<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>gSignaled, %ebx<br></font></span></span><span><span><span><font face=Georgia>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .L8:<br></font></span></span></span><span><span><span><font face=Georgia><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>testl<span>&nbsp;&nbsp;&nbsp; </span>%ebx, %ebx<br></font></span></span></span><span><span><span><font face=Georgia><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>.L8</font><br></span></span></span></div>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>在不执行优化的情况下编译后编译器有可能不会生成上面那样的伪代码</span><span>。</span><span>但</span><span>Gcc</span><span>当使用</span><span>-O2</span><span>选项做优化编译时，生成的实际那样的汇编代码产生的危害并不仅仅是像上面说的威胁那样简单。这方面的问题，是设备驱动的开发者所要知道的常识，但现实情况是对于应用程序的设计者</span><span>.</span><span>开发者几乎都不知道这些知识。</span><span><br></span><span>为了解决上面的问题，全局变量</span><span>gSignaled</span><span>的类型要像下面那样声明</span><span>。</span><span><br></span></p>
<div><span></span><span>&nbsp;&nbsp;&nbsp;&nbsp; <font face=Georgia>volatile sig_atomic_t gSignaled;</font><br></span></div>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp; volatile</span><span>则是提示编译器不要像上面那样做优化处理，变成每次循环都要参照该变量内存里的值那样进行编译。所以在信号处理函数里把该变量的值修改后也能真实反映到</span><span>main</span><span>函数的</span><span>while</span><span>循环里</span><span>。</span><span><br></span><span>sig_atomic_t </span><span>是根据</span><span>CPU</span><span>类型使用</span><span>typedef</span><span>来适当定义的整数值，例如</span><span>x86</span><span>平台是</span><span>int</span><span>类</span><span>型</span><span>。</span><span>就是指</span><span>&#8221;</span><span>用一条机器指令来更新内存里的最大<a target=_blank name=20040712fn2></a>数据</span><a title="「最大の」は嘘でした。例えばAlphaは32/64bit変数は単一で更新できるけど、8/16bitは複数命令になったりするそうで。http://lists.sourceforge.jp/mailman/archives/anthy-dev/2005-September/002336.html このへんです。" href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712f2" target=_blank><span><sup><span><u><font color=#0000ff>*2</font></u></span></sup></span></a><span>&#8220;</span><span>。</span><span>在信号处理函数里要被引用的变量必须要定义成</span><span>sig_atomic_t</span><span>类型</span><span>。</span><span>那么不是</span><span>sig_atomic_t</span><span>类型的变量</span><span>(</span><span>比如</span><span>x86</span><span>平台上的</span><span>64</span><span>位整数</span><span>)</span><span>、</span><span>就得使用两条机器指令来完成更新动作。如果在执行一条机器指令的时候突然收到一个信号而程序执行被中断，而且在信号处理函数中一引用这个变量的话，就只能看到这个变量的部分的值。另外，由于字节对齐的问题不能由一条机器指令来完成的情况也会存在。把该变量的类型变成</span><span>sig_atomic_t</span><span>的话，这个变量被更新时就只需要一条机器指令就可以完成了。所以在信号处理函数里即使使用了该变量也不会出现任何问题</span><span>。</span></p>
<p><strong><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></strong><strong><span>2006/1/16 </span></strong><strong><span>补充</span></strong><strong><span>:</span></strong><span> </span><span>有一点东西忘记写了</span><span>。</span><span>关于</span><span>sig_atomic_t</span><span>详细的东西，请参考</span><span>C99</span><span>规范的</span><span>&#167;7.14.1.1/5</span><span>小节</span><span>。</span><span>在信号处理函数里对</span><span>volatile sig_atomic_t</span><span>以外的变量进行修改，其结果都是</span><span>"unspecified"</span><span>的</span><span>(</span><span>参照译者注</span><span>2)</span><span>。</span><span>另外，</span><span> <span>sig_atomic_t</span></span><span>类型的变量的取值范围是在</span><span>SIG_ATOMIC_MIN/MAX</span><span>之间</span><span> <span>(</span></span><span>参见</span><span>&#167;7.18.3/2)</span><span>。</span><span>有无符号是跟具体的实现有关。考虑到移植性取值在</span><span>0</span><span>～<span>127</span></span><span>之间是比较合适的</span><span>。<span>C99</span></span><span>也支持这个取值范围</span><span>。</span><span>C++</span><span>规范</span><span>(14882:2003)</span><span>里也有同样的描述</span><span>、</span><span>确切的位置是</span><span>&#167;1.9/9</span><span>这里</span><span>。</span><span>在</span><span>SUSv3</span><span>的相关描述请参考</span><span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/sigaction.html" target=_blank><span><u><font color=#0000ff>sigaction</font></u></span></a></span><a target=_blank name=20040712fn3></a><span>这里</span><a title="If the signal occurs other than as the result of calling abort(), kill(), or raise(), the behavior is undefined if the signal handler calls any function in the standard library other than one of the functions listed in the table above or refers to any obj" href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712f3" target=_blank><span><sup><span><u><font color=#0000ff>*3</font></u></span></sup></span></a><span>。</span><span>此外</span><span>、</span><span>虽然在</span><span>GCC</span><span>的参考手册里也</span><span><a href="http://www.gnu.org/software/libc/manual/html_node/Atomic-Types.html" target=_blank><span><u><font color=#0000ff>说了</font></u></span></a></span><span>把指针类型更新成原子操作，但在标准</span><span>C/C++</span><span>却没有记载</span><a target=_blank name=20040712fn4></a><a title="このマニュアルの in practice, you can assume that int and other integer types no longer than int are atomic. の部分は嘘とのこと。alphaの例を参照" href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712f4" target=_blank><span><sup><span><u><font color=#0000ff>*4</font></u></span></sup></span></a><span>。</span><span><br></span><em><font color=#000099><span>◆</span><span>译者注</span><span>2</span><span>：</span></font></em><span><br></span><font size=2><font face=Georgia><em><font color=#000099><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When the processing of the abstract machine is interrupted by receipt of a signal, the value of objects with type other than </span><span>volatile <a target=_blank name=baidusnap0></a><strong><span>sig_atomic_t</span></strong></span><span> are <strong><span>unspecified</span></strong>, and the value of any object not of </span><span>volatile <strong><span>sig_atomic_t</span></strong></span><span> that is modified by the handler becomes undefined.</span></font></em></font></font><span><br><font face=Century><font size=2><em><font color=#000099><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>------ </font></em></font></font></span><font size=3><font size=2><em><font color=#000099><font face=Century><span>ISO/IEC FDIS 14882:1998(E)</span><span> </span></font><span>的</span><span><font face=Century>1.9</font></span><span>小节</span></font></em></font></font></p>
<font size=3><font size=2><span></span></font><span>
<p><br></p>
</span></font><strong><span>错误</span></strong><strong><span>3: </span></strong><strong><span>在信号处理函数里调用了不可重入的函数</span></strong><span><br></span><span>上述的样例代码中调用了</span><span>printf</span><span>函数，但是这个函数是一个不可重入函数，所以在信号处理函数里调用的话可能会引起问题。具体的是，在信号处理函数里调用</span><span>printf</span><span>函数的瞬间，引起程序死锁的可能性还是有的。但是，这个问题跟具体的时机有关系，所以再现起来很困难，也就成了一个很难解决的</span><span>bug</span><span>了。</span><span><br></span><span>下面讲一下</span><span>bug</span><span>发生的过程。首先</span><span>、</span><span>讲解一下</span><span>printf</span><span>函数的内部实现。</span><span><br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>printf</span><span>函数内部调用</span><span>malloc</span><span>函数</span><span> <br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>malloc</span><span>函数会在内部维护一个静态区域来保存</span><span>mutex</span><span>锁</span><span>、</span><span>是为了在多线程调用</span><span>malloc</span><span>函数的时候起到互斥的作用</span><span> <br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>总之</span><span>、</span><span>malloc</span><span>函数里有&#8220;</span><span>mutex</span><span>锁定，分配内存，</span><span>mutex</span><span>解锁&#8221;这样&#8220;连续的不能被中断&#8221;的处理</span><span> </span><span>
<p>&nbsp;</p>
</span>
<div><span><font face=Georgia>main</font></span><span><font face=Georgia>関数</font><span><font face=Georgia>:<br></font></span></span><font face=Georgia><span><span>&nbsp; </span>call printf<span>&nbsp; </span>// while</span><span>循环中的</span><span>printf</span><span>函数</span></font><span><br></span><font face=Georgia><span><span>&nbsp;&nbsp;&nbsp; </span>call malloc<br></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call pthread_mutex_lock(</span><span>锁定</span><span>malloc</span><span>函数内的静态</span></font><font face=Georgia><span>mutex)<br></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span><span>在</span><span>malloc</span><span>处理时</span></font><font face=Georgia><span>..<br></span><span>☆</span><span>收到</span><span>SIGINT</span><span>信号</span></font><span><font face=Georgia>！</font><span><br></span></span><font face=Georgia><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call sig_handler<br></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call printf // </span><span>信号处理函数中的</span><span>printf</span><span>函数</span></font><span><br></span><font face=Georgia><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call malloc<br></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>call pthread_mutex_lock(</span><span>锁定</span><span>malloc</span><span>函数内的静态</span></font><font face=Georgia><span>mutex) <br></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>// </span><span>相同的</span><span>mutex</span><span>一被再度锁定，就死锁啦</span><span>!!</span></font></div>
<div><span><br>&nbsp;</span><span>&nbsp;&nbsp;&nbsp;&nbsp; 知道上面的流程的话</span><span>、</span><span>像这样的由于信号中断引起的死锁就能被理解了吧。为了修正这个</span><span>bug</span><span>，在信号处理函数里就必须调用可重入函数。可重入函数的一览表在</span><span>UNIX</span><span>规范</span><span> </span><span>(SUSv3)</span><span>有详细</span><span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03" target=_blank><span><u><font color=#0000ff>记载</font></u></span></a></span><a target=_blank name=20040712fn5></a><a title="The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. の後ろの表" href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712f5" target=_blank><span><sup><span><u><font color=#0000ff>*5</font></u></span></sup></span></a><span>。</span><span>你一定会惊讶于这个表里的函数少吧。</span><span><br></span><span>另外，一定不要忘记以下的几点：</span><span><br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>虽然在</span><span>SUSv3</span><span>里有异步信号安全</span><span>(</span><span>async-signal-safe</span><span>)</span><span>函数的一览，但根据不同的操作系统，某些函数是没有被实现的。所以一定要参考操作系统的手册</span><span> <br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>第三者做成的函数，如果没有特别说明的场合，首先要假定这个函数是不可重入函数，不能随便在信&nbsp; 号处理函数中使用。</span><span> <br></span><span><span>&nbsp;&nbsp;&nbsp; &#183;<span>&nbsp;</span></span></span><span>调用不可重入函数的那些函数就会变成不可重入函数了</span><span> </span></div>
<span>
<p><br></p>
</span><span>&nbsp;&nbsp;&nbsp; 最后，为了明确起见，想说明一下什么是</span><span>&#8221; </span><span>异步信号安全</span><span>(</span><span>async-signal-safe</span><span>)&#8221;</span><span>函数</span><span>。</span><span>异步信号安全函数是指</span><span>&#8221;</span><span>在该函数内部即使因为信号而正在被中断，在其他的地方该函数再被调用了也没有任何问题</span><span>&#8221;</span><span>。如果函数中存在更新静态区域里的数据的情况</span><span>(</span><span>例如，</span><span>malloc)</span><span>，一般情况下都是不全的异步信号函数。但是，即使使用静态数据，如果在这里这个数据时候把信号屏蔽了的话，它就会变成异步信号安全函数了。</span><span><br></span><em><font color=#000099><span>◆</span><span>译者注</span><span>3</span><span>：不可重入函数就不是异步信号安全函数</span></font></em>
<p>&nbsp;</p>
<p><span></span><span><br></span><a target=_blank name=20040712f1></a><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712fn1" target=_blank><span><span><font face=Georgia color=#0000ff><u>*1</u></font></span></span></a><font face=Georgia><span>：<span>sigaction</span></span><span>函数被调用前，一接收到</span><span>SIGINT</span><span>信号就终止程序，暂且除外吧</span></font><span><br></span><a target=_blank name=20040712f2></a><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712fn2" target=_blank><span><span><font face=Georgia color=#0000ff><u>*2</u></font></span></span></a><font face=Georgia><span>：</span><span>&#8220;最大&#8221;是不完全正确的</span><span>。</span><span>例如，</span><span>Alpha</span><span>平台上</span><span>32/64bit</span><span>的变量用一条命令也能被更新，但是好像把</span><span>8/16bit</span><span>的数据更新编程了多条命令了</span><span>。</span><span><a href="http://lists.sourceforge.jp/mailman/archives/anthy-dev/2005-September/002336.html" target=_blank><span><u><font color=#0000ff>http://lists.sourceforge.jp/mailman/archives/anthy-dev/2005-September/002336.html</font></u></span></a></span><span> </span><span>请参考这个</span><span>URL</span><span>地址</span><span>。</span></font><span><br></span><a target=_blank name=20040712f3></a><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712fn3" target=_blank><span><span><font face=Georgia color=#0000ff><u>*3</u></font></span></span></a><span><font face=Georgia>：</font><span><font face=Georgia>If the signal occurs other than as the result of calling abort(), kill(), or raise(), the behavior is undefined if the signal handler calls any function in the standard library other than one of the functions listed in the table above or refers to any object with static storage duration other than by assigning a value to a static storage duration variable of type volatile sig_atomic_t. Furthermore, if such a call fails, the value of errno is unspecified.<br></font></span></span><a target=_blank name=20040712f4></a><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712fn4" target=_blank><span><span><font face=Georgia color=#0000ff><u>*4</u></font></span></span></a><font face=Georgia><span>：</span><span>在这个手册里&#8220;</span><span> In practice, you can assume that int and other integer types no longer than int are atomic. </span><span>&#8221;这部分是不正确的</span><span>。</span><span>请参照</span><span>Alpha</span><span>的例子</span></font><span><br></span><a target=_blank name=20040712f5></a><a href="http://d.hatena.ne.jp/yupo5656/20040712/p2#20040712fn5" target=_blank><span><span><font face=Georgia color=#0000ff><u>*5</u></font></span></span></a><font face=Georgia><span>：<span>The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. </span></span><span>后面</span><span>有异步信号安全函数一览</span></font></p><img src ="http://www.cppblog.com/lymons/aggbug/51837.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-06-01 20:22 <a href="http://www.cppblog.com/lymons/archive/2008/06/01/51837.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>准则3：多线程程序里不准使用fork</title><link>http://www.cppblog.com/lymons/archive/2008/06/01/51836.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Sun, 01 Jun 2008 12:16:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2008/06/01/51836.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/51836.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2008/06/01/51836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/51836.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/51836.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: From 2008精选            鉄則3: マルチスレッドのプログラムでのforkはやめよう准则3：多线程程序里不准使用fork&nbsp;マルチスレッドのプログラムで、「自スレッド以外のスレッドが存在している状態」でfork&nbsp;何が起きるか能引起什么问题呢?&nbsp;実例から見てみましょう。次のコードを実行すると、子プロセスは実行...&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2008/06/01/51836.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/51836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2008-06-01 20:16 <a href="http://www.cppblog.com/lymons/archive/2008/06/01/51836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在xml里追加结点时添加回车(libxml2)</title><link>http://www.cppblog.com/lymons/archive/2007/11/30/37553.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Fri, 30 Nov 2007 05:44:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2007/11/30/37553.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/37553.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2007/11/30/37553.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/37553.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/37553.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 隐锋同学的blog上有关于libxml2的一篇文章，正好最近要使用这个库来处理xml文件。<br>不过在测试时我们发现用文章里F. 添加属性例程代码 时，添加的keyword结点后面没有回车，<br>跟后面的结点挤在一行了，不是很好看。<br>例如，以下的xml例子文件&nbsp;&nbsp;<a href='http://www.cppblog.com/lymons/archive/2007/11/30/37553.html'>阅读全文</a><img src ="http://www.cppblog.com/lymons/aggbug/37553.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2007-11-30 13:44 <a href="http://www.cppblog.com/lymons/archive/2007/11/30/37553.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给文件锁设定timeout</title><link>http://www.cppblog.com/lymons/archive/2007/11/26/37318.html</link><dc:creator>lymons</dc:creator><author>lymons</author><pubDate>Mon, 26 Nov 2007 06:52:00 GMT</pubDate><guid>http://www.cppblog.com/lymons/archive/2007/11/26/37318.html</guid><wfw:comment>http://www.cppblog.com/lymons/comments/37318.html</wfw:comment><comments>http://www.cppblog.com/lymons/archive/2007/11/26/37318.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/lymons/comments/commentRss/37318.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/lymons/services/trackbacks/37318.html</trackback:ping><description><![CDATA[<table style="WIDTH: auto">
    <tbody>
        <tr>
            <td><a href="http://picasaweb.google.com/lh/photo/mOgHTiWzGmFMS4ZV0xYTYQ?feat=embedwebsite"><img src="http://lh3.ggpht.com/_tDmtFJ6XjL4/SLEyA7_GOOI/AAAAAAAAB8A/KGWRMAe6chw/s400/DSC_0292.jpg"></a></td>
        </tr>
        <tr>
            <td style="FONT-SIZE: 11px; FONT-FAMILY: arial,sans-serif; TEXT-ALIGN: right">From <a href="http://picasaweb.google.com/lymons/2008?feat=embedwebsite">2008精选</a></td>
        </tr>
    </tbody>
</table>
Linux 文件锁是建议锁，也有人把它叫做记录锁，是通过系统调用fcntl(2)来实现的。<br>这种锁在锁定文件时有两种模式，分别是阻塞(block)和非阻塞模式。<br>在编码时比较常用的是有一种的非阻塞模式，也就是发现文件已经被其他进程<br>锁定时，立即返回不予等待。而阻塞模式则正好与它相反，也就是一直等待直到<br>其他进程释放文件锁为止。<br><span style="FONT-STYLE: italic">注：关于详细内容请参看《Unix环境高级编程》</span><br><br>不过，有的时候也会用到阻塞模式的文件锁，而且会要求不能被一直阻塞，等待<br>了一定时间后应返回。也就是说，想給阻塞版本的文件锁加上一个超时时间(timeout)。<br><br>通过man手册，fcntl(2)里面没有关于在阻塞模式时，设置超时时间的任何描述。<br>但从man手册里我们发现，文件锁在阻塞时会被信号(signal)中断。所以我们就像<br>可以利用设置信号软中断来实现一个自己版本的等待超时呢。<br><br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #008080">&nbsp;1</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;2</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;3</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">unistd.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;4</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">fcntl.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;5</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">types.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;6</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">sys</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">stat.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;7</span>&nbsp;<span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">signal.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;8</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;9</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;readw_lock(fd)&nbsp;\</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">10</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;lock_reg((fd),&nbsp;F_SETLKW,&nbsp;F_RDLCK)<br></span><span style="COLOR: #008080">11</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;writew_lock(fd)&nbsp;\</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">12</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;lock_reg((fd),&nbsp;F_SETLKW,&nbsp;F_WRLCK)<br></span><span style="COLOR: #008080">13</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;unlock(fd)&nbsp;\</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">14</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;lock_reg((fd),&nbsp;F_SETLK,&nbsp;F_UNLCK)<br></span><span style="COLOR: #008080">15</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">16</span>&nbsp;<span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">LW_FN)(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">fname);<br></span><span style="COLOR: #008080">17</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">18</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">19</span>&nbsp;<span style="COLOR: #000000">lock_reg(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;fd,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;cmd,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;type)<br></span><span style="COLOR: #008080">20</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">21</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;flock&nbsp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">22</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">.l_type&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;type;<br></span><span style="COLOR: #008080">23</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">.l_start&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">24</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">.l_whence&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;SEEK_SET;<br></span><span style="COLOR: #008080">25</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">.l_len&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">26</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">27</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;fcntl(fd,&nbsp;cmd,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #0000ff">lock</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">28</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">29</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">30</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;hander(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;signo)<br></span><span style="COLOR: #008080">31</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">32</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;do&nbsp;nothing</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">33</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">34</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">35</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">36</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;lockw(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">fname,&nbsp;LW_FN&nbsp;fn,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;timeout)<br></span><span style="COLOR: #008080">37</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">38</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">39</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;fd;<br></span><span style="COLOR: #008080">40</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;sigaction&nbsp;act,&nbsp;oact;<br></span><span style="COLOR: #008080">41</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">42</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((fd&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;open(fname,&nbsp;&nbsp;O_CREAT&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;O_RDWR,&nbsp;</span><span style="COLOR: #000000">0666</span><span style="COLOR: #000000">))&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;{<br></span><span style="COLOR: #008080">43</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">open&nbsp;failed!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">44</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">45</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">46</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">47</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;set&nbsp;timer&nbsp;to&nbsp;wakeup&nbsp;fcntl</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">48</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;act.sa_handler&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hander;<br></span><span style="COLOR: #008080">49</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;sigemptyset(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">act.sa_mask);<br></span><span style="COLOR: #008080">50</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;act.sa_flags&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;here,&nbsp;must&nbsp;be&nbsp;zero&nbsp;for&nbsp;wakeup&nbsp;fcntl</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">51</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;sigaction(SIGALRM,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">act,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">oact);<br></span><span style="COLOR: #008080">52</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">53</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;sec&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;alarm(timeout);<br></span><span style="COLOR: #008080">54</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">55</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(writew_lock(fd)&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)&nbsp;{<br></span><span style="COLOR: #008080">56</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alarm(sec);<br></span><span style="COLOR: #008080">57</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;recovery&nbsp;signal&nbsp;handler.</span><span style="COLOR: #008000"><br></span></span><span style="COLOR: #000000"><span style="COLOR: #000000"><span style="COLOR: #008080">58</span><font style="BACKGROUND-COLOR: #eeeeee">&nbsp;</font><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>sigaction(SIGALRM,&nbsp;<span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">oact,&nbsp;NULL);</span><br></span><span style="COLOR: #000000"><span style="COLOR: #008080">59</span><font style="BACKGROUND-COLOR: #eeeeee">&nbsp;</font><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><br></span><span style="COLOR: #008080">60</span><font style="BACKGROUND-COLOR: #eeeeee">&nbsp;</font><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="COLOR: #000000">printf(<span style="COLOR: #000000">"</span><span style="COLOR: #000000">locked&nbsp;OK!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);</span><br></span><span style="COLOR: #008080"><span style="COLOR: #000000"><span style="COLOR: #008080">61</span><font style="BACKGROUND-COLOR: #eeeeee">&nbsp;</font><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span></span>62</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;here,&nbsp;add&nbsp;code&nbsp;about&nbsp;file.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">63</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">#ifdef&nbsp;_TEST<br></span><span style="COLOR: #008080">64</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getchar();<br></span><span style="COLOR: #008080">65</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">66</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">#else</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">67</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;fn(fname);<br></span><span style="COLOR: #008080">68</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">#endif</span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">69</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">70</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">unlocked!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">71</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unlock(fd);<br></span><span style="COLOR: #008080">72</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">73</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;{<br></span><span style="COLOR: #008080">74</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alarm(sec);<br></span><span style="COLOR: #008080">75</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;recovery&nbsp;signal&nbsp;handler.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">76</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sigaction(SIGALRM,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">oact,&nbsp;NULL);<br></span><span style="COLOR: #008080">77</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;lock&nbsp;failed,&nbsp;because&nbsp;of&nbsp;timeout.</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">78</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">write&nbsp;lock&nbsp;failed\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">79</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">80</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="COLOR: #008080">81</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">82</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;ret;<br></span><span style="COLOR: #008080">83</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">84</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">85</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;test&nbsp;code</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">86</span>&nbsp;<span style="COLOR: #008000"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;func(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">fname)<br></span><span style="COLOR: #008080">87</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">88</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">check&nbsp;file:%s&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;fname);<br></span><span style="COLOR: #008080">89</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;getchar();<br></span><span style="COLOR: #008080">90</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">91</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">92</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">93</span>&nbsp;<span style="COLOR: #000000"></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br></span><span style="COLOR: #008080">94</span>&nbsp;<span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">95</span>&nbsp;<span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;lockw(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">file.lock</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;func,&nbsp;</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">96</span>&nbsp;<span style="COLOR: #000000">}<br></span><span style="COLOR: #008080">97</span>&nbsp;<span style="COLOR: #000000"><br></span><span style="COLOR: #008080">98</span>&nbsp;<span style="COLOR: #000000"></span></div>
<p><br>该程序的原理是，利用了alarm(2)设置的定时器，在一定时间过后会产生<span style="COLOR: #000000">SIGALRM信号，会使当前正在<br>执行的系统调用中断，导致该系统调用(fcntl)返回失败。</span> </p>
<p>上述代码有以下的说明：<br><span style="COLOR: red">&nbsp;1. 信号处理函数hander是一个空函数，里面什么也不做。它的存在就是为了接收<strong>SIGALRM</strong>信号<br>&nbsp;2. <strong>sigaction</strong>的<strong><em>sa_flags</em></strong>成员一定要设置成0，否则不会是系统调用中断<br>&nbsp;3. 为了防止把以前设置的定时器破坏，不管是加锁成功还是失败都立即恢复以前的定时器。<br>&nbsp;4. 因为为了接收<strong>SIGALRM</strong>信号，我们设置了它的信号处理函数。那在加锁失败和成功后也要恢复以前的设定。<br></span><br><em>注：虽然上面的代码能实现文件锁超时等待的问题，但又引入了另一个问题，就是该代码会破坏以前设定的定时器，即使是后面也恢复了以前的定时器设置，也会有一些副作用。比如：当为了等待其他进程释放文件锁，传递到lockw函数里的等待时间(也就是形参timeout)超过了以前设定的定时器触发时间，那这段期间内的以前设定的定时器就无效了。也就是说, 在调用lockw之前,该进程了已经设定了一个2秒的定时器, 而这个进程在调用lockw时传递的timeout时间为10秒(锁定的阻塞时间为10秒), 那么从调用lockw的那一刻起,2秒的定时器就无效了,知道锁定成功或者失败为止.<br></em><br></p><img src ="http://www.cppblog.com/lymons/aggbug/37318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/lymons/" target="_blank">lymons</a> 2007-11-26 14:52 <a href="http://www.cppblog.com/lymons/archive/2007/11/26/37318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>