posts - 200, comments - 8, trackbacks - 0, articles - 0

2015年1月13日

     摘要: 1    背景    12    Volatile:易变的    12.1    小结    23    Volatil...  阅读全文

posted @ 2015-01-13 20:35 鑫龙 阅读(375) | 评论 (0)编辑 收藏

2014年9月29日

     摘要: 转自:http://my.oschina.net/moooofly/blog/175019最近看 GLib 的代码遇到这个东东,网上搜索一圈,发现很多人都写过这个,自己今天才研究到,汗颜一下,扫盲一个点,留此记录为证! 首先看一篇最官方的讲解: ====== likely() and unlikely() What are they ?       In Linux ...  阅读全文

posted @ 2014-09-29 21:31 鑫龙 阅读(7545) | 评论 (0)编辑 收藏

2014年9月23日

一、linux下安装cmake

首先下载源码包
        http://www.cmake.org/cmake/resources/software.html

        这里下载的是cmake-2.8.9.tar.gz

随便找个目录解压缩

1         tar -xzvf cmake-2.8.9.tar.gz
2         cd cmake-cmake-2.8.9

        依次执行:

1         ./bootstrap
2          make
3         make install

        cmake 会默认安装在 /usr/local/bin 下面

要改变安装路径,在bootstrap命令中加入'--prefix=PATH'选项。


二、linux下安装boost
linux平台下要编译安装除gcc和gcc-c++之外,还需要两个开发库:bzip2-devel 和python-devel,因此在安装前应该先保证这两个库已经安装:

#yum install gcc gcc-c++ bzip2 bzip2-devel bzip2-libs python-devel -y

然后是去官网下载源码包,地址
下载,解压,按照如下步骤:

#tar xvzf boost_1_50_0.tar.gz

进入boost_1_50_0目录:

#cd boost_1_50_0

然后是编译安装,boost源码包中有配置脚本,直接用就可以:

#sh ./bootstrap.sh

Building Boost.Build engine with toolset gcc... tools/build/v2/engine/bin.linuxx86_64/b2
Detecting Python version... 2.6
Detecting Python root... /usr
Unicode/ICU support for Boost.Regex?... not found.
Generating Boost.Build configuration in project-config.jam...

Bootstrapping is done. To build, run:

./b2

To adjust configuration, edit 'project-config.jam'.
Further information:

- Command line help:
./b2 --help

- Getting started guide:
http://www.boost.org/more/getting_started/unix-variants.html

- Boost.Build documentation:
http://www.boost.org/boost-build2/doc/html/index.html

接下来就是编译,重点关注是否编译成功:

#./b2

然后就是漫长的等待,如果最后出现:

The Boost C++ Libraries were successfully built!

The following directory should be added to compiler include paths:

/home/gang/BAK/boost_1_50_0

The following directory should be added to linker library paths:

/home/gang/BAK/boost_1_50_0/stage/lib

表示编译成功,如果没有成功,就需要回查看哪里出现error,再安装相应的库,
最后就是安装:

#./b2 install --prefix=/usr/local

安装后的头文件在/usr/local/include/boost里面,而相应的库在/usr/local/lib/libboost_*

posted @ 2014-09-23 09:29 鑫龙 阅读(1029) | 评论 (0)编辑 收藏

2014年9月17日

转自:http://www.cnblogs.com/jiqing9006/archive/2012/09/11/2679831.html

冒泡事件就是点击子节点,会向上触发父节点,祖先节点的点击事件。

下面是html代码部分:

<body>
<div id="content">
    外层div元素
    <span>内层span元素</span>
    外层div元素
</div>

<div id="msg"></div>
</body>

对应的jQuery代码如下:

<script type="text/javascript">
$(function(){
    // 为span元素绑定click事件
    $('span').bind("click",function(){
        var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>";//获取html信息
        $('#msg').html(txt);// 设置html信息
    });
    // 为div元素绑定click事件
    $('#content').bind("click",function(){
        var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>";
        $('#msg').html(txt);
    });
    // 为body元素绑定click事件
    $("body").bind("click",function(){
        var txt = $('#msg').html() + "<p>body元素被点击.<p/>";
        $('#msg').html(txt);
    });
})
</script>

当点击span时,会触发div与body 的点击事件。点击div时会触发body的点击事件。

