??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲视频在线播放,美女国产一区,欧美三级小说 http://m.shnenglu.com/lymons/category/7270.html<h1><b>心灵的旅?lt;/b></h1></p>
人生是一场旅行,不在乎旅行的目的圎ͼ在乎的是沉K的风景和看风景的心?! zh-cn Mon, 15 Nov 2010 18:40:16 GMT Mon, 15 Nov 2010 18:40:16 GMT 60 C++ 局部静态初始化不是U程安全的! http://m.shnenglu.com/lymons/archive/2010/08/01/120638.htmllymons lymons Sun, 01 Aug 2010 05:19:00 GMT http://m.shnenglu.com/lymons/archive/2010/08/01/120638.html http://m.shnenglu.com/lymons/comments/120638.html http://m.shnenglu.com/lymons/archive/2010/08/01/120638.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/120638.html http://m.shnenglu.com/lymons/services/trackbacks/120638.html
h?C++ 局部静态初始化不是U程安全!
http://blogs.msdn.com/b/oldnewthing/archive/2004/03/08/85901.aspx
8 Mar 2004 7:00 AM
在块作用域中的静态变量的规则 (与之相对的是全局作用域的静态变? ? E序W一ơ执行到他的声明的时候进行初始化.
察看下面的竞争条?
int ComputeSomething()
{
static int cachedResult = ComputeSomethingSlowly();
return cachedResult;
}
q段代码的意图是在该函数W一ơ被调用的时候去计算一些费? q且把结果缓冲v来待函数来再被调用的时候则直接q回q个值即?
q个基本技巧的变种,在网l上也被叫做 避免 "static initialization order fiasco" . ( fiascoq个?在这个网上有非常棒的描q?因此我徏议大家去Mȝ后去理解?)
q段代码的问题是非线E安全的. 在局部作用域中的静态变量是~译时会在编译器内部转换成下面的样子:
int ComputeSomething()
{
static bool cachedResult_computed = false;
static int cachedResult;
if (!cachedResult_computed) {
cachedResult_computed = true;
cachedResult = ComputeSomethingSlowly();
}
return cachedResult;
}
现在竞争条g比较容易看C.
假设两个U程在同一时刻都调用这个函? W一个线E在执行 cachedResult_computed = true ? 被抢? W二个线E现在看到的 cachedResult_computed 是一个真? true ),然后qq了if分支的处?最后该函数q回的是一个未初始化的变量.
现在你看到的东西q不是一个编译器的bug, q个行ؓ C++ 标准所要求?
你也能写一个变体来产生一个更p糕的问?
class Something { ... };
int ComputeSomething()
{
static Something s;
return s.ComputeIt();
}
同样的在~译器内部它会被重写 (q次, 我们使用C++伪代?:
class Something { ... };
int ComputeSomething()
{
static bool s_constructed = false;
static uninitialized Something s;
if (!s_constructed) {
s_constructed = true;
new(&s) Something; // construct it
atexit(DestructS);
}
return s.ComputeIt();
}
// Destruct s at process termination
void DestructS()
{
ComputeSomething::s.~Something();
}
注意q里有多重的竞争条g. 像前面所说的, 一个线E很可能在另一个线E之前运行ƈ且在"s"q没有被构造前׃用它.
甚至更糟p的情况, W一个线E很可能在s_contructed 条g判定 之后,在他被设|成"true"之前 被抢? 在这U场合下, 对象s׃?span style="font-weight: bold;">双重构?/span>?span style="font-weight: bold;">双重析构.
q样׃是很?
但是{等, qƈ不是全部, 现在(原文是Not,我认为是Now的笔?看看如果?span style="font-style: italic;">两个q行期初始化局部静态变量的话会发生什?
class Something { ... };
int ComputeSomething()
{
static Something s(0);
static Something t(1);
return s.ComputeIt() + t.ComputeIt();
}
上面的代码会被编译器转化Z面的伪C++代码:
class Something { ... };
int ComputeSomething()
{
static char constructed = 0;
static uninitialized Something s;
if (!(constructed & 1)) {
constructed |= 1;
new(&s) Something; // construct it
atexit(DestructS);
}
static uninitialized Something t;
if (!(constructed & 2)) {
constructed |= 2;
new(&t) Something; // construct it
atexit(DestructT);
}
return s.ComputeIt() + t.ComputeIt();
}
Z节省I间, ~译器会把两?x_constructed" 变量攑ֈ一?bitfield ? 现在q里在变?construted"上就有多?span style="font-weight: bold;">无内部锁?/span>的读-?存操?
现在考虑一下如果一个线E尝试去执行 "constructed |= 1", 而在同一旉另一个线E尝试执?"constructed |= 2".
在x86q_? q条语句会被汇编?/p>
or constructed, 1
...
or constructed, 2
q没?"lock" 前缀. 在多处理机器? 很有可能发生两个存储都去d一个旧值ƈ且互怋用冲H的D行碰?clobber).
?ia64 ?alphaq_? q个撞更加明?因ؓ它们么没有这L???/span>的单条指? 而是被编码成三条指o:
ldl t1,0(a0) ; load
addl t1,1,t1 ; modify
stl t1,1,0(a0) ; store
如果q个U程?load ?store之间被抢? q个存储的值可能将不再是它曄要写入的那个?
因此,现在考虑下面q个有问题的执行序:
U程A 在测?"constructed" 条g后发C是零, q且正要准备把这个D定成1Q?但是它被抢占?
U程B q入同样的函? 看到 "constructed" 是零ql去构?"s" ?"t", d?"constructed" {于3.
U程A l箋执行q且完成它的 ???的指令序? 讑֮ "constructed" ?1, 然后构?"s" (W二?.
U程A 然后l箋L?"t" (W二? q设?"constructed" (最l? ?3.
现在, 你可能会认ؓ你能用界区 (critical section) 来封装这个运行期初始化动?
int ComputeSomething()
{
EnterCriticalSection(...);
static int cachedResult = ComputeSomethingSlowly();
LeaveCriticalSection(...);
return cachedResult;
}
因ؓ你现在把q个一ơ初始化攑ֈ了界区里面,而它线E安?
但是如果从同一个线E再一ơ调用这个函C怎样? ("我们跟踪了这个调? 它确实是来自q个U程!") 如果 ComputeSomethingSlowly() 它自己间接地调用 ComputeSomething()׃发生q个状况.
l论: 当你看见一个局部静态变量在q行期初始化? 你一定要心.
]]> 用C++~写synchronized method比较?/title> http://m.shnenglu.com/lymons/archive/2010/07/17/118502.htmllymons lymons Sat, 17 Jul 2010 04:17:00 GMT http://m.shnenglu.com/lymons/archive/2010/07/17/118502.html http://m.shnenglu.com/lymons/comments/118502.html http://m.shnenglu.com/lymons/archive/2010/07/17/118502.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/118502.html http://m.shnenglu.com/lymons/services/trackbacks/118502.html 在C++下编写synchronized method比较?(1)
在Java中有叫做synchronizedq样一个方便的关键字。用这个关键字的话,可以像下面那样能够单的q行"同步"method. 然?被同步的methodq不表示它就能在多线E中同时被执?
public class Foo { public synchronized boolean getFoo() { }
那么、在C++ (with pthread)中如何能实现同样的功能呢? 首先,有一个最单的Ҏ是下面q个.
// Ҏ a void Foo::need_to_sync( void ) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock( & mutex); // 临界区处?nbsp; pthread_mutex_unlock( & mutex); return ; }
q个Ҏ, 暂且不说C语言, 是在C++中下面的若干问题
在界区中间被return出来
在界区中间发生异常exception
发生的场? mutex没有被解锁unlock。我们可以像下面代码那样对这点进行改q?
// Ҏ b class ScopedLock : private boost::noncopyable { public : explicit ScopedLock(pthread_mutex_t & m) : m_(m) { pthread_mutex_lock( & m_); } ~ ScopedLock(pthread_mutex_t & m) { pthread_mutex_unlock( & m_); } private : pthread_mutex_t & m_; }; void Foo::need_to_sync( void ) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; { // 虽然不加q个括号E序也没有问题?/span> ScopedLock lock (mutex); // 在此处添加处?/span> } return ; }
OK。return和异常的问题可以解决了. 但是, 上面q没有完全解册个问?仍然有下面这个问?
使用q个pthread_mutex_tq不是C++?特别是存在下面的问题:
不能和其他的Mutexcd做同L处理
与其他的Mutexcd使用同一个ScopedLockc?则不能lock
Java的synchronizedҎ虽然可以"递归lock", 但是上面的代码ƈ不是q样. 在界区中递归调用自己的话׃发生死锁.
特别?W?点的递归lock的问题是很重要的. q里好好C用glibc扩展的话可以象下面那样解决.
/ Ҏ c void Foo::need_to_sync( void ) { static pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;
从NPpthread_mutex_init来初始化递归mutex,而pthread_mutex_init函数在一个线E中只能被调用一? 如果惌用synchronized method的方法来实现q个的话,变成了"是先有鸡q是先有?的话题了. 所?用叫做pthread_once的函数来实现?q也?a >在SUSv3中被记蝲的定?/a>?/p>
// Ҏ d namespace /* anonymous */ { pthread_once_t once = PTHREAD_ONCE_INIT; pthread_mutex_t mutex; pthread_mutexattr_t attr; void mutex_init() { pthread_mutexattr_init( & attr); pthread_mutexattr_settype( & attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init( & mutex, & attr); } } void Foo::need_to_sync( void ) { pthread_once( & once, mutex_init); { ScopedLock lock (mutex); // 処理 } return ; }
上面的代码就OK了?/p>
q就能够解决递归lock的问题了.但是..., q个Ҏ
q越来越不像C++的代码了.。对每一个想要同?synchronize)的方法都像这么样写代码的?效率变得非常低下.
随机成本大。速度慢?
׃产生上面那样的新问题.
?/span> [C++ ] 在C++下编写synchronized method比较?2)
"? Ҏ的同步应该是l常必需? q不是没有方便的办法",q样的说法也有吧. 是的, ? 一般的办法是下面那?
做成一个Mutex,作ؓ(non-POD型的, x通的)C++c?
做成一个Mutexcȝ实例,作ؓcd? 或者是全局变量, 来同步化Ҏ
来看看它的具体实现吧. 首先做成的MutexcL下面那样?/p>
class Mutex { public : Mutex() { pthread_mutex_init( & m_, 0 ); } void Lock( void ) { pthread_mutex_lock( & m_); } void Unlock( void ) { pthread_mutex_unlock( & m_); } private : pthread_mutex_t m_; };
现在的Mutexc?被作为抽象基c?接口c?的场合也比较? 在这里就不说? ScopedLockcM需要有若干的修? 想下面那样写好.
template < typename T > class ScopedLock { public : ScopedLock(T & m) : _m(m) { _m.Lock(); } ~ ScopedLock() { _m.Unlock(); } private : T & _m; };
用这个MutexcL同步Ҏ, 可以像下面那样? 首先是看看一个明昄有错误的例子.
// Ҏe void Foo::need_to_sync( void ) { static Mutex mutex; { ScopedLock < Mutex > lock (mutex); // 処理 } return ; }
q是... 代码虽然单易?但是很遗?它不能很好工? NG!. Foo::need_to_sync函数W一ơ被执行的时候如果恰好是多个U程同时执行的话, mutex 的构造函数就有被多次调用的可能?关于理由Q可以参考微软中比较有名气的blog文章The Old New Thing?a C++ scoped static initialization is not thread-safe, on purpose!"在这里面有详的描述Q所以就我们׃在详l叙qC。在q篇blog里用了VC++的代码作Z子,但是g++也是差不多的。所?strong>“动态的初始化局部的静态变?#8221;是, 在线E所完全意识不到的情况下q行?span class="footnote">*4 ?/p>
接下来,要介l一个在目前做的比较好的Ҏ?Z单我们用了全局变量Q但是即使作为类变量Q类中的static成员变量Q也是一L。这个方法就是?#8220;非局部的静态变?#8221;来做成Mutex?/p>
// Ҏf namespace /* anonymous */ { Mutex mutex; } void Foo::need_to_sync( void ) { ScopedLock < Mutex > lock (mutex); // 处理 return ; }
q个是最行的方法,而且基本 上可以没有问题就能工作得很好?/p>
在一个全局的类对象x存在Q且在x的构造函C直接或者绕弯间接的调用Foo::need_to_sync函数的场合,会引起一些问题。也是静态的对象的初始化序的问题,q个问题一般也被叫?static initialization order fiasco" 。在执行到mutex的构造函C前, mutex.Lock()有可能会被执行?/p>
q里的FAQ ?0.12?0.16、在里面对自q代码的初始化序已经证明了没有问题,而且来也不会出现问题,所以上面的Ҏ是OK的?/p>
如果Q?初始化顺序的问题不能保证他没有问题的话, 只好使用pthread_once?#8220;Ҏd”Q或者移植性低?#8220;Ҏc”。我的个人感觉是Ҏcq是比较不错的选择?/p>
在最后我们尝试考虑一下如何把Ҏc变成C++的代码?/p>
// Ҏc (重新讨论) void Foo::need_to_sync(void) { static pthread_mutex_t mutex = PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;
目标是?/p>
隐藏pthread_mutex_tcd、让自己写的cȝcd可见?
在方法e,f中像使用ScopedLock模板那样q行修改?
当然Q不让它发生初始化顺序的问题?br>
?/span> [C++ ] 用C++~写synchronized method比较?(3)
q是Ҏc的改良?首先Q?Z避免发生初始化顺序的问题Q?必须是不允许调用构造函数就能完成对象的初始化。因此,必须像下面那样初始化mutex对象
// Ҏc' (假设) void Foo::need_to_sync(void) { static StaticMutex mutex = { PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP, ........ };
一般的不允许像q样初始化C++cRؓ了实C面那L初始化,StaticMutexcdLPOD型的。所谓POD型就是,
不允许有构造函?
不允许有析构函数
不允许编译器生成拯构造函敎ͼ 赋值构造函数?
不允许有private, protected 的成?
不允许有虚函?
满以上规格的类?span class="footnote">*6 ?/p>
大概有严格的制约,但是利用"定义非虚成员函数是没有问题的"q个Ҏ? 我们试改良Ҏc的方?
...像下面那样如?
// Ҏc' #define POD_MUTEX_MAGIC 0xdeadbeef #define STATIC_MUTEX_INITIALIZER { PTHREAD_INITIALIZER, POD_MUTEX_MAGIC } #define STATIC_RECURSIVE_MUTEX_INITIALIZER { PTHREAD_RECURSIVE_INITIALIZER_NP, POD_MUTEX_MAGIC } class PODMutex { public : void Lock() { assert(magic_ == POD_MUTEX_MAGIC); pthread_mutex_lock( & mutex_); } void Unlock() { assert(magic_ == POD_MUTEX_MAGIC); pthread_mutex_unlock( & mutex_); } typedef ScopedLock < PODMutex > ScopedLock; public : // 虽然~程了POD? 但是不定义成public是无效?/span> pthread_mutex_t mutex_; const unsigned int magic_; }; // ScopedLockcL板是留用了在Ҏe,f中做成的代码. void Foo::need_to_sync( void ) { static PODMutex mutex = STATIC_RECURSIVE_MUTEX_INITIALIZER; { PODMutex::ScopedLock lock (mutex); // 处理. } return ; }
上面的代码满了"隐藏了pthread_mutex_t?留用了ScopedLock<>"q两个目? q不是有点儿像C++的代码了? q有,PODMutexcd是即使在上记例子中那L局部静态变量以?也能攑ֿ的用全局变量,cd量了.
而且, 成员变量 magic_ ? 一个const成员变量, 所以当使用~译器自动生成的构造函数来创徏一个对象时׃发生错误. 因此,在构建release版程序时把它剔除好?
使用g++ -S来编译上面的代码, 生成汇编代码. 我们p看见下面那样的局部的静态变?
$ g++ -S sample.cpp $ c++filt < sample.s | lv (? .size Foo::need_to_sync()::mutex, 28 Foo::need_to_sync()::mutex: .long 0 .long 0 .long 0 .long 1 .long 0 .long 0 .long -559038737
0,0,0,1,0,0 q样的东西是 PTHREAD_RECURSIVE_INITIALIZER_NP , -559038737 则是 POD_MUTEX_MAGIC 。即使没有进行动态的初始?不调用构造函?、仅仅是在目标文件上生成的目标代码那Lq行静态初始化, mutex对象也能被正常的初始? 所以这D代码是OK?
随便, 在用boost库的场合, Ҏf之外的选择余地几乎没有(臛_是现?. 一看见ML{? (当然!!)q道可能会出现 order序的问? 但是, q前来? 既要保证既要保证可移植?a title="Windowsはどうする?という命を解決できないのかもしれません - 推測" name="20040713fn7">*7和速度,又要能做成与Ҏc相当的PODMutex的方法好像还没有出现?
完结
]]> W2K信号(Signals)的设备驱?/title> http://m.shnenglu.com/lymons/archive/2010/04/13/112426.htmllymons lymons Tue, 13 Apr 2010 02:29:00 GMT http://m.shnenglu.com/lymons/archive/2010/04/13/112426.html http://m.shnenglu.com/lymons/comments/112426.html http://m.shnenglu.com/lymons/archive/2010/04/13/112426.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/112426.html http://m.shnenglu.com/lymons/services/trackbacks/112426.html
Unix下的信号提供了一个简单的IPC机制Q也是当进E收C个信号后会异?asynchronous) 地调用你的信号处理函?也叫做句?Q不你的代码是否已l处在执行的q程之中?而在Windows 2000(译者注Q版本高于W2k的Windowsq_)下就需要用C个设备驱动,以便你能使用异步q程调用(asynchronous procedure calls , UAPCs或者APC) 来达成同L效果.
By Panagiotis E.
August 01, 2001
URL:http://www.ddj.com/windows/184416344
译QLymons (lymons@gmail.com)
阅读全文 ]]> 设计模式在Linux文gpȝ中的单实? http://m.shnenglu.com/lymons/archive/2009/02/02/72798.htmllymons lymons Mon, 02 Feb 2009 05:08:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/02/72798.html http://m.shnenglu.com/lymons/comments/72798.html http://m.shnenglu.com/lymons/archive/2009/02/02/72798.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/72798.html http://m.shnenglu.com/lymons/services/trackbacks/72798.html 阅读全文 ]]> ATA Over Ethernet: 把硬盘放到局域网?/title> http://m.shnenglu.com/lymons/archive/2009/02/02/72796.htmllymons lymons Mon, 02 Feb 2009 04:54:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/02/72796.html http://m.shnenglu.com/lymons/comments/72796.html http://m.shnenglu.com/lymons/archive/2009/02/02/72796.html#Feedback 1 http://m.shnenglu.com/lymons/comments/commentRss/72796.html http://m.shnenglu.com/lymons/services/trackbacks/72796.html 阅读全文 ]]> 用lex/yacc来解析配|文? http://m.shnenglu.com/lymons/archive/2009/02/02/72793.htmllymons lymons Mon, 02 Feb 2009 04:31:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/02/72793.html http://m.shnenglu.com/lymons/comments/72793.html http://m.shnenglu.com/lymons/archive/2009/02/02/72793.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/72793.html http://m.shnenglu.com/lymons/services/trackbacks/72793.html 阅读全文 ]]> RX-RPC的客L与服务器端的通信解析 http://m.shnenglu.com/lymons/archive/2009/02/02/72790.htmllymons lymons Mon, 02 Feb 2009 03:58:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/02/72790.html http://m.shnenglu.com/lymons/comments/72790.html http://m.shnenglu.com/lymons/archive/2009/02/02/72790.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/72790.html http://m.shnenglu.com/lymons/services/trackbacks/72790.html 阅读全文 ]]> Linux中处理来自共享对象的同步事g http://m.shnenglu.com/lymons/archive/2009/02/02/72784.htmllymons lymons Mon, 02 Feb 2009 03:28:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/02/72784.html http://m.shnenglu.com/lymons/comments/72784.html http://m.shnenglu.com/lymons/archive/2009/02/02/72784.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/72784.html http://m.shnenglu.com/lymons/services/trackbacks/72784.html 阅读全文 ]]> 跟着开?来学习源C语言代码的读?/title> http://m.shnenglu.com/lymons/archive/2009/02/01/72764.htmllymons lymons Sun, 01 Feb 2009 07:54:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/01/72764.html http://m.shnenglu.com/lymons/comments/72764.html http://m.shnenglu.com/lymons/archive/2009/02/01/72764.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/72764.html http://m.shnenglu.com/lymons/services/trackbacks/72764.html
照片1●「Code Reading―跟着开源来学习软g的开发技法? q本书的前言里,~程高手Dave Thomas写下了下面的话? 没有研读q其他作家作品的伟大作家Q没有研I过其他dW法的伟大画Ӟ没有盗取qƈ肩作战的同事的技术的技巧高明的外科ȝQ没有在副驾驶的位置U篏实际l验的L?67的机长,在现实生zM真的会存在他们这L人吗Q?nbsp; 阅读全文 ]]> 准则6Q遵守多U程~程的常识(下) http://m.shnenglu.com/lymons/archive/2009/02/01/70835.htmllymons lymons Sun, 01 Feb 2009 07:41:00 GMT http://m.shnenglu.com/lymons/archive/2009/02/01/70835.html http://m.shnenglu.com/lymons/comments/70835.html http://m.shnenglu.com/lymons/archive/2009/02/01/70835.html#Feedback 1 http://m.shnenglu.com/lymons/comments/commentRss/70835.html http://m.shnenglu.com/lymons/services/trackbacks/70835.html
From 2008_N?/a>
UNIX上C++E序设计守则(6)
准则6: 遵守多线E编E的常识
要准把握在POSIX标准的函CQ那些函数是非线E安全的Q一定不要?
要让自己~写的函数符合线E安?
在访问共享数?变量之前一定要先锁?
如果使用C++的话Q一定要注意函数的同步方?
说明: (2) 要让自己~写的函数符合线E安?/p>
在写多线E的应用E序?在多个线E里׃n的变量要先锁定然后在更新?。那么在多线E里׃n的变量主要有全局变量和函数内的静态变量。而且,即是short型和int型的׃n变量也要先锁定后更新才能保证其安全?/p>
?详细的是参?
q有,在用C++~程的场合要注意函数的方步方法。一般的说来下面的写法是错误的?strong>Mutex在函数内被声明成静态变量是不允许的?/p>
int incr_counter(void) { static Mutex m; // q么写不?br> m.Lock(); static int counter = 0; int ret = ++counter; m.Unlock(); return ret; }
应该用下面的方式来代?
Mutex m; int incr_counter(void) { m.Lock(); // ...
把Mutex声明成全局变量的话比较?E微比上一个好)?/p>
?详细是参?
UNIX上C++E序设计守则(6)-- 补记
U程安全函数是像下面那样
不要操作局部的静态变?函数内的static型的变量)和非局部的静态数?全局变量)。ƈ?其它的非U程安全函数不要调用
要操作这L变量的话, p使用mutexq行同步处理,来限制多个线E同时对它进行操?
被定义的,但是
特别是前? 和被叫做可重入的(reentrant)函数有区?
反之, 后者特别是和叫?Serializable"(不单单是MT-Safe)"Safe"的函数有区别
也有以上的情c在Solaris的man手册? 用后者的方式q行区别. 从多U程E序里安全调用的?叫?Safe", 而且, 在多U程中能够ƈ?concurrency)地执行这个函数的处理的话,好像 叫?MT-Safe"?/p>
? 因ؓ比较详细? 如果不是在对于执行速度要求比较苛刻的环境中~写代码的话, 单单地意识到「是否线E安全」就_?不是吗?/p>
]]>
准则6Q遵守多U程~程的常识(上) http://m.shnenglu.com/lymons/archive/2008/12/29/70678.htmllymons lymons Mon, 29 Dec 2008 09:11:00 GMT http://m.shnenglu.com/lymons/archive/2008/12/29/70678.html http://m.shnenglu.com/lymons/comments/70678.html http://m.shnenglu.com/lymons/archive/2008/12/29/70678.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/70678.html http://m.shnenglu.com/lymons/services/trackbacks/70678.html
From 2008_N?/a>
UNIX上C++E序设计守则(6)
准则6: 遵守多线E编E的常识
要准把握在POSIX标准的函CQ那些函数是非线E安全的Q一定不要?
要让自己~写的函数符合线E安?
在访问共享数?变量之前一定要先锁?
如果使用C++的话Q一定要注意函数的同步方?
说明: (1) 要准把握那些非U程安全的函敎ͼ一定不要?/p>
如果在POSIXq_上进行多U程~程Ӟ有几个最基本的知识,也就是所说的“常识”Q希望大家一定要严格遵守?/p>
...首先、我们要理解“U程安全”的意思。线E安全的函数是指,“一个能被在多个U程同时调用也不会发生问题的函数”。这L函数通常要满以下几个的特质?/p>
不要操作局部的静态变?函数内的static变量)和全局静态数?全局变量Q函数外的静态变?。而且Q也不要调用其他的非U程安全的函?
如果要操作这L变量的话Q事先必M用互斥锁mutexq行同步Q否则一定要限制多个U程同时对它的访?
那么、在POSIX标准的函数里面,也有不满上q条件的。由于历史遗留问题,一些函数的识别标识(signature)的定义没有考虑到线E安全的问题Q所以不怎么做都不能满上述的条件。例如,看看 localtime函数 吧。它的定义的识别标识QsignatureQ如下:
struct tm *localtime(const time_t *timer);
localtime 函数是,把一个用整数形式表示的时??970/1/1到现在ؓ止的U数)、{换成一个能让hҎ明白的年月日形式表示出来的tml构体ƈq回l调用者的函数。根据规D明、返回出来的tml构体是不需要free()掉,也不能释攄。这个函数典型的实现像下面的代码那P
struct tm *localtime(const time_t *timer) { static struct tm t; /* ... 从timer参数里算出年月日{数?... */ t.tm_year = XXX; /* ...把它们填入到l构体内... */ t.tm_hour = XXX; t.tm_min = XXX; t.tm_sec = XXX; return &t; }
q个函数如果被像下面那样使用的话Q就会有漏洞Q?/p>
在线EA里执?ta = localtime(x);
在线EB里执?tb = localtime(y);
U程A参照tal构体里的数?→ 发现这些数据是一些奇怪的|
...在函数的说明手册里对q个问题也没有做q详l的说明。关于这个漏z,在localtime函数即使用了mutex锁也不能被回避掉。所以,q个函数定义的识别标识是不行滴?br>[译者lymons注:在多个线E里调用localtime函数之所以有问题的原因是Qlocaltime函数里返回的tm构造体是一个静态的l构体,所以在U程A里调用localtime函数Ӟ该结构体被赋予正的|而在U程A参照q个l构体之前,U程B又调用localtime的话Q这个静态的l构体又被赋予新的一个倹{因此在U程A对这个结构体的访问都是基于一个错误的D行的]
正因为如此,像上面说过的POSIX规格(SUSv3)里整齐的
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,
(省略)
对于在规g被定义ؓ非线E安全的函数Q应该制定一个避免用它们的规则出来Qƈ且制作一个能够自动检查出是否使用了这些函数的开发环境,应该是比较好的?/p>
反之Q在q里没有被登载的POSIX标准函数都被假定?"shall be thread-safe" 的、所以在实际的用中可以认ؓ在多U程环境里是没有问题?而且在用的q_上没有特别地说明它是非线E安全的??/p>
另外Q有几个非线E安全的函数Q都准备了一个备用的U程安全版本的函?仅仅是变更了函数的识别标?。像q些函数Z与原版进行区别都在其函数名后面添加了 _r q个后缀。例如,asctime函数有U程安全版本的函数asctime_r。在规格说明中是否定义了备用函数Q可以试着点击刚才?a >那个|页
里面的函数名可以看到。点?rand函数 可以看刎ͼ
[TSF] int rand_r(unsigned *seed);
用[TSF]q样的文字标记出来的函数吧。这是备用函数。在一览中没有记蝲出来的函?备注Q?E微有点儿出入。请参照q里 )、据我所知还有下面的备用函数?/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
q有Q在规格以外Q还准备了很多的下面那样的函数?/p>
gethostbyname_r, gethostbyname2_r
在最q的操作pȝ中、也使用 getaddrinfo API函数来解决IPv6名字对应的问题。gethostbynamepd的API都是比较陈旧的函C、所以用前面的函数q是比较好吧。根据规格SUSv3Qgetaddrinfo也是U程安全的:
The freeaddrinfo() and getaddrinfo() functions shall be thread-safe.
在多U程~程中,不要使用非线E安全的函数Q而他们的备用函数可以攑ֿ地积极的M用?/p>
→后箋
]]> 准则5Q尽可能避免U程的gq撤销处理 http://m.shnenglu.com/lymons/archive/2008/12/25/70227.htmllymons lymons Thu, 25 Dec 2008 06:08:00 GMT http://m.shnenglu.com/lymons/archive/2008/12/25/70227.html http://m.shnenglu.com/lymons/comments/70227.html http://m.shnenglu.com/lymons/archive/2008/12/25/70227.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/70227.html http://m.shnenglu.com/lymons/services/trackbacks/70227.html
From 2008_N?/a>
[C++ ] UNIX上C++E序设计守则(5)
准则5: 可能避免线E中做gq撤销的处?br>
U程的异步撤消是指:一个线E发Z断其他线E的处理的一个动?
延迟撤消因ؓ是规D由度比较高、所以根据OS和C库函数的版本它也有各式各L动作
要想在不同的环境下都能稳定的动作的话Q就必须要详l调查运行环境和Q对C库函数进行抽象化Q做必要的条件编?
在C++中、「撤消发生时的对象释放」的实现不具有可UL?
说明:
在前面我们已l讲q,U程的撤消分为「异步」「gq」这两种cd、ƈ且「异步撤消」也是非常容易引起各U复杂问题的元凶?/p>
那么Q现在要在程序中除掉「gq撤消」。gq撤消虽然不会像异步撤消那样会引起各U各L问题、但是、注意事还是有很多的。只有把下面的这些注意事全部都把握之后才能攑ֿ使用?/p>
注意事项1: 要好好把握撤消点
和异步撤消不一L是、撤消处理一直会被gq到在代码上明示出来的撤消点之后才会被执行。如果编写了一个具有gq撤消可能的代码、代码中的那条语句是撤消炏V必要正确的把握?/p>
首先、调用过pthread_testcancel函数的地方就变成撤消点了。当然这个函数是、仅仅ؓ了「变成gq撤消」的目的而设|出来的函数。除此之外、某些标准库函数被调用后会不会变成撤消点是在规格(SUSv3)中决定的?a >请参照规D?/a>、有下面的函C览?/p>
下面的函?span style="FONT-WEIGHT: bold">?/span>撤消?strong> accept, aio_suspend, clock_nanosleep, close, connect, creat, fcntl, fdatasync, fsync, getmsg, getpmsg, lockf, mq_receive, mq_send, mq_timedreceive, mq_timedsend, msgrcv, msgsnd, msync, nanosleep, open, pause, poll, pread, pselect, pthread_cond_timedwait, pthread_cond_wait, pthread_join, pthread_testcancel, putmsg, putpmsg, pwrite, read, readv, recv, recvfrom, (?
下面的函?span style="FONT-WEIGHT: bold">不是撤消?strong> access, asctime, asctime_r, catclose, catgets, catopen, closedir, closelog, ctermid, ctime, ctime_r, dbm_close, dbm_delete, dbm_fetch, dbm_nextkey, dbm_open, dbm_store, dlclose, dlopen, endgrent, endhostent, endnetent, endprotoent, endpwent, endservent, endutxent, fclose, fcntl, fflush, fgetc, fgetpos, fgets, fgetwc, fgetws, fmtmsg, fopen, fpathconf, fprintf, fputc, fputs, fputwc, fputws, (?
看到q些我想已经明白了、但是在规格中也说明了「能否成为撤消点跟具体的实现相关的函数」也是多数存在的。原因是、ؓ了可UL性、保证「在一定的旉内让U程的gq撤消完成」是很困隄事情。做的不好的话、只要稍微一提升OS的版本就可能让做出来的程序品不能动作?/p>
即是这样那q想要用gq撤消吗Q?/p>
注意事项2: 实现要知道cleanup函数的必要?/strong>
可能被gq撤销的线E在q行的过E中Q要甌资源的场合,一定要考虑C下的几点Q否则就会编制出含有资源丢失和死锁的软g产品?/p>
例如~写的下面的函数׃能被安全的gq撤销掉?/p>
void* cancel_unsafe(void*) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&mutex); // 此处不是撤消?br> struct timespec ts = {3, 0}; nanosleep(&ts, 0); // l常是撤消点 pthread_mutex_unlock(&mutex); // 此处不是撤消?br> return 0; } int main(void) { pthread_t t; // pthread_create后马发上收到一个有效的延迟撤消的要?br> pthread_create(&t, 0, cancel_unsafe, 0); pthread_cancel(t); pthread_join(t, 0); cancel_unsafe(0); // 发生死锁Q?br> return 0; }
在上面的样例代码中、nanosleep执行的过E中l常会触发gq撤销的最l动作,但是q个时候的mutex锁还处于被锁定的状态。而且、线E一被gq撤消的话就意味着没有人去释放掉这个互斥锁?span class=footnote>*2 。因此、在下面的main函数中调用同Lcancel_unsafe函数时就会引h锁了?/p>
Z回避q个问题、利用pthread_cleanup_push函数在撤消时释放掉互斥锁的话OK了,也就不会死锁了?/p>
// 新增清除函数 void cleanup(void* mutex) { pthread_mutex_unlock((pthread_mutex_t*)mutex); } // _体字部分是新增的语?br>void* cancel_unsafe(void*) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cleanup_push(cleanup, &mutex); pthread_mutex_lock(&mutex); struct timespec ts = {3, 0}; nanosleep(&ts, 0); pthread_mutex_unlock(&mutex); pthread_cleanup_pop(0); return 0; }
注意事项3: 实现要清楚gq撤消和C++之间的兼容度
使用C语言的场合,利用上面的pthread_cleanup_push/pop函数p安全地执行gq撤消的动作Q但是在C++语言的场合就会出现其他的问题?strong>C++与gq撤消之间的兼容度是非常差的。具体的表现有以下两个问?
执行延迟撤消的时候,内存栈上的对象的析构函数会不会被调用跟具体的开发环境有关系
GCC3版本׃会调用?
Solaris和Tru64 UNIX下的原生~译器的场合Q就调用析构函数(好像)
pthread_cleanup_push/pop函数和C++的异常处理机制之间有着怎样的相互媄响也能具体环境有?
不调用析构函敎ͼ或者在抛出异常的时候不能做cleanup处理Q经常是发生内存泄漏Q资源丢失,E序崩溃Q死锁等现象的原因。o人意外的是对于这个深层次的问题,pBoost C++?/a>都束手无{?/p>
[Q] Why isn't thread cancellation or termination provided?
[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.
先必ȝ保对象的自由存储Q而后全都让cleanup函数去释攑֯象的Ҏ也有Q但是这ơ是牺牲了异常安全性?br>Q原文没有看明白Qオブジェクトを必ずフリヹ{トア上にZし、解体を全て、クリーミ냊ップハンドラに行わせる手もありますが、今度は例外安全性が犠牲になるでしょう。)
应该说的是,在用C++的工E里不对U程q行延迟撤消处理q是比较实际的?/p>
]]>
准则4Q请不要做线E的异步撤消的设?/title> http://m.shnenglu.com/lymons/archive/2008/12/19/69810.htmllymons lymons Fri, 19 Dec 2008 03:01:00 GMT http://m.shnenglu.com/lymons/archive/2008/12/19/69810.html http://m.shnenglu.com/lymons/comments/69810.html http://m.shnenglu.com/lymons/archive/2008/12/19/69810.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/69810.html http://m.shnenglu.com/lymons/services/trackbacks/69810.html
From 2008_N?/a>
原文地址Qhttp://d.hatena.ne.jp/yupo5656/20040724/p1
?/span> [C++ ] UNIX上的C++E序设计守则 (4)
铁则4: 请不要做U程的异步撤消的设计
U程的异步撤销是指Q?某个U程的执?span style="FONT-WEIGHT: bold">立刻被其他线E给强制l止?
请不要单单ؓ了让“设计更简?#8221;或?#8220;看v了更?#8221;而用线E的异步撤消
咋一看还是挺单的。但是搞不好可能会引起各U各L问题。请不要在不能把握问题的实质做Z用线E的异步撤消的设计!
在pthread的规D明中Q允怸个线E可以强制中断某个线E的执行。这是所说的异步撤消?/p>
U程的撤消有下面的两U方式?/p>
方式1Q?异步撤消(PTHREAD_CANCEL_ASYNCHRONOUS)
方式2Q?延迟撤销(PTHREAD_CANCEL_DEFERRED)
撤消动作Q是让线E的处理一直被延迟到撤消点才会L?
q有Q到底是用哪U撤消方式,不是撤消侧,而是被撤销侧能够决定的。另外,在被撤销侧也能够选择完全止撤消的这U方??/p>
会造成什么问题呢
那么Q让我看看ؕ用线E的异步撤消会引起什么问题呢。看q准?的h可能会知道,在下面的脚本里,被撤销U程以外的Q意一个线E会被死锁?/p>
U程1中调用malloc函数正在做内存分配的q程中,U程2异步撤消了线E?的处?
U程1马上被撤销Q但是malloc函数中的互斥锁就没有U程去解除了
后面的Q意一个线E如果再ơ调用malloc函数的话׃马上D该线E死?
在这个例子中使用了malloc函数Q但是其他的危险函数q有很多?/p>
反之Q即使做了异步撤消也没有问题的函C有少数存在的、我们把它们叫做「async-cancel safe函数」或者「异步撤消安全函数」。在一些商用UNIX中、OS提供的api函数的文档说明中有async-cancel safety的记载、但是在Linux(glibc)里就很遗憾,几乎没有相关的说明?/p>
在这儿,参看规格(SUSv3)的话Q会发现Q?a >描述异步撤消安全的函数只??/a>?/p>
pthread_cancel
pthread_setcancelstate
pthread_setcanceltype
而且、里面还?No other functions are required to be async-cancel-safe"q样的记载。因此,Linux的场合,如果在文里没有记蝲成async-cancel safety的函敎ͼ我们q是把它假定成不安全的函Cؓ?
如何避免q些问题?/h4>
在多U程~程中ؓ了安全的使用异步撤消处理、有没有回避死锁的方法呢Q我们试着想了几个。他们与准则3里的U程+fork的场合的回避{很怼?/p>
回避Ҏ1: 被撤销U程中,只能使用异步撤消安全函数
首先Q被撤销U程中,只能使用异步撤消安全函数。但是这个方?/p>
在规D明中只有3个异步撤消安全的函数
q些以外的函数是不是异步撤消安全(商用UNIX)、因为没有说明文我们不清楚(Linux)
中有以上的两点,所以这个回避方法几乎不现实?/p>
回避Ҏ2: 被撤销U程中,在做非异步撤消安全处理的q程中,再把撤消方式讄成「gq」或者是「禁止?/strong>
W二个是Q被撤销U程在做非异步撤消安全处理的q程中,把撤消方式再讑֮成「gq」或者「禁止」。对于这个方?/p>
像Ҏ1写的那样、要把我那个函数是异步撤消安全的一时还是挺ȝ?
在Q意的场所q不能保证撤消动作会被马上执?
例如Q再讑֮成「gq」后的一D|间内如果撤消发生时、某个正在阻塞的I/O函数是否能够被解除阻塞还是挺微妙?
如果讑֮成撤消禁止的话,则撤消会被屏蔽掉
有上面样的问题、会D「一_ֿ设计撤消方式的替换,从一开始就使用延迟撤消q不够好」这Ll果。所以这几乎是不好的一个回避策?/p>
回避Ҏ3Q?使用pthread_cleanup_push函数Q登录异步撤消时的线E数据清除的回调函数
W三U则是,用pthread_cleanup_push函数、登录一个在异步撤消发生时的数据清除的回调函数。这和在准则3中介l的pthread_atfork函数有点儿类伹{用q个函数d的回调函数来清除U程的数据和锁,可以回避死锁了?/p>
...但是Qpthread_cleanup_push函数d的回调函敎ͼ在「gq撤消」的场合是不能被调用的。因此、这个回避方法对于异步撤消没有什么大的作用?/p>
回避ҎQ: 不要执行异步撤消处理
最后是、不要执行异步撤消处理。反而代之的是?/p>
或?br>
不得不用线E撤消的话、不做异步撤消而作延迟撤消的处?
q是比较实际的做法,是我们值得推荐的?/p>
]]> 怎么利用设计模式来更有效的用共享内?/title> http://m.shnenglu.com/lymons/archive/2008/06/01/51842.htmllymons lymons Sun, 01 Jun 2008 13:41:00 GMT http://m.shnenglu.com/lymons/archive/2008/06/01/51842.html http://m.shnenglu.com/lymons/comments/51842.html http://m.shnenglu.com/lymons/archive/2008/06/01/51842.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/51842.html http://m.shnenglu.com/lymons/services/trackbacks/51842.html 阅读全文 ]]> 准则1Q不依赖于信h发的设计 http://m.shnenglu.com/lymons/archive/2008/06/01/51838.htmllymons lymons Sun, 01 Jun 2008 12:27:00 GMT http://m.shnenglu.com/lymons/archive/2008/06/01/51838.html http://m.shnenglu.com/lymons/comments/51838.html http://m.shnenglu.com/lymons/archive/2008/06/01/51838.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/51838.html http://m.shnenglu.com/lymons/services/trackbacks/51838.html
From 2008_N?/a>
?/u> [ C++ ] UNIX上的C++E序设计守则(1)
原文:http://d.hatena.ne.jp/yupo5656/20040712/p1
Unix跟Windows{那?#8221;对于开发者易于?#8221;的OS比v来,在信号和U程的利用方面有诸多的限制。但是即使不知道q些知识做构架设计和实现的情况也随处可见。这个就是那些经怸能再现的bug的温床吧?/p>
因此Q我惛_成几回来写一些准则来防止陷入到这些圈套里?strong>
准则 1 Q不依赖于信h发的设计
·l其他进E以及自己发送异步信号ƈ改变处理程的设计不要做
异步信号是值用killpȝ调用来创?发送的信号、例如SIGUSR1,SIGUSR2,SIGINT,SIGTERM {?
单的使用忽略信号(SIG_IGN)则没有问?
·不要把线E和信号一起?nbsp;
说明Q?/p>
同步信号是指Q因为某些特定的操作*1 而引起向自nq程发送某些特定的信号Q例如SIGSEGV,SIGBUS,SIGPIPE,SIGSYS,SIGILL,SIGFPE。异步信号就是这些以外的信号。在什么时机发送异步信号ƈ不能被预出来。我们会在程序里q加收到某些信号时做一些特D处?信号处理函数)的函数。那么根据收到的信号pC号处理函数的E序叫?#8221;在Q意代码处都能发生跌{”的程序。这LE序往往隐藏q下面的那些问题Q?/p>
Ҏ引入 BUG ?#8221;L的代?#8221;虽然也包?#8221;执行C/C++里面的一条语句的q程?#8221;的意思,但这很容易蟩出程序员的正常思维以及默认的假定条件。编写程序的时候往往需要考虑比C++异常分支q要多得多的分支情况?
使测试项目激?/strong>。即使根据白盒测试达?00%的分支覆?也不能网|到因ؓ接受信号而发生的跌{分支处理。也是说做?00%的网|信可转分支的试是不能全部实现的。一般的Q加上要考虑” 在实行某个特定代码时因ؓ接受C可发生的误操?#8221;q样的BUG会经常发?a title="id:yupo5656:20040703 のsigsafeの記事を参照" >*2 的这U情况,试困难往往是D软g的品质低下的诱因?
Ҏl验Q?#8221;当检查到子进E结?接收到SIGCHLD信号)Ӟ要做必要的处?#8221;像这L信号处理不管做什么都是有必要的情况会有,但是除此以外的信号处理,例如
把自q状态用信号告诉其他q程
ȝE在输入输出函数里发送信L被阻塞的子线E,q解除阻?
{,是应该事先好好好好考虑q后再去做实际的实现。前者的话,如果不强制在”普通的”q程间进行通信的话可能会很好,后者是Ҏ要用线E,也要应该按照即d了也不能发生问题那样再设计?/p>
不管怎么P如果必须要用信L话,也要先全?a title="とりあえず「鉄則2」を :-) " >*3 理解q些陷阱以及Q和多线EY件设计的场合一h者说比它更严格的制约.注意事项都需要铭记在心里?/p>
*1 Q例如,引用I指?/p>
*2 Q参?id:yupo5656:20040703 的sigsafe说明
*3 Q暂时先掌握”准则2”:-)
]]> 准则2Q要知道信号处理函数中可以做那些处理 http://m.shnenglu.com/lymons/archive/2008/06/01/51837.htmllymons lymons Sun, 01 Jun 2008 12:22:00 GMT http://m.shnenglu.com/lymons/archive/2008/06/01/51837.html http://m.shnenglu.com/lymons/comments/51837.html http://m.shnenglu.com/lymons/archive/2008/06/01/51837.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/51837.html http://m.shnenglu.com/lymons/services/trackbacks/51837.html
From 2008_N?/a>
UNIX ?/span> C++E序 设计守则 (2) 原文地址Q?/span> http://d.hatena.ne.jp/yupo5656/20040712/p2
准则 2: 要知道信号处理函C可以做那些处?/span> · 在用 sigaction 函数登记的信号处理函C可以做的处理是被严格限定?/span> · 仅仅允许做下面的三种处理 1. 局部变量的相关处理 2. “volatile sig_atomic_t” cd的全局变量的相x?/span> 3. 调用异步信号安全的相兛_?/span> · 以外的其他处理不要做 Q?/span>
说明 Q?/span> 因ؓ在收Ch要做一些处理,那通常是准备一个信号处理函数ƈ?/span>sigaction 函数把它和信号名q行兌的话?/span>OK 了。但是,在这个信号处理函数里可以做的处理是像上面那样被严格限定的。没有很好掌握这些知识就随便写一些代码的话就会引起下面那L问题 Q?/span>· 问题 1: 有程序死锁的危险 o q是那些依赖于某一时刻Q而且错误再现比较困难?/span>BUG 产生的真正原?/span> o 死锁是一个比较典型的例子Q除此之外还能引起函数返回g正确Q以及在某一函数内执行时H然收到 SEGV 信号{的误操作?/span> ?/em> 译者注 1 Q?/span>SEGV 通常 ?/span>生在 q?/span>E?/span>试图讉K 无效内存区域 ?/span>Q可能是?/span>NULL ?/span>?/span>Q或出 q?/span>E空 ?/span>之外的内存地址Q。当 bug 原因?/span>SEGV 影响在不?/span>旉 ?/span>现时 Q它 ?/span>?/span>别难 于捕 ?/span>到?/span>
· 问题 2: ׃~译器无意识的优化操作,有导致程序紊q危险 o q是跟编译器以及~译器优化别有关系?/span>bug 。它也是“~译器做了优化处理而不能正常动?#8221;Q?#8220;因ؓ inline 化了E序不能动作?#8221;Q?#8220;变换?/span>OS 了程序也不能动作”{这些解析困?/span>bug 产生的原因?/span>
q是一边看具体的代码一边解说吧。在下面的代码里臛_有三个问题,Ҏ环境的不同很可能引v不正的动作 *1 ?/span>按照ơ序来说明里面的错误 ?/span>
1 int gSignaled; 2 void sig_handler( int signo) { 3 std::printf(" signal %d received!\n " , signo); 4 gSignaled = 1 ; 5 } 6 int main( void ) { 7 struct sigaction sa; 8 // (省略) 9 sigaction(SIGINT, & sa, 0 ); 10 gSignaled = 0 ; 11 while ( ! gSignaled) { 12 // std::printf("waiting \n"); 13 struct timespec t = { 1 , 0 } ; nanosleep( & t, 0 ); 14 }15 }16
错误 1: 竞争条g 在上面的代码里有竞争条g ?/span>?/span>sigaction 函数被调用后 ?/span>?/span>gSignaled q未被赋值成 0 g前,如果接受?/span>SIGINT 信号了那会变得怎么样呢 ? 在信号处理函C被覆写成 1 后的 gSignaled 会在信号处理函数q回后被初始化成 0 ?/span>在后面的 while 循环里可能会变成d@?/span>?/span>
错误 2: 全局变量 gSignaled 声明的类型不正确 在信号处理函数里使用的全局变数 gSignaled 的类型没有声明成 volatile sig_atomic_t ?/span>q样的话 ?/span>在执?/span>while 循环里的代码的时候接收到了了 SIGINT 信号?/span>?/span>有可能引?/span>while 的死循环 ?/span>那ؓ什么能引vq样的情况呢 Q?span> · 信号处理函数里,把内存上 gSignaled 的值变更成 1 Q它的汇~代码如下:
movl $1, gSignaled
· 但是Q就像下面的代码描述的那P main 函数是把 gSignaled 的值存攑ֈ了寄存器?/span>?/span>?/span>w hile 循环之前Q仅仅是做了一ơ拷贝变?/span>gSignaled 内存上的值到寄存器里 ?/span>而在 while 循环里只是参照这个寄存器里的?/span>?/span>
movl gSignaled, %ebx .L8: testl %ebx, %ebx jne .L8
在不执行优化的情况下~译后编译器有可能不会生成上面那L伪代?/span>?/span>?/span>Gcc 当?/span>-O2 选项做优化编译时Q生成的实际那样的汇~代码生的危害q不仅仅是像上面说的威胁那样单。这斚w的问题,是设备驱动的开发者所要知道的常识Q但现实情况是对于应用程序的设计?/span>. 开发者几乎都不知道这些知识?/span>Z解决上面的问题,全局变量 gSignaled 的类型要像下面那样声?/span>?/span>
volatile sig_atomic_t gSignaled;
volatile 则是提示~译器不要像上面那样做优化处理,变成每次循环都要参照该变量内存里的值那栯行编译。所以在信号处理函数里把该变量的g改后也能真实反映?/span>main 函数?/span>while 循环?/span>?/span>sig_atomic_t 是根?/span>CPU cd使用 typedef 来适当定义的整数|例如 x86 q_?/span>int c?/span>?/span>?/span>是?/span>” 用一条机器指令来更新内存里的最?a target=_blank name=20040712fn2>数据 ?/span>( 参照译者注 2) ?/span>另外Q?/span> sig_atomic_t cd的变量的取D围是?/span>SIG_ATOMIC_MIN/MAX 之间 ( 参见 §7.18.3/2) ?/span>有无W号是跟具体的实现有兟뀂考虑到移植性取值在 0 ?span>127 之间是比较合适的 ?span>C99 也支持这个取D?/span>?/span>C++ 规范 (14882:2003) 里也有同L描述 ?/span>切的位|是 §1.9/9 q里 ?/span>?/span>SUSv3 的相xq请参?/span>sigaction q里 *3 ?/span>此外 ?/span>虽然?/span>GCC 的参考手册里?/span>说了 把指针类型更新成原子操作Q但在标?/span>C/C++ 却没有记?/span>*5 ?/span>你一定会惊讶于这个表里的函数吧?/span>另外Q一定不要忘C下的几点Q?/span> · 虽然?/span>SUSv3 里有异步信号安全 ( async-signal-safe ) 函数的一览,但根据不同的操作pȝQ某些函数是没有被实现的。所以一定要参考操作系l的手册 · W三者做成的函数Q如果没有特别说明的场合Q首先要假定q个函数是不可重入函敎ͼ不能随便在信 号处理函C使用?/span> · 调用不可重入函数的那些函数就会变成不可重入函C
最后,Z明确赯Q想说明一下什么是 ” 异步信号安全 ( async-signal-safe )” 函数 ?/span>异步信号安全函数是指 ” 在该函数内部即因ؓ信号而正在被中断Q在其他的地方该函数再被调用了也没有M问题 ” 。如果函C存在更新静态区域里的数据的情况 ( 例如Q?/span>malloc) Q一般情况下都是不全的异步信号函数。但是,即使用静态数据,如果在这里这个数据时候把信号屏蔽了的话,它就会变成异步信号安全函C?/span>?/span>译者注 3 Q不可重入函数就不是异步信号安全函数
*1 Q?span>sigaction
函数被调用前Q一接收?/span>SIGINT 信号q止程序,暂且除外?/span>*2 Q?/span>“最?#8221;是不完全正确?/span>?/span>例如Q?/span>Alpha q_?/span>32/64bit 的变量用一条命令也能被更新Q但是好像把 8/16bit 的数据更新编E了多条命o?/span>?/span>http://lists.sourceforge.jp/mailman/archives/anthy-dev/2005-September/002336.html 请参考这?/span>URL 地址 ?/span> *3 Q?/font>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. *4 Q?/span>在这个手册里“ In practice, you can assume that int and other integer types no longer than int are atomic. ”q部分是不正的 ?/span>请参?/span>Alpha 的例?/span> *5 Q?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. 后面 有异步信号安全函C?/span>
]]> 准则3Q多U程E序里不准用fork http://m.shnenglu.com/lymons/archive/2008/06/01/51836.htmllymons lymons Sun, 01 Jun 2008 12:16:00 GMT http://m.shnenglu.com/lymons/archive/2008/06/01/51836.html http://m.shnenglu.com/lymons/comments/51836.html http://m.shnenglu.com/lymons/archive/2008/06/01/51836.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/51836.html http://m.shnenglu.com/lymons/services/trackbacks/51836.html 阅读全文 ]]> 在xml里追加结Ҏd回R(libxml2) http://m.shnenglu.com/lymons/archive/2007/11/30/37553.htmllymons lymons Fri, 30 Nov 2007 05:44:00 GMT http://m.shnenglu.com/lymons/archive/2007/11/30/37553.html http://m.shnenglu.com/lymons/comments/37553.html http://m.shnenglu.com/lymons/archive/2007/11/30/37553.html#Feedback 4 http://m.shnenglu.com/lymons/comments/commentRss/37553.html http://m.shnenglu.com/lymons/services/trackbacks/37553.html 不过在测试时我们发现用文章里F. d属性例E代?Ӟd的keywordl点后面没有回RQ? 跟后面的l点挤在一行了Q不是很好看? 例如Q以下的xml例子文g 阅读全文 ]]> l文仉讑֮timeout http://m.shnenglu.com/lymons/archive/2007/11/26/37318.htmllymons lymons Mon, 26 Nov 2007 06:52:00 GMT http://m.shnenglu.com/lymons/archive/2007/11/26/37318.html http://m.shnenglu.com/lymons/comments/37318.html http://m.shnenglu.com/lymons/archive/2007/11/26/37318.html#Feedback 0 http://m.shnenglu.com/lymons/comments/commentRss/37318.html http://m.shnenglu.com/lymons/services/trackbacks/37318.html
From 2008_N?/a>
Linux 文g锁是锁,也有人把它叫做记录锁Q是通过pȝ调用fcntl(2)来实现的?br>q种锁在锁定文g时有两种模式Q分别是d(block)和非d模式?br>在编码时比较常用的是有一U的非阻塞模式,也就是发现文件已l被其他q程 锁定Ӟ立即q回不予{待。而阻塞模式则正好与它相反Q也是一直等待直?br>其他q程释放文g锁ؓ止?br>注:关于详细内容请参看《Unix环境高~程?/span> 不过Q有的时候也会用到阻塞模式的文g锁,而且会要求不能被一直阻塞,{待 了一定时间后应返回。也是_想d版本的文仉加上一个超时时?timeout)?br> 通过man手册Qfcntl(2)里面没有关于在阻塞模式时Q设|超时时间的M描述?br>但从man手册里我们发玎ͼ文g锁在d时会被信?signal)中断。所以我们就?br>可以利用讄信号软中断来实现一个自q本的{待时呢?br>
1 #include < stdio.h > 2 #include < stdlib.h > 3 #include < unistd.h > 4 #include < fcntl.h > 5 #include < sys / types.h > 6 #include < sys / stat.h > 7 #include < signal.h > 8 9 #define readw_lock(fd) \ 10 lock_reg((fd), F_SETLKW, F_RDLCK) 11 #define writew_lock(fd) \ 12 lock_reg((fd), F_SETLKW, F_WRLCK) 13 #define unlock(fd) \ 14 lock_reg((fd), F_SETLK, F_UNLCK) 15 16 typedef int ( * LW_FN)( char * fname); 17 18 int 19 lock_reg( int fd, int cmd, int type) 20 { 21 struct flock lock ; 22 lock .l_type = type; 23 lock .l_start = 0 ; 24 lock .l_whence = SEEK_SET; 25 lock .l_len = 0 ; 26 27 return fcntl(fd, cmd, & lock ); 28 } 29 30 void hander( int signo) 31 { 32 // do nothing 33 return ; 34 } 35 36 int lockw( char * fname, LW_FN fn, int timeout) 37 { 38 int ret = 0 ; 39 int fd; 40 struct sigaction act, oact; 41 42 if ((fd = open(fname, O_CREAT | O_RDWR, 0666 )) == - 1 ) { 43 printf( " open failed!\n " ); 44 return - 1 ; 45 } 46 47 // set timer to wakeup fcntl 48 act.sa_handler = hander; 49 sigemptyset( & act.sa_mask); 50 act.sa_flags = 0 ; // here, must be zero for wakeup fcntl 51 sigaction(SIGALRM, & act, & oact); 52 53 int sec = alarm(timeout); 54 55 if (writew_lock(fd) == 0 ) { 56 alarm(sec); 57 // recovery signal handler. 58 sigaction(SIGALRM, & oact, NULL); 59 60 printf(" locked OK!\n " ); 61 62 // here, add code about file. 63 #ifdef _TEST 64 getchar(); 65 ret = 0 ; 66 #else 67 ret = fn(fname); 68 #endif 69 70 printf( " unlocked!\n " ); 71 unlock(fd); 72 } 73 else { 74 alarm(sec); 75 // recovery signal handler. 76 sigaction(SIGALRM, & oact, NULL); 77 // lock failed, because of timeout. 78 printf( " write lock failed\n " ); 79 ret = - 1 ; 80 } 81 82 return ret; 83 } 84 85 // test code 86 int func( char * fname) 87 { 88 printf( " check file:%s \n " , fname); 89 getchar(); 90 return 0 ; 91 } 92 93 int main() 94 { 95 return lockw( " file.lock " , func, 5 ); 96 } 97 98
该程序的原理是,利用了alarm(2)讄的定时器Q在一定时间过后会产生SIGALRM信号Q会使当前正?br>执行的系l调用中断,D该系l调?fcntl)q回p|?/span>
上述代码有以下的说明Q?br> 1. 信号处理函数hander是一个空函数Q里面什么也不做。它的存在就是ؓ了接?strong>SIGALRM信号 2. sigaction ?strong>sa_flags 成员一定要讄?Q否则不会是pȝ调用中断 3. Z防止把以前设|的定时器破坏,不管是加锁成功还是失败都立即恢复以前的定时器?br> 4. 因ؓZ接收SIGALRM 信号Q我们设|了它的信号处理函数。那在加锁失败和成功后也要恢复以前的讑֮?br> 注:虽然上面的代码能实现文g锁超时等待的问题Q但又引入了另一个问题,是该代码会破坏以前讑֮的定时器Q即使是后面也恢复了以前的定时器讄Q也会有一些副作用。比如:当ؓ了等待其他进E释放文仉Q传递到lockw函数里的{待旉(也就是Ş参timeout)过了以前设定的定时器触发时_那这D|间内的以前设定的定时器就无效了。也是? 在调用lockw之前,该进E了已经讑֮了一?U的定时? 而这个进E在调用lockw时传递的timeout旉?0U?锁定的阻塞时间ؓ10U?, 那么从调用lockw的那一刻v,2U的定时器就无效?知道锁定成功或者失败ؓ?
]]> 青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
ŷר߲ |
ŷۺɫС˵ |
һ |
αtsһ |
Ʒǿˬˬˬ |
ŷtv |
ѹۿպav |
ŷպdvd߹ۿ |
ѹۿ³³³³³Ƶ |
ŷ߹ۿƵ
|
ۺ |
Ʒһ |
þþƷӰ |
ŷպһ |
ҹƷ |
þþƷƷ
|
ݺݾþۺò |
Ʒ99һһ |
Ƶ߹ۿ
|
һѹۿ |
պһ |
ӰԺ |
һɫþۺϺݺƪô |
Ƶѹۿ |
ŷպ |
ŷۺϾþ |
ۺ |
Ʒһ |
þþøһ |
ţӰһ |
þþƷһӰԺ |
ŷþþþþþ |
Ʒһþþ |
Ʒպþ |
һĻ |
ȾƷƵѹۿ
|
˳С˵վɫ |
һվ |
þþùƵŮո |
þþƷһѿ |
Ӱ |
Ƶ |
ŷСƵ |
һߵӰ |
þþƷһ
|
պٸ߹ۿ |
ľƷƵ |
ŷ˹һ |
ƷҹСƵ |
ҹɫһ |
þҹҹݺ2022 |
ҹһ |
ŮͼƬһ |
һƵ߹ۿ |
þþavվ |
ۺպ |
ŷպƷ |
99ƷȾþ |
þƵѹۿ |
ŷ߹ۿ |
þaaaaxxxx |
Ʒþù |
˹ۿ |
Ʒۺ |
ŷƬ߹ۿ |
Ƶ˹ۿwww |
ŷպ |
ŷձרһ |
ŷպƷ |
ŷ |
һӰԺ |
ũ帾ŮƷ |
һ |
ŷ1819sex岥 |
Ʒ߳þ |
һ߹ۿƵ |
avƷþþþ |
ŷսþþþþþþ |
ʾƷŷƷ |
þþ91ƷһƷ |
ŷר߲ |
Ʒavþ707 |
ŷۺɫ |
˳ɫۺϾþ |
պƵ |
˸徫Ʒ |
ŷһ13˾ |
˸Ƭ߲ |
þù |
ŷද |
պƵþ |
ŷһƷƬ |
ۺ |
պһav |
ŷҹһ |
ŷ |
þùƷһ |
Ƶwww |
|
˾Ʒþ |
ŷպһ |
ŷһƵ |
ŷպƷ |
ŷ |
þtsһ
|
Ƶ߹ۿһ |
һһþ㽶 |
һaëƬ |
ŷ߲ |
þþƷһ |
ŷƷպ |
ŷһƬ |
Ƶ߶ |
xxxx137 |
þó˹ |
ŷպСƵ߹ۿ |
ŷһƬaaaaaaaƵ |
ŷvavaۺ |
ҹӰԺպ |
ŷ˼ |
³ʦӰԺһ |
ŷƷһ߹ۿ |
ŷ |
Ʒ鶹ŷպww
|
ŷvideohd |
ŷ |
þþƷ |
ŷպһ |
߾Ʒ |
ŷպ |
ݺݶۺϾþþ |
һ |
Ʒav |
99ƷԲ |
þþþþ69 |
ŷƵ |
Ƶ |
һվ |
ŷƵ߹ۿ |
žž91 |
ɫ |
þþþƷŷ |
Ůջһ |
ݺɫþۺ϶
|
һ߹ۿƵ
|
ëƬƷһ |
ҹav |
ҹƷ |
ŷƵ |
þþۺϼ |
ƷƵ |
þۺ |
ɫһ |
һһþ |
ŷ߹ۿƵ |
ƷһƵ |
һ.www |
þþַ |
ݺadyƷӰ |
ŷձ |
ŷ12 |
һ |
ǧһ |
ŷƷv |
ŷһ |
ŷƵƷ߹ۿ |
ҹƵ |
ɫav㽶һ |
ѹۿŷ |
ŷ˸ |
պƵ |
þþþùɫavѿӰԺ |
һ |
ŷgayƵ |
Ů |
þþѾƷƵ |
ŷƷþ |
Ƶ |
ŷһҹƵ |
Ӱ |
һ߹ۿƵ |
պƷ߲ |
ȾƷ |
һƵ߲ |
þþþһ |
պۺһ |
ŷƷһ |
þþƷ1 |
123 |
һɫ**ۺƷ |
ŮƷƵվ |
þ˽˹Ʒvaҩ |
һƵ |
Ļպŷ |
ձ۵Ӱ |
þþþ99 |
һƷ |
ŷ鶹 |
ŷgayƵ |
av |