如何防止这种冒泡事件发生呢?

修改如下:

<script type="text/javascript">
$(function(){
       // 为span元素绑定click事件
    $('span').bind("click",function(event){
        var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>";
        $('#msg').html(txt);
        event.stopPropagation();    //  阻止事件冒泡
    });
    // 为div元素绑定click事件
    $('#content').bind("click",function(event){
        var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>";
        $('#msg').html(txt);
        event.stopPropagation();    //  阻止事件冒泡
    });
    // 为body元素绑定click事件
    $("body").bind("click",function(){
        var txt = $('#msg').html() + "<p>body元素被点击.<p/>";
        $('#msg').html(txt);
    });
})
</script>

event.stopPropagation(); // 阻止事件冒泡

 

有时候点击提交按钮会有一些默认事件。比如跳转到别的界面。但是如果没有通过验证的话,就不应该跳转。这时候可以通过设置event.preventDefault(); //阻止默认行为 ( 表单提交 )。

下面是案例:

<script type="text/javascript">
$(function(){
   $("#sub").bind("click",function(event){
         var username = $("#username").val();  //获取元素的值,val() 方法返回或设置被选元素的值。
         if(username==""){     //判断值是否为空
             $("#msg").html("<p>文本框的值不能为空.</p>");  //提示信息
             event.preventDefault();  //阻止默认行为 ( 表单提交 )
         }
   })
})
</script>

html部分:

<body>
<form action="test.html">
用户名:<input type="text" id="username" />
<br/>
<input type="submit" value="提交" id="sub"/>
</form>

<div id="msg"></div>
</body>

还有一种防止默认行为的方法就是return false。效果一样。

代码如下:

<script type="text/javascript">
$(function(){
   $("#sub").bind("click",function(event){
         var username = $("#username").val();  //获取元素的值
         if(username==""){     //判断值是否为空
             $("#msg").html("<p>文本框的值不能为空.</p>");  //提示信息
             return false;
         }
   })
})
</script>

同理,上面的冒泡事件也可以通过return false来处理。

<script type="text/javascript">
$(function(){
       // 为span元素绑定click事件
    $('span').bind("click",function(event){
        var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>";
        $('#msg').html(txt);
        return false;
    });
    // 为div元素绑定click事件
    $('#content').bind("click",function(event){
        var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>";
        $('#msg').html(txt);
        return false;
    });
    // 为body元素绑定click事件
    $("body").bind("click",function(){
        var txt = $('#msg').html() + "<p>body元素被点击.<p/>";
        $('#msg').html(txt);
    });
})
</script>

posted @ 2014-09-17 14:22 鑫龙 阅读(244) | 评论 (0)编辑 收藏

2014年9月12日

转自:http://www.cnblogs.com/jiayy/p/3246167.html

在看多核编程相关论文时,往往一个并发算法会说自己是wait-free的或者lock-free的,或者是 non-blocking 的,这些专有词汇其实表示的是并发的程度,或者说并发的级别。并发级别的理解是阅读各种并发算法设计论文以及并发数据结构实现的必备基础。

1.1  Wait-freedom 无等待并发

Wait-freedom 指的是每一个线程都一直运行下去而无须等待外部条件,整个流程中任何操作都能在一个有限的步骤内完成,这是最高的并发级别,没有任何阻塞。

 结合之前原子操作部分的知识,可以简单认为能够直接调用一个原子操作实现的算法或程序就属于Wait-free,比如下面的 increment_reference_counter 函数就是wait-free的,它封装了atomic_increment这个原子自增原语,多个线程可以同时调用这个函数对同一个内存变量进行自增,而无须任何阻塞(其实也是有阻塞的,是总线锁级别)

 与此做对比,CAS类的调用就不是wait-free的,注意wait-free的原语都不能包含内部循环,CAS原语使用时通常包含在“循环直到成功”的循环内部。

 void increment_reference_counter(rc_base* obj)

{

    atomic_increment(obj->rc);

}

1.2  Lock-freedom 无锁并发

Lock-freedom 指的是整个系统作为一个整体一直运行下去,系统内部单个线程某段时间内可能会饥饿,这是比wait-freedom弱的并发级别,但系统整体上看依然是没有阻塞的。所有wait-free的算法显然都满足lock-free的要求。

 Lock-free算法通常可以通过同步原语 CAS实现。

 void stack_push(stack* s, node* n)

{

    node* head;

    do

    {

        head = s->head;

        n->next = head;

    }

    while ( ! atomic_compare_exchange(s->head, head, n));

}

多个线程同时调用上述函数,理论上某个线程可以一直困在循环内部,但一旦有一个线程原子操作失败而返回循环,意味着有其他线程成功执行了原子操作而退出循环,从而保证系统整体是没有阻塞的。

 其实前面的原子自增函数也可以用下面的原语实现,在这种实现里,不再是所有线程都无阻塞了,某些线程可能会因为CAS失败而回绕若干次循环。

void increment_reference_counter(rc_base* obj)

{

       Int rc;

       Do {

       rc = obj->rc;

} while(!atomic_compare_exchange(obj->rc,rc,rc+1));

}

1.3  Obstruction-freedom 无阻塞并发

Obstruction-free 是指在任何时间点,一个孤立运行线程的每一个操作可以在有限步之内结束。只要没有竞争,线程就可以持续运行,一旦共享数据被修改,Obstruction-free 要求中止已经完成的部分操作,并进行回滚,obstruction-free 是并发级别更低的非阻塞并发,该算法在不出现冲突性操作的情况下提供单线程式的执行进度保证,所有 Lock-Free 的算法都是 Obstruction-free 的。

1.4  Blocking algoithms 阻塞并发

阻塞类的算法是并发级别最低的同步算法,它一般需要产生阻塞。可以简单认为基于锁的实现是blocking的算法。详细参考第五章

上述几种并发级别可以使用下图描述:
蓝色是阻塞的算法,绿色是非阻塞算法,金字塔越上方,并发级别越高,性能越好,右边的金字塔是实现工具(原子操作、锁、互斥体等)

posted @ 2014-09-12 13:05 鑫龙 阅读(362) | 评论 (0)编辑 收藏

2014年9月3日

转自:http://www.isnowfy.com/understand-to-lock-free/

以前一直不明白lock free是什么,后来发现原来是完全理解错了概念,lock free看到大家有的翻译为无锁,有的翻译为锁无关,其实用不用锁和lock free是不相关的,用了锁也可能是lock free,而不用锁有可能不是lock free。

一个lock free的解释是

一个“锁无关”的程序能够确保执行它的所有线程中至少有一个能够继续往下执行。

其实看我们那副图就是说你的各个线程不会互相阻塞,那么你的程序才能成为lock free的。像我们平常用的互斥锁,当有线程获得锁,其他线程就被阻塞掉了,这里的问题就是如果获得锁的线程挂掉了,而且锁也没有释放,那么整个程序其实就被block在那了,而如果程序是lock free的那么即使有线程挂掉,也不影响整个程序继续向下进行,也就是系统在整体上而言是一直前进的。

那么,不用锁就是lock free的吗,一开始就提到了,不用锁也可能不是lock free的,举个例子

  1. while (x == 0) {
  2.     x = 1-x;
  3. }

在这里如果两个线程同时执行,可能同时进入while循环,然后x两次改变值之后,依然是0,那么两个线程就会一直互相在这里阻塞掉了,所以这里虽然没有锁,依然不是lock free的。

现在大家写lock free的时候一般都会使用CAS(compare and set)操作来写,因为现在很多的cpu都是支持CAS操作并作为原子操作来处理的,CAS操作一般是这样的

  1. bool compare_and_swap (int *oldval, int *dest, int newval) {
  2.   if (*oldval == *dest) {
  3.       *dest = newval;
  4.       return true;
  5.   }
  6.   return false;
  7. }

其实这样一个操作和乐观锁很像,并且操作简单,相应的比互斥锁的代价要小。所以现在大家都是喜欢用lock free的技术来提高系统的performance。

最后如果大家对于如何编写lock free的数据结构感兴趣的话,可以参考我后面给出的链接。

一种高效无锁内存队列的实现
无锁队列的实现
锁无关的(Lock-Free)数据结构
An Introduction to Lock-Free Programming

posted @ 2014-09-03 21:11 鑫龙 阅读(3180) | 评论 (0)编辑 收藏

2014年8月23日

转自:http://zhucuicui.96986.blog.163.com/blog/static/5833370220136219016445/

建立socket后默认connect()函数为阻塞连接状态,在大多数实现中,connect的超时时间在75s至几分钟之间,想要缩短超时时间,可解决问题的两种方法:方法一、将socket句柄设置为非阻塞状态,方法二、采用信号处理函数设置阻塞超时控制。

在一个TCP套接口被设置为非阻塞之后调用connect,connect会立即返回EINPROGRESS错误,表示连接操作正在进行中,但是仍未完成;同时TCP的三路握手操作继续进行;在这之后,我们可以调用select来检查这个链接是否建立成功;非阻塞connect有三种用途:
1.我们可以在三路握手的同时做一些其它的处理.connect操作要花一个往返时间完成,而且可以是在任何地方,从几个毫秒的局域网到几百毫秒或几秒的广域网.在这段时间内我们可能有一些其他的处理想要执行;
2.可以用这种技术同时建立多个连接.在Web浏览器中很普遍;
3.由于我们使用select来等待连接的完成,因此我们可以给select设置一个时间限制,从而缩短connect的超时时间.在大多数实现中,connect的超时时间在75秒到几分钟之间.有时候应用程序想要一个更短的超时时间,使用非阻塞connect就是一种方法;
非阻塞connect听起来虽然简单,但是仍然有一些细节问题要处理:
1.即使套接口是非阻塞的,如果连接的服务器在同一台主机上,那么在调用connect建立连接时,连接通常会立即建立成功.我们必须处理这种情况;
2.源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
A:当连接建立成功时,套接口描述符变成可写;
B:当连接出错时,套接口描述符变成既可读又可写;
注意:当一个套接口出错时,它会被select调用标记为既可读又可写;

非阻塞connect有这么多好处,但是处理非阻塞connect时会遇到很多可移植性问题;

处理非阻塞connect的步骤:
第一步:创建socket,返回套接口描述符;
第二步:调用fcntl把套接口描述符设置成非阻塞;
第三步:调用connect开始建立连接;
第四步:判断连接是否成功建立;
A:如果connect返回0,表示连接简称成功(服务器可客户端在同一台机器上时就有可能发生这种情况);
B:调用select来等待连接建立成功完成;
如果select返回0,则表示建立连接超时;我们返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去;
如果select返回大于0的值,则需要检查套接口描述符是否可读或可写;如果套接口描述符可读或可写,则我们可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0,如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比如:ECONNREFUSED,ETIMEDOUT等).
"读取套接口上的错误"是遇到的第一个可移植性问题;如果出现问题,getsockopt源自Berkeley的实现是返回0,等待处理的错误在变量errno中返回;但是Solaris会让getsockopt返回-1,errno置为待处理的错误;我们对这两种情况都要处理;

这样,在处理非阻塞connect时,在不同的套接口实现的平台中存在的移植性问题,首先,有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来.在这种情况下,连接成功时套接口将既可读又可写.这和连接失败时是一样的.这个时候我们还得通过getsockopt来读取错误值;这是第二个可移植性问题;
移植性问题总结:
1.对于出错的套接口描述符,getsockopt的返回值源自Berkeley的实现是返回0,待处理的错误值存储在errno中;而源自Solaris的实现是返回0,待处理的错误存储在errno中;(套接口描述符出错时调用getsockopt的返回值不可移植)
2.有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来,在这种情况下,套接口描述符是既可读又可写;这与套接口描述符出错时是一样的;(怎样判断连接是否建立成功的条件不可移植)

这样的话,在我们判断连接是否建立成功的条件不唯一时,我们可以有以下的方法来解决这个问题:
1.调用getpeername代替getsockopt.如果调用getpeername失败,getpeername返回ENOTCONN,表示连接建立失败,我们必须以SO_ERROR调用getsockopt得到套接口描述符上的待处理错误;
2.调用read,读取长度为0字节的数据.如果read调用失败,则表示连接建立失败,而且read返回的errno指明了连接失败的原因.如果连接建立成功,read应该返回0;
3.再调用一次connect.它应该失败,如果错误errno是EISCONN,就表示套接口已经建立,而且第一次连接是成功的;否则,连接就是失败的;

被中断的connect:
如果在一个阻塞式套接口上调用connect,在TCP的三路握手操作完成之前被中断了,比如说,被捕获的信号中断,将会发生什么呢?假定connect不会自动重启,它将返回EINTR.那么,这个时候,我们就不能再调用connect等待连接建立完成了,如果再次调用connect来等待连接建立完成的话,connect将会返回错误值EADDRINUSE.在这种情况下,应该做的是调用select,就像在非阻塞式connect中所做的一样.然后,select在连接建立成功(使套接口描述符可写)或连接建立失败(使套接口描述符既可读又可写)时返回;

方法二、定义信号处理函数:

 

  1. sigset(SIGALRM, u_alarm_handler);
  2. alarm(2);
  3. code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
  4. alarm(0);
  5. sigrelse(SIGALRM);

首先定义一个中断信号处理函数u_alarm_handler,用于超时后的报警处理,然后定义一个2秒的定时器,执行connect,当系统connect成功,则系统正常执行下去;如果connect不成功阻塞在这里,则超过定义的2秒后,系统会产生一个信号,触发执行u_alarm_handler函数, 当执行完u_alarm_handler后,程序将继续从connect的下面一行执行下去。
其中,处理函数可以如下定义,也可以加入更多的错误处理。

  1. void u_alarm_handler()
  2. {
  3. }

posted @ 2014-08-23 17:01 鑫龙 阅读(1517) | 评论 (0)编辑 收藏

2014年8月7日

引用:http://www.cppblog.com/isware/archive/2011/07/20/151470.aspx
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
“据说”惊群问题已经是一个很古老的问题了,并且在大多数系统中已经得到有效解决,但对我来说,仍旧是一个比较新的概念,因此有必要记录一下。

什么是惊群

        举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。对于操作系统来说,多个进程/线程在等待同一资源是,也会产生类似的效果,其结 果就是每当资源可用,所有的进程/线程都来竞争资源,造成的后果:
1)系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。
2)为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。

        最常见的例子就是对于socket描述符的accept操作,当多个用户进程/线程监听在同一个端口上时,由于实际只可能accept一次,因此就会产生惊群现象,当然前面已经说过了,这个问题是一个古老的问题,新的操作系统内核已经解决了这一问题。

linux内核解决惊群问题的方法

        对于一些已知的惊群问题,内核开发者增加了一个“互斥等待”选项。一个互斥等待的行为与睡眠基本类似,主要的不同点在于:
        1)当一个等待队列入口有 WQ_FLAG_EXCLUSEVE 标志置位, 它被添加到等待队列的尾部. 没有这个标志的入口项, 相反, 添加到开始.
        2)当 wake_up 被在一个等待队列上调用时, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止。
        也就是说,对于互斥等待的行为,比如如对一个listen后的socket描述符,多线程阻塞accept时,系统内核只会唤醒所有正在等待此时间的队列 的第一个,队列中的其他人则继续等待下一次事件的发生,这样就避免的多个线程同时监听同一个socket描述符时的惊群问题。

根据以上背景信息,我们来比较一下常见的Server端设计方案。
方案1:listen后,启动多个线程(进程),对此socket进行监听(仅阻塞accept方式不惊群)。
方案2:主线程负责监听,通过线程池方式处理连接。(通常的方法)
方案3:主线程负责监听,客户端连接上来后由主线程分配实际的端口,客户端根据此端口重新连接,然后处理数据。

先考虑客户端单连接的情况
方案1:每当有新的连接到来时,系统内核会从队列中以FIFO的方式选择一个监听线程来服务此连接,因此可以充分发挥系统的系能并且多线程负载均衡。对于单连接的场景,这种方案无疑是非常优越的。遗憾的是,对于select、epoll,内核目前无法解决惊群问题。(nginx对于惊群问题的解决方法)
方案2:由于只有一个线程在监听,其瞬时的并发处理连接请求的能力必然不如多线程。同时,需要对线程池做调度管理,必然涉及资源共享访问,相对于方案一来说管理成本要增加不少,代码复杂度提高,性能也有所下降。
方案3:与方案2有不少类似的地方,其优势是不需要做线程调度。缺点是增加了主线程的负担,除了接收连接外还需要发送数据,而且需要两次连接,孰优孰劣,有待测试。

再考虑客户端多连接的情况:
对于数据传输类的应用,为了充分利用带宽,往往会开启多个连接来传输数据,连接之间的数据有相互依赖性,因此Server端要想很好的维护这种依赖性,把同一个客户端的所有连接放在一个线程中处理是非常有必要的。
A、同一客户端在一个线程中处理
方案1:如果没有更底层的解决方案的话,Server则需要维护一个全局列表,来记录当前连接请求该由哪个线程处理。多线程需要同时竞争一个全局资源,似乎有些不妙。
方案2:主线程负责监听并分发,因此与单连接相比没有带来额外的性能开销。仅仅会造成主线程忙于更多的连接请求。
方案3:较单线程来说,主线程工作量没有任何增加,由于多连接而造成的额外开销由实际工作线程分担,因此对于这种场景,方案3似乎是最佳选择。

B、同一客户端在不同线程中处理
方案1:同样需要竞争资源。
方案2:没理由。
方案3:不可能。

另外:
(《UNIX网络编程》第三版是在第30章)
读《UNIX网络编程》第二版的第一卷时,发现作者在第27章“客户-服务器程序其它设计方法”中的27.6节“TCP预先派生子进程服务器程序,accept无上锁保护”中提到了一种由子进程去竞争客户端连接的设计方法,用伪码描述如下:

服务器主进程:

listen_fd = socket(...);
bind(listen_fd, ...);
listen(listen_fd, ...);
pre_fork_children(...);
close(listen_fd);
wait_children_die(...);


服务器服务子进程:

while (1) {
conn_fd = accept(listen_fd, ...);
do_service(conn_fd, ...);
}


初识上述代码,真有眼前一亮的感觉,也正如作者所说,以上代码确实很少见(反正我读此书之前是确实没见过)。作者真是构思精巧,巧妙地绕过了常见的预先创建 子进程的多进程服务器当主服务进程接收到新的连接必须想办法将这个连接传递给服务子进程的“陷阱”,上述代码通过共享的倾听套接字,由子进程主动地去向内 核“索要”连接套接字,从而避免了用UNIX域套接字传递文件描述符的“淫技”。

不过,当接着往下读的时候,作者谈到了“惊群” (Thundering herd)问题。所谓的“惊群”就是,当很多进程都阻塞在accept系统调用的时候,即使只有一个新的连接达到,内核也会唤醒所有阻塞在accept上 的进程,这将给系统带来非常大的“震颤”,降低系统性能。

除了这个问题,accept还必须是原子操作。为此,作者在接下来的27.7节讲述了加了互斥锁的版本:

while (1) {
lock(...);
conn_fd = accept(listen_fd, ...);
unlock(...);
do_service(conn_fd, ...);
}


原子操作的问题算是解决了,那么“惊群”呢?文中只是提到在Solaris系统上当子进程数由75变成90后,CPU时间显著增加,并且作者认为这是因为进 程过多,导致内存互换。对“惊群”问题回答地十分含糊。通过比较书中图27.2的第4列和第7列的内容,我们可以肯定“真凶”绝对不是“内存对换”。

“元凶”到底是谁?

仔细分析一下,加锁真的有助于“惊群”问题么?不错,确实在同一时间只有一个子进程在调用accept,其它子进程都阻塞在了lock语句,但是,当 accept返回并unlock之后呢?unlock肯定是要唤醒阻塞在这个锁上的进程的,不过谁都没有规定是唤醒一个还是唤醒多个。所以,潜在的“惊 群”问题还是存在,只不过换了个地方,换了个形式。而造成Solaris性能骤降的“罪魁祸首”很有可能就是“惊群”问题。

崩溃了!这么说所有的锁都有可能产生惊群问题了?

似乎真的是这样,所以减少锁的使用很重要。特别是在竞争比较激烈的地方。

作者在27.9节所实现的“传递文件描述符”版本的服务器就有效地克服了“惊群”问题,在现实的服务器实现中,最常用的也是此节所提到的基于“分配”形式。

把“竞争”换成“分配”是避免“惊群”问题的有效方法,但是也不要忽视“分配”的“均衡”问题,不然后果可能更加严重哦!

posted @ 2014-08-07 14:21 鑫龙 阅读(508) | 评论 (0)编辑 收藏

2014年7月25日

程序设计类
《C++ PRIMER》
《STL源码解析》


系统编程类
《UNIX环境高级编程》W.Richard Stevens:非常经典的书。虽然初学者就可以看,但是事实上它是《Unix Network Programing》的一本辅助资料。国内的翻译的《UNIX环境高级编程》的水平不怎么样,现在有影印版,直接读英文比读中文来得容易。
《Unix网络编程卷二》Unix网络编程卷第二卷没有涉及网络的东西,主要讲进程间通讯和Posix线程。



网络编程类
《Unix网络编程》第一卷讲BSD Socket网络编程接口和另外一种网络编程接口的,不过现在一般都用BSD Socket,所以这本书只要看大约一半多就可以了。
《TCP/IP详解》一共三卷,卷一讲协议,卷二讲实现,卷三讲编程应用,也很经典的。
《用TCP/IP进行网际互连》一共三卷,内容讲解十分精彩。卷一讲原理,卷二讲实现,卷三讲高级协议。感觉上这一套要比Stevens的那一套要好,就连Stevens也不得不承认它的第一卷非常经典。事实上,第一卷即使你没有一点网络的知识,看完以后也会对网络的来龙去脉了如指掌。第一卷中还有很多习题也设计得经典和实用,因为作者本身就是一位教师,并且卷一是国外研究生的教材。习题并没有答案,留给读者思考,因为问题得答案可以让你成为一个中级的Hacker,这些问题的答案可以象Douglus索取,不过只有他只给教师卷二我没有怎么看,卷三可以作为参考手册,其中地例子也很经典。
Linux 多线程服务端编程:使用 muduo C++ 网络库》


Linux系统管理类
《linux系统管理手册》
《LINUX与UNIX SHELL编程指南》
《Advanced Bash Scripting Guide》


系统内核类
《Linux内核代码情景分析》
《深入Linux内核源码》


面向对象设计类
《设计模式》
《敏捷软件开发:原则、模式与实践》
《敏捷项目管理》


内功修炼类
《操作系统:设计与实现(第二版)》
《操作系统概念》
《数据结构与算法-面向对象的C++设计模式》
《编译原理》国防陈火旺
《离散数学及其应用》
《计算机组织与体系结构?性能分析》
《深入理解计算机系统》【美】Randal E. Bryant David O'Hallaron著v

posted @ 2014-07-25 20:29 鑫龙 阅读(1623) | 评论 (0)编辑 收藏

2014年4月8日

程序如下:

import java.util.*;

import java.io.*;

public class BadExecJavac

{

    public static void main(String args[])

    {

        try

        {           

            Runtime rt = Runtime.getRuntime();

            Process proc = rt.exec("javac");

            int exitVal = proc.exitValue();

            System.out.println("Process exitValue: " + exitVal);

        } catch (Throwable t)

          {

            t.printStackTrace();

          }

    }

}

运行结果如下

E:\classes\com\javaworld\jpitfalls\article2>java BadExecJavac
java.lang.IllegalThreadStateException: process has not exited       
at java.lang.Win32Process.exitValue(Native Method)       
at BadExecJavac.main(BadExecJavac.java:13)

这是因为当进程还没有结束的情况下,调用exitValue方法会抛出IllegalThreadStateException.当然了我们会问为什吗这个方法不会等到进程结束在返回一个合理的值?

在检查Process类的所有可用方法以后我们发现WairFor()是一个更合适的方法。事实上waitFor也会返回exit value。这意味着你不可以同时用exitvalue和waitfor,而是只能选择一个。

当然了也有情况你要在waitfor之前用exitvalue方法:就是你不想因为外部程序永远无法完成而一直等待下去。

因此为了避免这个陷阱,我们要么捕获IllegalThreadStateException异常,要么等待进程完成。我们相当然的以为可以用waitfor来等待程序的结束。代码如下:

import java.util.*;
import java.io.*;
public class BadExecJavac2{   
   public static void main(String args[])    {       
      try        {                        
         Runtime rt = Runtime.getRuntime();           
         Process proc = rt.exec("javac");           
         int exitVal = proc.waitFor();           
         System.out.println("Process exitValue: " + exitVal);       
      } catch (Throwable t)          {           
         t.printStackTrace();         
      }   
   }
}

这次在linux下面返回的结果是2,而在windows下面据说程序会挂起,关于其原因我们可以在jdk文档中找到部分解释:因为一些操作系统为标准的输入输出仅仅提供有限的缓冲区,当不能正确的将信息写进输入流或者从输出流中获取信息时,就会导致子进程的阻塞,甚至死锁。现在我们就根据jdk文档来处理javac进程的输出,当你不带任何参数运行javac时,它会打印出一系列的有用的提示信息。而这些会被传送到stderr流中。我们可以写程序在其返回前获取这些信息。下面的代码提供了一个平庸的解决方案。

import java.util.*;import java.io.*;
public class MediocreExecJavac{   
   public static void main(String args[])    {       
       try        {                        
         Runtime rt = Runtime.getRuntime();           
         Process proc = rt.exec("javac");           
         InputStream stderr = proc.getErrorStream();           
         InputStreamReader isr = new InputStreamReader(stderr);           
         BufferedReader br = new BufferedReader(isr);           
         String line = null;           
         System.out.println("<ERROR>");           
         while ( (line = br.readLine()) != null)               
            System.out.println(line);           
         System.out.println("</ERROR>");           
         int exitVal = proc.waitFor();           
         System.out.println("Process exitValue: " + exitVal);       
      } catch (Throwable t)          {           
          t.printStackTrace();         
      }   
   }
}

这次程序可以正确的输出了提示信息,但是我们应该注意到其返回代码是2,我们知道任何非0的返回代码都表示程序不正常。所以我们需要进一步的查找原因。对于win32而言是file not found,很明显javac期望我们提供编译的文件。所以对于永远挂起的问题,如果你运行的程序会有输出或者要求输出入时,你需要处理输出和输入。我在linux下面运行的结果是正确的。前面说了在win32下面2代表是文件没有找到,而在这种情况下表明是dir.exe没有找到,(因为根本就没有这个文件,他们都被封装到common.com (win95)或者cmd.exe中了。

下面我们列出一个正确的处理Process的输入输出流的方法。需要用一个线程类。

import java.util.*;
import java.io.*;
class StreamGobbler extends Thread{   
   InputStream is;   
   String type;        
   StreamGobbler(InputStream is, String type)    {       
       this.is = is;       
       this.type = type;   
   }        

   public void run()    {       
      try        {           
          InputStreamReader isr = new InputStreamReader(is);           
          BufferedReader br = new BufferedReader(isr);           
          String line=null;           
      while ( (line = br.readLine()) != null)               
      System.out.println(type + ">" + line);                
      } catch (IOException ioe)             {               
       ioe.printStackTrace();               
      }   
   }
}

用于专门的处理输入输出。

public class GoodWindowsExec{   
    public static void main(String args[])    {       
        if (args.length < 1)        {           
            System.out.println("USAGE: java GoodWindowsExec <cmd>");           
            System.exit(1);       
        }                
        try        {                        
            String osName = System.getProperty("os.name" );           
            String[] cmd = new String[3];           
            if( osName.equals( "Windows NT" ) )            {               
                cmd[0] = "cmd.exe" ;               
                cmd[1] = "/C" ;               
                cmd[2] = args[0];           
            }            else if( osName.equals( "Windows 95" ) )            {               
                cmd[0] = "command.com" ;               
                cmd[1] = "/C" ;               
                cmd[2] = args[0];            }                        
            Runtime rt = Runtime.getRuntime();            
            System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);           
            Process proc = rt.exec(cmd);            // any error message?           
            StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");  // any output?           
            StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");                            // kick them off           
            errorGobbler.start();           
            outputGobbler.start();                                                // any error???           
            int exitVal = proc.waitFor();           
            System.out.println("ExitValue: " + exitVal);                
        } catch (Throwable t)          {           
            t.printStackTrace();          
        }   
    }
}

如果运行如下命令上面的代码会调用word程序

>java GoodWindowExec “abc.doc”

也就是说文件类型如果window能够识别它就会调用对应的程序处理。

StreamGlobbler的最重要作用是他会清空所有的传递给他的inputstream,这样不会造成Process阻塞或者死锁。

posted @ 2014-04-08 15:20 鑫龙 阅读(3415) | 评论 (0)编辑 收藏