﻿<?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++博客-&lt;font color="#ff8000"&gt;&amp;nbsp&amp;nbsp&amp;nbspC++&amp;nbsp技术中心&lt;/font&gt;-随笔分类-Linux 编程</title><link>http://www.cppblog.com/API/category/16117.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 12 Dec 2017 13:21:13 GMT</lastBuildDate><pubDate>Tue, 12 Dec 2017 13:21:13 GMT</pubDate><ttl>60</ttl><item><title>套接字read/write返回值</title><link>http://www.cppblog.com/API/archive/2017/12/12/215420.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Tue, 12 Dec 2017 02:32:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2017/12/12/215420.html</guid><wfw:comment>http://www.cppblog.com/API/comments/215420.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2017/12/12/215420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/215420.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/215420.html</trackback:ping><description><![CDATA[<p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">1、阻塞模式与非阻塞模式下recv的返回值各代表什么意思？有没有区别？（就我目前了解阻塞与非阻塞recv返回值没有区分，都是&nbsp;&lt;0：出错，=0：连接关闭，&gt;0接收到数据大小，特别：返回值&nbsp;&lt;0时并且(errno&nbsp;==&nbsp;EINTR&nbsp;||&nbsp;errno&nbsp;==&nbsp;EWOULDBLOCK&nbsp;||&nbsp;errno&nbsp;==&nbsp;EAGAIN)的情况下认为连接是正常的，继续接收。只是阻塞模式下recv会阻塞着接收数据，非阻塞模式下如果没有数据会返回，不会阻塞着读，因此需要&nbsp;循环读取</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">2、阻塞模式与非阻塞模式下write的返回值各代表什么意思？有没有区别？</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">阻塞与非阻塞write返回值没有区分，都是&nbsp;&lt;0：出错，=0：连接关闭，&gt;0发送数据大小，特别：返回值&nbsp;&lt;0时并且(errno&nbsp;==&nbsp;EINTR&nbsp;||&nbsp;errno&nbsp;==&nbsp;EWOULDBLOCK&nbsp;||&nbsp;errno&nbsp;==&nbsp;EAGAIN)的情况下认为连接是正常的，继续发送。只是阻塞模式下write会阻塞着发送数据，非阻塞模式下如果暂时无法发送数据会返回，不会阻塞着&nbsp;write，因此需要循环发送</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">3、阻塞模式下read返回值&nbsp;&lt;&nbsp;0&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EINTR&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EWOULDBLOCK&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EAGAIN时，连接异常，需要关闭，read返回值&nbsp;&lt;&nbsp;0&nbsp;&amp;&amp;&nbsp;(errno&nbsp;==&nbsp;EINTR&nbsp;||&nbsp;errno&nbsp;==&nbsp;EWOULDBLOCK&nbsp;||&nbsp;errno&nbsp;==&nbsp;EAGAIN)时表示没有数据，需要继续接收，如果返回值大于0表示接送到数据。&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">非阻塞模式下read返回值&nbsp;&lt;&nbsp;0表示没有数据，=&nbsp;0表示连接断开，&gt;&nbsp;0表示接收到数据。&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">这2种模式下的返回值是不是这么理解，有没有跟详细的理解或跟准确的说明？&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">4、阻塞模式与非阻塞模式下是否send返回值&nbsp;&lt;&nbsp;0&nbsp;&amp;&amp;&nbsp;(errno&nbsp;==&nbsp;EINTR&nbsp;||&nbsp;errno&nbsp;==&nbsp;EWOULDBLOCK&nbsp;||&nbsp;errno&nbsp;==&nbsp;EAGAIN)表示暂时发送失败，需要重试，如果send返回值&nbsp;&lt;=&nbsp;0,&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EINTR&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EWOULDBLOCK&nbsp;&amp;&amp;&nbsp;errno&nbsp;!=&nbsp;EAGAIN时，连接异常，需要关闭，如果send返回值&nbsp;&gt;&nbsp;0则表示发送了数据？send的返回值是否这么理解，阻塞模式与非阻塞模式下send返回值=0是否都是发送失败，还是那个模式下表示暂时不可发送，需要&nbsp;重发？</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">1.&nbsp;send函数</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">int&nbsp;send(&nbsp;SOCKET&nbsp;s,&nbsp;const&nbsp;char&nbsp;FAR&nbsp;*buf,&nbsp;int&nbsp;len,&nbsp;int&nbsp;flags&nbsp;);&nbsp;&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">不论是客户端还是服务器端应用程序都用send函数来向TCP连接的另一端发送数据。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">客户端程序一般用send函数向服务器发送请求，而服务器则通常用send函数来向客户程序发送应答。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">该函数的：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第一个参数指定发送端套接字描述符；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第二个参数指明一个存放应用程序要发送数据的缓冲区；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第三个参数指明实际要发送的数据的字节数；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第四个参数一般置0。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">这里只描述同步Socket的send函数的执行流程。当调用该函数时，send先比较待发送数据的长度len和套接字s的发送缓冲的长度，如果len大于s的发送缓冲区的长度，该函数返回SOCKET_ERROR；如果len小于或者等于s的发送缓冲区的长度，那么send先检查协议&nbsp;是否正在发送s的发送缓冲中的数据，如果是就等待协议把数据发送完，如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据，那么&nbsp;send就比较s的发送缓冲区的剩余空间和len，如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完，如果len小于剩余&nbsp;空间大小send就仅仅把buf中的数据copy到剩余空间里（<span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">注意并不是send</span>把s的发送缓冲中的数据传到连接的另一端的，而是协议传的，send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里<span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">）。</span>如果send函数copy数据成功，就返回实际copy的字节数，如果send在copy数据时出现错误，那么send就返回SOCKET_ERROR；如果send在等待协议传送数据时网络断开的话，那么send函数也返回SOCKET_ERROR。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"><span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">要注意send</span>函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了，但是此时这些数据并不一定马上被传到连接的另一端<span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">。</span>如果协议在后续的传送过程中出现网络错误的话，那么下一个Socket函数就会返回SOCKET_ERROR。（每一个除send外的Socket函数在执&nbsp;行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续，如果在等待时出现网络错误，那么该Socket函数就返回&nbsp;SOCKET_ERROR）</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">注意：在Unix系统下，如果send在等待协议传送数据时网络断开的话，调用send的进程会接收到一个SIGPIPE信号，进程对该信号的默认处理是进程终止。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">Send函数的返回值有三类：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（1）返回值=0：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（2）返回值&lt;0：发送失败，错误原因存于全局变量errno中</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（3）返回值&gt;0：表示发送的字节数（实际上是拷贝到发送缓冲中的字节数）</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"></p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">错误代码：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EBADF&nbsp;参数s&nbsp;非合法的socket处理代码。<br style="box-sizing: border-box;" />EFAULT&nbsp;参数中有一指针指向无法存取的内存空间<br style="box-sizing: border-box;" />ENOTSOCK&nbsp;参数s为一文件描述词，非socket。<br style="box-sizing: border-box;" />EINTR&nbsp;被信号所中断。<br style="box-sizing: border-box;" />EAGAIN&nbsp;此操作会令进程阻断，但参数s的socket为不可阻断。<br style="box-sizing: border-box;" />ENOBUFS&nbsp;系统的缓冲内存不足<br style="box-sizing: border-box;" />ENOMEM&nbsp;核心内存不足<br style="box-sizing: border-box;" />EINVAL&nbsp;传给系统调用的参数不正确。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"></p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">2.&nbsp;&nbsp;recv函数</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">int&nbsp;recv(&nbsp;SOCKET&nbsp;s,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;FAR&nbsp;*buf,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;len,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;flags&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);&nbsp;&nbsp;&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">不论是客户端还是服务器端应用程序都用recv函数从TCP连接的另一端接收数据。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">该函数的：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第一个参数指定接收端套接字描述符；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第二个参数指明一个缓冲区，该缓冲区用来存放recv函数接收到的数据；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第三个参数指明buf的长度；</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">第四个参数一般置0。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时，recv先等待s的发送缓冲&nbsp;中的数据被协议传送完毕，如果协议在传送s的发送缓冲中的数据时出现网络错误，那么recv函数返回SOCKET_ERROR，如果s的发送缓冲中没有数&nbsp;据或者数据被协议成功发送完毕后，recv先检查套接字s的接收缓冲区，如果s接收缓冲区中没有数据或者协议正在接收数据，那么recv就一直等待，只到&nbsp;协议把数据接收完毕。当协议把数据接收完毕，recv函数就把s的接收缓冲中的数据copy到buf中（<span style="box-sizing: border-box; margin: 0px; padding: 0px; color: #000000;">注意协议接收到的数据可能大于buf</span>的长度，所以&nbsp;在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据，真正的接收数据是协议来完成的），recv函数返回其实际copy的字节数。如果recv在copy时出错，那么它返回SOCKET_ERROR；如果recv函数在等待协议接收数据时网络中断了，那么它返回0。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">注意：在Unix系统下，如果recv函数在等待协议接收数据时网络断开了，那么调用recv的进程会接收到一个SIGPIPE信号，进程对该信号的默认处理是进程终止。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"></p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">默认情况下socket是阻塞的。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">阻塞与非阻塞recv返回值没有区别，都是：</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&lt;0&nbsp;出错</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">=0&nbsp;对方调用了close&nbsp;API来关闭连接</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">&gt;0&nbsp;接收到的数据大小，</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"></p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">特别地：返回值&lt;0时并且(errno&nbsp;==&nbsp;EINTR&nbsp;||&nbsp;errno&nbsp;==&nbsp;EWOULDBLOCK&nbsp;||&nbsp;errno&nbsp;==&nbsp;EAGAIN)的情况下认为连接是正常的，继续接收。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">只是阻塞模式下recv会一直阻塞直到接收到数据，非阻塞模式下如果没有数据就会返回，不会阻塞着读，因此需要循环读取）。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;"></p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">返回说明：&nbsp;&nbsp;&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（1）成功执行时，返回接收到的字节数。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（2）若另一端已关闭连接则返回0，这种关闭是对方主动且正常的关闭</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">（3）失败返回-1，errno被设为以下的某个值&nbsp;&nbsp;&nbsp;</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EAGAIN：套接字已标记为非阻塞，而接收操作被阻塞或者接收超时</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EBADF：sock不是有效的描述词</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">ECONNREFUSE：远程主机阻绝网络连接</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EFAULT：内存空间访问出错</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EINTR：操作被信号中断</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">EINVAL：参数无效</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">ENOMEM：内存不足</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">ENOTCONN：与面向连接关联的套接字尚未被连接上</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; word-wrap: break-word; word-break: normal; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">ENOTSOCK：sock索引的不是套接字</p><img src ="http://www.cppblog.com/API/aggbug/215420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2017-12-12 10:32 <a href="http://www.cppblog.com/API/archive/2017/12/12/215420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于linux信号总结</title><link>http://www.cppblog.com/API/archive/2017/09/27/215270.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Wed, 27 Sep 2017 09:51:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2017/09/27/215270.html</guid><wfw:comment>http://www.cppblog.com/API/comments/215270.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2017/09/27/215270.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/215270.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/215270.html</trackback:ping><description><![CDATA[一.信号列表<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
-->$&nbsp;kill&nbsp;-l<br />
1)&nbsp;SIGHUP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2)&nbsp;SIGINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3)&nbsp;SIGQUIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4)&nbsp;SIGILL<br />
&nbsp;5)&nbsp;SIGTRAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6)&nbsp;SIGABRT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7)&nbsp;SIGBUS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8)&nbsp;SIGFPE<br />
&nbsp;9)&nbsp;SIGKILL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;10)&nbsp;SIGUSR1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;11)&nbsp;SIGSEGV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;12)&nbsp;SIGUSR2<br />
13)&nbsp;SIGPIPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;14)&nbsp;SIGALRM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;15)&nbsp;SIGTERM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;17)&nbsp;SIGCHLD<br />
18)&nbsp;SIGCONT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;19)&nbsp;SIGSTOP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;20)&nbsp;SIGTSTP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;21)&nbsp;SIGTTIN<br />
22)&nbsp;SIGTTOU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;23)&nbsp;SIGURG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;24)&nbsp;SIGXCPU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;25)&nbsp;SIGXFSZ<br />
26)&nbsp;SIGVTALRM&nbsp;&nbsp;&nbsp;27)&nbsp;SIGPROF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;28)&nbsp;SIGWINCH&nbsp;&nbsp;&nbsp;&nbsp;29)&nbsp;SIGIO<br />
30)&nbsp;SIGPWR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;31)&nbsp;SIGSYS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;34)&nbsp;SIGRTMIN&nbsp;&nbsp;&nbsp;&nbsp;35)&nbsp;SIGRTMIN+1<br />
36)&nbsp;SIGRTMIN+2&nbsp;&nbsp;37)&nbsp;SIGRTMIN+3&nbsp;&nbsp;38)&nbsp;SIGRTMIN+4&nbsp;&nbsp;39)&nbsp;SIGRTMIN+5<br />
40)&nbsp;SIGRTMIN+6&nbsp;&nbsp;41)&nbsp;SIGRTMIN+7&nbsp;&nbsp;42)&nbsp;SIGRTMIN+8&nbsp;&nbsp;43)&nbsp;SIGRTMIN+9<br />
44)&nbsp;SIGRTMIN+10&nbsp;45)&nbsp;SIGRTMIN+11&nbsp;46)&nbsp;SIGRTMIN+12&nbsp;47)&nbsp;SIGRTMIN+13<br />
48)&nbsp;SIGRTMIN+14&nbsp;49)&nbsp;SIGRTMIN+15&nbsp;50)&nbsp;SIGRTMAX-14&nbsp;51)&nbsp;SIGRTMAX-13<br />
52)&nbsp;SIGRTMAX-12&nbsp;53)&nbsp;SIGRTMAX-11&nbsp;54)&nbsp;SIGRTMAX-10&nbsp;55)&nbsp;SIGRTMAX-9<br />
56)&nbsp;SIGRTMAX-8&nbsp;&nbsp;57)&nbsp;SIGRTMAX-7&nbsp;&nbsp;58)&nbsp;SIGRTMAX-6&nbsp;&nbsp;59)&nbsp;SIGRTMAX-5<br />
60)&nbsp;SIGRTMAX-4&nbsp;&nbsp;61)&nbsp;SIGRTMAX-3&nbsp;&nbsp;62)&nbsp;SIGRTMAX-2&nbsp;&nbsp;63)&nbsp;SIGRTMAX-1<br />
64)&nbsp;SIGRTMAX</div>
<br />(1)<span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">1 ~ 31的信号为传统UNIX支持的信号，是不可靠信号(非实时的)<br /></span>(2)<span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">32 ~ 63的信号是后来扩充的，称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队，可能会造成信号丢失，而后者不会。</span><br /><br />二.具体每个信号的产生原因<br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">1) SIGHUP:当用户退出shell时，由该shell启动的所有进程将收到这个信号，默认动作为终止进程<br /></span><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">登录Linux时，系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序，包括前台进程组和后台进程组，一般都属于这个Session。当用户退出Linux登录时，前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程，因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号，比如wget能捕获SIGHUP信号，并忽略它，这样就算退出了Linux登录，wget也能继续下载。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">此外，对于与终端脱离关系的守护进程，这个信号用于通知它重新读取配置文件。</p><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />2）SIGINT：当用户按下了&lt;Ctrl+C&gt;组合键时，用户终端向正在运行中的由该终端启动的程序发出此信号。默认动<br />作为终止里程。<br /></span><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br /></span><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">3）SIGQUIT：当用户按下&lt;ctrl+\&gt;组合键时产生该信号，用户终端向正在运行中的由该终端启动的程序发出些信<br />号。默认动作为终止进程。</span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">4）SIGILL：CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">5）SIGTRAP：该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">由断点指令或其它trap指令产生. 由debugger使用。<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">6 ) SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。<br /><br />7）SIGBUS：非法访问内存地址，包括内存对齐出错，默认动作为终止进程并产生core文件。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">8）SIGFPE：在发生致命的运算错误时发出。不仅包括浮点运算错误，还包括溢出及除数为0等所有的算法错误。默<br />认动作为终止进程并产生core文件。<br /><br /><br />9）SIGKILL：无条件终止进程。本信号不能被忽略，处理和阻塞。默认动作为终止进程。它向系统管理员提供了可<br />以杀死任何进程的方法。<br /><br />10）SIGUSE1：用户定义 的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。<br /><br />11）SIGSEGV：指示进程进行了无效内存访问。默认动作为终止进程并产生core文件。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">12）SIGUSR2：这是另外一个用户自定义信号 ，程序员可以在程序中定义 并使用该信号。默认动作为终止进程。<br /><br />13）SIGPIPE：Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。<br /><br />14) SIGALRM:定时器超时，超时的时间 由系统调用alarm设置。默认动作为终止进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">15）SIGTERM：程序结束信号，与SIGKILL不同的是，该信号可以被阻塞和终止。通常用来要示程序正常退出。执行<br />shell命令Kill时，缺省产生这个信号。默认动作为终止进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出，shell命令kill缺省产生这个信号。如果进程终止不了，我们才会尝试SIGKILL。<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">17）SIGCHLD：子进程结束时，父进程会收到这个信号。默认动作为忽略这个信号。<br /></span><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">子进程结束时, 父进程会收到这个信号。</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">如果父进程没有处理这个信号，也没有等待(wait)子进程，子进程虽然终止，但是还会在内核进程表中占有表项，这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号，或者捕捉它，或者wait它派生的子进程，或者父进程先终止，这时子进程的终止自动由init进程来接管)。</p><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />18）SIGCONT：停止进程的执行。信号不能被忽略，处理和阻塞。默认动作为终止进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符<br /></span><br />19)<span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">SIGSTOP</span><br style="box-sizing: border-box; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;" /><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.<br /></span><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />20）SIGTSTP：停止进程的运行。按下&lt;ctrl+z&gt;组合键时发出这个信号。默认动作为暂停进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号<br /><br /></span><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">21）SIGTTIN：后台进程读终端控制台。默认动作为暂停进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">22）SIGTTOU:该信号类似于SIGTTIN，在后台进程要向终端输出数据时发生。默认动作为暂停进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />23）SIGURG：套接字上有紧急数据时，向当前正在运行的进程发出些信号，报告有紧急数据到达。如网络带外数据<br />到达，默认动作为忽略该信号。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">有"紧急"数据或out-of-band数据到达socket时产生.</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />24）SIGXCPU：进程执行时间超过了分配给该进程的CPU时间 ，系统产生该信号并发送给该进程。默认动作为终止<br />进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />25）SIGXFSZ：超过文件的最大长度设置。默认动作为终止进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">当进程企图扩大文件以至于超过文件大小资源限制。</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />26）SIGVTALRM：虚拟时钟超时时产生该信号。类似于SIGALRM，但是该信号只计算该进程占用CPU的使用时间。默<br />认动作为终止进程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />27）SGIPROF：类似于SIGVTALRM，它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进<br />程。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">包括该进程用的CPU时间以及系统调用的时间</span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;"><br />28）SIGWINCH：窗口变化大小时发出。默认动作为忽略该信号。<br /><br />29）SIGIO：此信号向进程指示发出了一个异步IO事件。默认动作为忽略。<br /></span><span style="color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">文件描述符准备就绪, 可以开始进行输入/输出操作<br /></span><br /><span style="color: #555555; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">30）SIGPWR：关机。默认动作为终止进程。<br /><br />31）SIGSYS：无效的系统调用。默认动作为终止进程并产生core文件。<br /><br />32）SIGRTMIN～（64）SIGRTMAX：LINUX的实时信号，它们没有固定的含义（可以由用户自定义）。<br /></span><span style="color: red; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">所有的实时信</span><span style="color: red; font-family: &quot;microsoft yahei&quot;; font-size: 15px; background-color: #ffffff;">号的默认动作都为终止进程。</span>&nbsp;<br /><br /><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">在以上列出的信号中，程序不可捕获、阻塞或忽略的信号有：SIGKILL,SIGSTOP<br style="box-sizing: border-box;" />不能恢复至默认动作的信号有：SIGILL,SIGTRAP<br style="box-sizing: border-box;" />默认会导致进程流产的信号有：SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ<br style="box-sizing: border-box;" />默认会导致进程退出的信号有：SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM<br style="box-sizing: border-box;" />默认会导致进程停止的信号有：SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU<br style="box-sizing: border-box;" />默认进程忽略的信号有：SIGCHLD,SIGPWR,SIGURG,SIGWINCH</p><p style="box-sizing: border-box; margin: 0px; padding: 0px; color: #454545; font-family: &quot;PingFang SC&quot;, &quot;Microsoft YaHei&quot;, SimHei, Arial, SimSun; font-size: 16px; background-color: #ffffff;">此外，SIGIO在SVR4是退出，在4.3BSD中是忽略；SIGCONT在进程挂起时是继续，否则是忽略，不能被阻塞。&nbsp;</p><img src ="http://www.cppblog.com/API/aggbug/215270.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2017-09-27 17:51 <a href="http://www.cppblog.com/API/archive/2017/09/27/215270.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent windows编译</title><link>http://www.cppblog.com/API/archive/2015/02/15/209824.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sun, 15 Feb 2015 05:14:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2015/02/15/209824.html</guid><wfw:comment>http://www.cppblog.com/API/comments/209824.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2015/02/15/209824.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/209824.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/209824.html</trackback:ping><description><![CDATA[<div>1. 下载libevent库<br />2.本人用vs2013编译，所有需要修改<br />
<p style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(0,0,0); padding-bottom: 0px; padding-top: 0px; font: 14px/20px Verdana, Arial, Helvetica, sans-serif; padding-left: 0px; margin: 10px auto; letter-spacing: normal; padding-right: 0px; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px"><span style="padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; padding-right: 0px">在以下</span>3<span style="padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; padding-right: 0px">个文件开头修改&#8220;</span>#define _WIN32_WINNT 0x0603<span style="padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; padding-right: 0px">&#8221;</span></p>
<p style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(0,0,0); padding-bottom: 0px; padding-top: 0px; font: 14px/20px Verdana, Arial, Helvetica, sans-serif; padding-left: 0px; margin: 10px auto; letter-spacing: normal; padding-right: 0px; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">libevent-2.0.22-stable\event_iocp.c</p>
<p style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(0,0,0); padding-bottom: 0px; padding-top: 0px; font: 14px/20px Verdana, Arial, Helvetica, sans-serif; padding-left: 0px; margin: 10px auto; letter-spacing: normal; padding-right: 0px; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">libevent-2.0.22-stable\evthread_win32.c</p>
<p style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(0,0,0); padding-bottom: 0px; padding-top: 0px; font: 14px/20px Verdana, Arial, Helvetica, sans-serif; padding-left: 0px; margin: 10px auto; letter-spacing: normal; padding-right: 0px; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">libevent-2.0.22-stable\listener.c<br /><br />3.设置nmake的环境<br />VC6&nbsp; prefix\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT<br />VC8&nbsp; prefix\Microsoft Visual Studio 8\VC\bin\vcvars32.bat<br />VC9&nbsp; prefix\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat<br />我这里是vs2013，执行D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat<br />注意：这些批处理文件只会在当前进程中设置（局部的）环境变量，也就是说：<br />1. 用cmd（或者command）打开的命令行窗口中， 运行某个vcvar32.bat一次。<br />那么当前命令行窗口中就可以正常使用cl，直到关闭。<br /><br />4.<span style="white-space: normal; text-transform: none; word-spacing: 0px; float: none; color: rgb(51,51,51); font: 13px/19px verdana, sans-serif; display: inline !important; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">使用VC的nmake -f Makefile.nmake即可编译32位release模式。</span><br style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(51,51,51); font: 13px/19px verdana, sans-serif; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px" /><span style="white-space: normal; text-transform: none; word-spacing: 0px; float: none; color: rgb(51,51,51); font: 13px/19px verdana, sans-serif; display: inline !important; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">如果要求编译64位的版本，需要在Makefile.nmake中添加一个LIBFLAGS选项 /MACHINE:X64</span><br style="white-space: normal; text-transform: none; word-spacing: 0px; color: rgb(51,51,51); font: 13px/19px verdana, sans-serif; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px" /><span style="white-space: normal; text-transform: none; word-spacing: 0px; float: none; color: rgb(51,51,51); font: 13px/19px verdana, sans-serif; display: inline !important; letter-spacing: normal; background-color: rgb(255,255,255); text-indent: 0px; -webkit-text-stroke-width: 0px">如果要加调试信息，可以在 CFLAGS中加入/Zi，32位加调试选项是 CFLAGS中加/ZI，当然要调整优化选项/Ox</span><br /></p></div><img src ="http://www.cppblog.com/API/aggbug/209824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2015-02-15 13:14 <a href="http://www.cppblog.com/API/archive/2015/02/15/209824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LVS十种调度算法介绍 </title><link>http://www.cppblog.com/API/archive/2013/12/03/204561.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Tue, 03 Dec 2013 01:53:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/12/03/204561.html</guid><wfw:comment>http://www.cppblog.com/API/comments/204561.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/12/03/204561.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/204561.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/204561.html</trackback:ping><description><![CDATA[<em>
<p style="line-height: 1.7; margin: 5px 0px; white-space: normal; font-size: 14px">1.轮叫调度（Round Robin）(简称rr)<br />调度器通过&#8220;轮叫&#8221;调度算法将外部请求按顺序轮流分配到集群中的真实<span>服务器</span>上，它均等地对待每一台<span>服务</span>器，而不管服务器上实际的连接数和<span>系统</span>负载。<br />2.加权轮叫（Weighted Round Robin）（简称wrr)<br />调度器通过&#8220;加权轮叫&#8221;调度算法根据真实服务器的不同处理能力来调度<span>访问</span>请求。这样可以保证处理能力强的服务器能处理更多的访问<span>流量</span>。调度器可以<span>自动</span>问询真实服务器的负载情况，并动态地调整其权值。<br />3.最少链接（Least Connections）(LC)<br />调度器通过&#8220;最少连接&#8221;调度算法动态地将网络请求调度到已建立的链接数最少的服务器上。如果集群系统的真实服务器具有相近的系统性能，采用&#8220;最小连接&#8221;调度算法可以较好地均衡负载。<br />4.加权最少链接（Weighted Least Connections）(WLC)<br />在集群系统中的服务器性能差异较大的情况下，调度器采用&#8220;加权最少链接&#8221;调度算法<span>优化</span>负载均衡性能，具有较高权值的服务器将承受较大比例的活动连接负载。调度器可以自动问询真实服务器的负载情况，并动态地调整其权值。<br />5.基于局部性的最少链接（Locality-Ba<span>sed</span><span class="Apple-converted-space"> </span>Least Connections）(LBLC)<br />&#8220;基于局部性的最少链接&#8221;调度算法是针对目标IP地址的负载均衡，目前主要用于Cache集群系统。该算法根据请求的目标IP地址找出该目标IP地址最近使用的服务器，若该服务器是可用的且没有超载，将请求发送到该服务器；若服务器不存在，或者该服务器超载且有服务器处于一半的工作负载，则用&#8220;最少链接&#8221; 的原则选出一个可用的服务器，将请求发送到该服务器。<br />6.带复制的基于局部性最少链接（Locality-Based Least Connections with Replication）(LBLCR)<br />&#8220;带复制的基于局部性最少链接&#8221;调度算法也是针对目标IP地址的负载均衡，目前主要用于Cache集群系统。它与LBLC算法的不同之处是它要维护从一个目标 IP地址到一组服务器的映射，而LBLC算法维护从一个目标IP地址到一台服务器的映射。该算法根据请求的目标IP地址找出该目标IP地址对应的服务器组，按&#8220;最小连接&#8221;原则从服务器组中选出一台服务器，若服务器没有超载，将请求发送到该服务器；若服务器超载，则按&#8220;最小连接&#8221;原则从这个集群中选出一台服务器，将该服务器加入到服务器组中，将请求发送到该服务器。同时，当该服务器组有一段时间没有被修改，将最忙的服务器从服务器组中删除，以降低复制的程度。<br />7.目标地址散列（Destination Hashing）(DH)<br />&#8220;目标地址散列&#8221;调度算法根据请求的目标IP地址，作为散列键（Hash Key）从静态分配的散列表找出对应的服务器，若该服务器是可用的且未超载，将请求发送到该服务器，否则返回空。<br />8.源地址散列（Source Hashing）(SH)<br />&#8220;源地址散列&#8221;调度算法根据请求的源IP地址，作为散列键（Hash Key）从静态分配的散列表找出对应的服务器，若该服务器是可用的且未超载，将请求发送到该服务器，否则返回空。</p>
<p style="line-height: 1.7; margin: 5px 0px; white-space: normal; font-size: 14px">9. 最短的期望的延迟（Shortest Expected Delay Scheduling SED）(SED)<br />基于wlc算法。这个必须举例来说了<br />ABC三台机器分别权重123 ，连接数也分别是123。那么如果使用WLC算法的话一个新请求进入时它可能会分给ABC中的任意一个。使用sed算法后会进行这样一个运算<br />A(1+1)/1<br />B(1+2)/2<br />C(1+3)/3<br />根据运算结果，把连接交给C 。<br />10.最少队列调度（Never Queue Scheduling NQ）(NQ)<br />无需队列。如果有台 realserver的连接数＝0就直接分配过去，不需要在进行sed运算</p></em><img src ="http://www.cppblog.com/API/aggbug/204561.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-12-03 09:53 <a href="http://www.cppblog.com/API/archive/2013/12/03/204561.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个基于Event Poll(epoll)的TCP Server Framework,浅析epoll</title><link>http://www.cppblog.com/API/archive/2013/12/01/204535.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sun, 01 Dec 2013 13:37:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/12/01/204535.html</guid><wfw:comment>http://www.cppblog.com/API/comments/204535.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/12/01/204535.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/204535.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/204535.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: epoll,event poll,on linux kernel 2.6.x.pthread,nptl-2.12&nbsp;&nbsp;&nbsp;LT/ET:ET也会多次发送event,当然频率远低于LT,但是epoll one shot才是真正的对"one connection&nbsp;VS one thread in worker thread pool,不依赖于任何connection-...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/12/01/204535.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/204535.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-12-01 21:37 <a href="http://www.cppblog.com/API/archive/2013/12/01/204535.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高并发的epoll+线程池，业务在线程池内</title><link>http://www.cppblog.com/API/archive/2013/11/26/204456.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Tue, 26 Nov 2013 07:49:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/11/26/204456.html</guid><wfw:comment>http://www.cppblog.com/API/comments/204456.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/11/26/204456.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/204456.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/204456.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: epoll是linux下高并发服务器的完美方案，因为是基于事件触发的，所以比select快的不只是一个数量级。单线程epoll，触发量可达到15000，但是加上业务后，因为大多数业务都与数据库打交道，所以就会存在阻塞的情况，这个时候就必须用多线程来提速。业务在线程池内，这里要加锁才行。测试结果2300个/s测试工具：stressmark因为加了适用与ab的代码，所以也可...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/11/26/204456.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/204456.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-11-26 15:49 <a href="http://www.cppblog.com/API/archive/2013/11/26/204456.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详谈 UNIX 环境进程异常退出</title><link>http://www.cppblog.com/API/archive/2013/09/30/203505.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 30 Sep 2013 08:50:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/09/30/203505.html</guid><wfw:comment>http://www.cppblog.com/API/comments/203505.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/09/30/203505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/203505.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/203505.html</trackback:ping><description><![CDATA[<strong style="font-family: arial, nsimsun, sans-serif; font-size: 12px; line-height: normal; background-color: #ffffff;">简介：</strong><span style="font-family: arial, nsimsun, sans-serif; font-size: 12px; line-height: normal; background-color: #ffffff;">&nbsp;本文详细论述 UNIX 环境上的进程异常退出，将导致进程异常退出的各种情景归纳为两类，对每类情况详细分析了问题出现的根本原因，同时添加了相应的实例以易于您更好地进行了解。在此基础上，文章最后论述了应该如何避免和调试进程异常退出问题。希望读者阅读此文后，对进程异常退出问题有更深层的认识，有更系统的梳理，对调试此类进程崩溃问题时也能有所帮助，写出更稳定、更可靠的软件。<br /><br /><br /></span><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="major1"><span style="font-size: 1.5em; font-weight: bold;">进程异常退出</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">进程退出意味着进程生命期的结束，系统资源被回收，进程从操作系统环境中销毁。进程异常退出是进程在运行过程中被意外终止，从而导致进程本来应该继续执行的任务无法完成。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">进程异常退出可能给软件用户造成如下负面影响：</p><ul style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">软件丧失部分或者全部功能性，无法完成既定任务。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">如果进程正在处理数据，可能造成数据损坏。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">如果是关键软件服务，必然导致服务异常中止 , 造成无法预计的损失。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">进程异常退出或者进程崩溃 , 也会给软件用户造成恐慌和困惑。</li></ul><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">进程异常退出是生产环境中经常遇到的问题，它会给软件用户造成很多负面影响，所以软件开发者应当避免这种问题的出现。但是导致进程异常退出的场景和原因是多种多样的，甚至令人琢磨不透。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">本文将所有可能造成进程异常退出的原因归结为两类。系统地将其分类，使读者对此类问题能有清晰的认识。对每类情况详细论述，分析根本原因，然后分析了这两类情况之间的联系，也就是信号与进程异常退出的紧密关系。希望您读完此文后，能对此类问题有更加全面、深入的理解，对调试此类问题也能有所帮助，写出更加可靠、更加稳定性、更加健壮的软件。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">首先我们来看导致进程异常退出的这两类情况：</p><ul style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">第一类：向进程发送信号导致进程异常退出；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">第二类：代码错误导致进程运行时异常退出。</li></ul><div style="clear: both; background-image: url(http://1.www.s81c.com/i/solid.gif); background-color: #ffffff; height: 1px; font-family: Simsun; font-size: medium; line-height: normal; background-repeat: repeat no-repeat;"></div><p ibm-back-to-top"="" style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 5px; font-size: 0.76em; clear: both; text-align: right; height: 15px; line-height: normal; background-color: #ffffff;"><a href="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/#ibm-pcon" style="color: #996699; display: inline; margin: 0px; padding: 0px 0px 0px 18px; text-decoration: none; background-image: url(http://1.www.s81c.com/i/v16/icons/u_bold.gif); font-weight: bold; background-position: 0px -1px; background-repeat: no-repeat no-repeat;">回页首</a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="major2"><span style="font-size: 1.5em; font-weight: bold;">第一类：向进程发送信号导致进程异常退出</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><strong>信号</strong>：</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">UNIX 系统中的信号是系统响应某些状况而产生的事件，是进程间通信的一种方式。信号可以由一个进程发送给另外进程，也可以由核发送给进程。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><strong>信号处理程序：</strong></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">信号处理程序是进程在接收到信号后，系统对信号的响应。根据具体信号的涵义，相应的默认信号处理程序会采取不同的信号处理方式：</p><ul style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">终止进程运行，并且产生 core dump 文件。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">终止进程运行。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">忽略信号，进程继续执行。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">暂停进程运行。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 3px;">如果进程已被暂停，重新调度进程继续执行。</li></ul><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">前两种方式会导致进程异常退出，是本文讨论的范围。实际上，大多数默认信号处理程序都会终止进程的运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在进程接收到信号后，如果进程已经绑定自定义的信号处理程序，进程会在用户态执行自定义的信号处理程序；反之，内核会执行默认信号程序终止进程运行，导致进程异常退出。</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="fig1" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">图 1. 默认信号处理程序终止进程运行</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><img alt="图 1. 默认信号处理程序终止进程运行" height="325" src="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/image002.jpg" width="554" style="border: 0px; padding: 0.3em 5px 0.7em; font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" />&nbsp;<br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">所以，通过向进程发送信号可以触发默认信号处理程序，默认信号处理程序终止进程运行。在 UNIX 环境中我们有三种方式将信号发送给目标进程，导致进程异常退出。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor2.1"><span style="font-size: 1.2em; font-weight: bold;">方式一：调用函数 kill() 发送信号</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">我们可以调用函数 kill(pid_t pid, int sig) 向进程 ID 为 pid 的进程发送信号 sig。这个函数的原型是：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> #include &lt;sys/types.h&gt;   #include &lt;signal.h&gt;   int kill(pid_t pid, int sig);  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">调用函数 kill() 后，进程进入内核态向目标进程发送指定信号；目标进程在接收到信号后，默认信号处理程序被调用，进程异常退出。</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="listing1" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">清单 1. 调用 kill() 函数发送信号</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;">				  /* sendSignal.c, send the signal &#8216; SIGSEGV &#8217; to specific process*/        1 #include &lt;sys/types.h&gt;        2 #include &lt;signal.h&gt;        3        4 int main(int argc, char* argv[])        5 {        6     char* pid = argv[1];        7     int PID = atoi(pid);        8        9     kill(PID, SIGSEGV);       10     return 0;       11 }  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">上面的代码片段演示了如何调用 kill() 函数向指定进程发送 SIGSEGV 信号。编译并且运行程序：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# gcc -o sendSignal sendSignal.c   [root@machine ~]# top &amp;   [1] 22055   [root@machine ~]# ./sendSignal 22055   [1]+  Stopped                 top   [root@machine ~]# fg %1   top   Segmentation fault (core dumped)  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">上面的操作中，我们在后台运行 top，进程 ID 是 22055，然后运行 sendSignal 向它发送 SIGSEGV 信号，导致 top 进程异常退出，产生 core dump 文件。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor2.2"><span style="font-size: 1.2em; font-weight: bold;">方式二：运行 kill 命令发送信号</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">用户可以在命令模式下运行 kill 命令向目标进程发送信号，格式为：</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">kill SIG*** PID</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在运行 kill 命令发送信号后，目标进程会异常退出。这也是系统管理员终结某个进程的最常用方法，类似于在 Windows 平台通过任务管理器杀死某个进程。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在实现上，kill 命令也是调用 kill 系统调用函数来发送信号。所以本质上，方式一和方式二是一样的。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">操作演示如下：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# top &amp;   [1] 22810   [root@machine ~]# kill -SIGSEGV 22810   [1]+  Stopped                 top   [root@machine ~]# fg %1   top   Segmentation fault (core dumped)  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor2.3"><span style="font-size: 1.2em; font-weight: bold;">方式三：在终端使用键盘发送信号</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">用户还可以在终端用键盘输入特定的字符（比如 control-C 或 control-\）向前台进程发送信号，终止前台进程运行。常见的中断字符组合是，使用 control-C 发送 SIGINT 信号，使用 control-\ 发送 SIGQUIT 信号，使用 control-z 发送 SIGTSTP 信号。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在实现上，当用户输入中断字符组合时，比如 control-C，终端驱动程序响应键盘输入，并且识别 control-C 是信号 SIGINT 的产生符号，然后向前台进程发送 SIGINT 信号。当前台进程再次被调用时就会接收到 SIGINT 信号。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">使用键盘中断组合符号发送信号演示如下：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# ./loop.sh  ( 注释：运行一个前台进程，任务是每秒钟打印一次字符串 )   i'm looping ...   i'm looping ...   i'm looping ...                 ( 注释：此时，用户输入 control-C)   [root@machine ~]#               ( 注释：接收到信号后，进程退出 )  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor2.4"><span style="font-size: 1.2em; font-weight: bold;">对这类情况的思考</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">这类情况导致的进程异常退出，并不是软件编程错误所导致，而是进程外部的异步信号所致。但是我们可以在代码编写中做的更好，通过调用 signal 函数绑定信号处理程序来应对信号的到来，以提高软件的健壮性。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">signal 函数的原型：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> #include &lt;signal.h&gt;   void (*signal(int sig, void (*func)(int)))(int);  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">signal 函数将信号 sig 和自定义信号处理程序绑定，即当进程收到信号 sig 时自定义函数 func 被调用。如果我们希望软件在运行时屏蔽某个信号，插入下面的代码，以达到屏蔽信号 SIGINT 的效果：</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">(void)signal(SIGINT, SIG_IGN)；</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">执行这一行代码后，当进程收到信号 SIGINT 后，进程就不会异常退出，而是会忽视这个信号继续运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">更重要的场景是，进程在运行过程中可能会创建一些临时文件，我们希望进程在清理这些文件后再退出，避免遗留垃圾文件，这种情况下我们也可以调用 signal 函数实现，自定义一个信号处理程序来清理临时文件，当外部发送信号要求进程终止运行时，这个自定义信号处理程序被调用做清理工作。代码清单 2 是具体实现。</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="listing2" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">清单 2. 调用 signal 函数绑定自定义信号处理程序</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;">				       /*  bindSignal.c  */        1 #include &lt;signal.h&gt;        2 #include &lt;stdio.h&gt;        3 #include &lt;unistd.h&gt;        4 void cleanTask(int sig) {        5     printf( "Got the signal, deleting the tmp file\n" );        6     if( access( "/tmp/temp.lock", F_OK ) != -1 ) {        7           if( remove( "/tmp/temp.lock" ) != 0 )        8               perror( "Error deleting file" );        9           else       10               printf( "File successfully deleted\n" );       11     }       12       13     printf( "Process existing...\n" );       14     exit(0);       15 }       16       17 int main() {       18     (void) signal( SIGINT, cleanTask );       19     FILE* tmp = fopen ( "/tmp/temp.lock", "w" );       20     while(1) {       21         printf( "Process running happily\n" );       22         sleep(1);       23     }       24       25     if( tmp )       26         remove( "/tmp/temp.lock" );       27 }  运行程序：  [root@machine ~]# ./bindSignal   Process running happily   Process running happily   Process running happily                       ( 注释：此时，用户输入 control-C)   Got the signal, deleting the tmp file      ( 注释：接收到信号后，cleanTask 被调用 )   File successfully deleted                    ( 注释：cleanTask 删除临时文件 )   Process existing...                           ( 注释：进程退出 )  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><div style="clear: both; background-image: url(http://1.www.s81c.com/i/solid.gif); background-color: #ffffff; height: 1px; font-family: Simsun; font-size: medium; line-height: normal; background-repeat: repeat no-repeat;"></div><p ibm-back-to-top"="" style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 5px; font-size: 0.76em; clear: both; text-align: right; height: 15px; line-height: normal; background-color: #ffffff;"><a href="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/#ibm-pcon" style="color: #996699; display: inline; margin: 0px; padding: 0px 0px 0px 18px; text-decoration: none; background-image: url(http://1.www.s81c.com/i/v16/icons/u_bold.gif); font-weight: bold; background-position: 0px -1px; background-repeat: no-repeat no-repeat;">回页首</a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="major3"><span style="font-size: 1.5em; font-weight: bold;">第二类：编程错误导致进程运行时异常退出</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">相比于第一类情况，第二类情况在软件开发过程中是常客，是编程错误，进程运行过程中非法操作引起的。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">操作系统和计算机硬件为应用程序的运行提供了硬件平台和软件支持，为应用程序提供了平台虚拟化，使进程运行在自己的进程空间。在进程看来，它自身独占整台系统，任何其它进程都无法干预，也无法进入它的进程空间。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">但是操作系统和计算机硬件又约束每个进程的行为，使进程运行在用户态空间，控制权限，确保进程不会破坏系统资源，不会干涉进入其它进程的空间，确保进程合法访问内存。当进程尝试突破禁区做非法操作时，系统会立刻觉察，并且终止进程运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">所以，第二类情况导致的进程异常退出，起源于进程自身的编程错误，错误的编码执行非法操作，操作系统和硬件制止它的非法操作，并且让进程异常退出。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在实现上，操作系统和计算机硬件通过异常和异常处理函数来阻止进程做非法操作。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor3.1"><span style="font-size: 1.2em; font-weight: bold;">异常和异常处理函数</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">当进程执行非法操作时，计算机会抛出处理器异常，系统执行异常处理函数以响应处理器异常，异常处理函数往往会终止进程运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">广义的异常包括软中断 (soft interrupts) 和外设中断 (I/O interrupts) 。外设中断是系统外围设备发送给处理器的中断，它通知处理器 I/O 操作的状态，这种异常是外设的异步异常，与具体进程无关，所以它们不会造成进程的异常退出。本文讨论的异常是指 soft interrupts，是进程非法操作所导致的处理器异常，这类异常是进程执行非法操作所产生的同步异常，比如内存保护异常，除 0 异常，缺页异常等等。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">处理器异常有很多种，系统为每个异常分配异常号，每个异常有相对应的异常处理函数。以 x86 处理器为例，除 0 操作产生 DEE 异常 (Divide Error Exception)，异常号是 0；内存非法访问产生 GPF 异常 (General Protection Fault)，异常号是 13，而缺页 (page fault) 异常的异常号是 14。当异常出现时，处理器挂起当前进程，读取异常号，然后执行相应的异常处理函数。如果异常是可修复，比如内存缺页异常，异常处理函数会修复系统错误状态，清除异常，然后重新执行一遍被中断的指令，进程继续运行；如果异常无法修复，比如内存非法访问或者除 0 操作，异常处理函数会终止进程运行，如图 2：</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="fig2" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">图 2. 异常处理函数终止进程运行</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><img alt="图 2. 异常处理函数终止进程运行" height="296" src="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/image003.jpg" width="553" style="border: 0px; padding: 0.3em 5px 0.7em; font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" />&nbsp;<br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor3.2"><span style="font-size: 1.2em; font-weight: bold;">实例以及分析</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><strong>实例一：内存非法访问</strong></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">这类问题中最常见的就是内存非法访问。内存非法访问在 UNIX 平台即 segmentation fault，在 Windows 平台这类错误称为 Access violation。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">内存非法访问是指：进程在运行时尝试访问尚未分配（即，没有将物理内存映射进入进程虚拟内存空间）的内存，或者进程尝试向只读内存区域写入数据。当进程执行内存非法访问操作时，内存管理单元 MMU 会产生内存保护异常 GPF(General Protection Fault)，异常号是 13。系统会立刻暂停进程的非法操作，并且跳转到 GPF 的异常处理程序，终止进程运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">这种编程错误在编译阶段编译器不会报错，是运行时出现的错误。清单 3 是内存非法访问的一个简单实例，进程在执行第 5 行代码时执行非法内存访问，异常处理函数终止进程运行。</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="listing3" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">清单 3. 内存非法访问实例 demoSegfault.c</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;">				       1 #include&lt;stdio.h&gt;        2 int main()        3 {        4      char* str = "hello";        5      str[0] = 'H';        6      return 0;        7 }  编译并运行：  [root@machine ~]# gcc demoSegfault.c -o demoSegfault   [root@machine ~]# ./demoSegfault   Segmentation fault (core dumped)   [root@machine ~]# gdb demoSegfault core.24065   ( 已省略不相干文本 )   Core was generated by `./demoSegfault'.   Program terminated with signal 11, Segmentation fault.  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">分析：实例中，字符串 str 是存储在内存只读区的字符串常量，而第 5 行代码尝试更改只读区的字符，所以这是内存非法操作。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">进程从开始执行到异常退出经历如下几步：</p><ol type="1" style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">进程执行第 5 行代码，尝试修改只读内存区的字符；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">内存管理单元 MMU 检查到这是非法内存操作，产生保护内存异常 GPF，异常号 13；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">处理器立刻暂停进程运行，跳转到 GPF 的异常处理函数，异常处理函数终止进程运行；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">进程 segmentation fault，并且产生 core dump 文件。GDB 调试结果显示，进程异常退出的原因是 segmentation fault。</li></ol><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><strong>实例二：除 0 操作</strong></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">实例二是除 0 操作，软件开发中也会引入这样的错误。当进程执行除 0 操作时，处理器上的浮点单元 FPU(Floating-point unit) 会产生 DEE 除 0 异常 (Divide Error Exception)，异常号是 0。</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="listing4" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">清单 4. 除 0 操作 divide0.c</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;">				       1 #include &lt;stdio.h&gt;        2        3 int main()        4 {        5     int a = 1, b = 0, c;        6     printf( "Start running\n" );        7     c = a/b ;        8     printf( "About to quit\n" );        9 }  编译并运行：  [root@machine ~]# gcc -o divide0 divide0.c   [root@machine ~]# ./divide0 &amp;   [1] 1229   [root@machine ~]# Start running   [1]+  Floating point exception(core dumped) ./divide0   [root@xbng103 ~]# gdb divide0 /corefiles/core.1229   ( 已省略不相干文本 )   Core was generated by `./divide0'.   Program terminated with signal 8, Arithmetic exception.  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">分析：实例中，代码第 7 行会执行除 0 操作，导致异常出现，异常处理程序终止进程运行，并且输出错误提示：Floating point exception。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor3.3"><span style="font-size: 1.2em; font-weight: bold;">异常处理函数内幕</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">异常处理函数在实现上，是通过向挂起进程发送信号，进而通过信号的默认信号处理程序终止进程运行，所以异常处理函数是&#8220;间接&#8221;终止进程运行。详细过程如下：</p><ol type="1" style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">进程执行非法指令或执行错误操作；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">非法操作导致处理器异常产生；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">系统挂起进程，读取异常号并且跳转到相应的异常处理函数；<ol type="a" style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 1em;"><li style="margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">异常处理函数首先查看异常是否可以恢复。如果无法恢复异常，异常处理函数向进程发送信号。发送的信号根据异常类型而定，比如内存保护异常 GPF 相对应的信号是 SIGSEGV，而除 0 异常 DEE 相对应的信号是 SIGFPE；</li><li style="margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">异常处理函数调用内核函数 issig() 和 psig() 来接收和处理信号。内核函数 psig() 执行默认信号处理程序，终止进程运行；</li></ol></li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">进程异常退出。</li></ol><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在此基础上，我们可以把图 2 进一步细化如下：</p><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><a name="fig3" style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;"><strong style="padding: 0.3em 5px 0.7em; font-size: 0.76em; font-family: arial, sans-serif;">图 3. 异常处理函数终止进程运行（细化）</strong></a><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><img alt="图 3. 异常处理函数终止进程运行（细化）" height="259" src="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/image004.jpg" width="553" style="border: 0px; padding: 0.3em 5px 0.7em; font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" />&nbsp;<br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">异常处理函数执行时会检查异常号，然后根据异常类型发送相应的信号。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">再来看一下实例一（代码清单 3）的运行结果：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# ./demoSegfault   Segmentation fault (core dumped)   [root@machine ~]# gdb demoSegfault core.24065   ( 已省略不相干文本 )   Core was generated by `./demoSegfault'.   Program terminated with signal 11, Segmentation fault.  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">运行结果显示进程接收到信号 11 后异常退出，在 signal.h 的定义里，11 就是 SIGSEGV。MMU 产生内存保护异常 GPF（异常号 13）时，异常处理程序发送相应信号 SIGSEGV，SIGSEGV 的默认信号处理程序终止进程运行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">再来看实例二（代码清单 4）的运行结果</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# ./divide0 &amp;   [1] 1229   [root@machine ~]# Start running   [1]+  Floating point exception(core dumped) ./divide0   [root@xbng103 ~]# gdb divide0 /corefiles/core.1229   ( 已省略不相干文本 )   Core was generated by `./divide0'.   Program terminated with signal 8, Arithmetic exception.  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">分析结果显示进程接收到信号 8 后异常退出，在 signal.h 的定义里，8 就是信号 SIGFPE。除 0 操作产生异常（异常号 0），异常处理程序发送相应信号 SIGFPE 给挂起进程，SIGFPE 的默认信号处理程序终止进程运行。</p><div style="clear: both; background-image: url(http://1.www.s81c.com/i/solid.gif); background-color: #ffffff; height: 1px; font-family: Simsun; font-size: medium; line-height: normal; background-repeat: repeat no-repeat;"></div><p ibm-back-to-top"="" style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 5px; font-size: 0.76em; clear: both; text-align: right; height: 15px; line-height: normal; background-color: #ffffff;"><a href="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/#ibm-pcon" style="color: #996699; display: inline; margin: 0px; padding: 0px 0px 0px 18px; text-decoration: none; background-image: url(http://1.www.s81c.com/i/v16/icons/u_bold.gif); font-weight: bold; background-position: 0px -1px; background-repeat: no-repeat no-repeat;">回页首</a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="major4"><span style="font-size: 1.5em; font-weight: bold;">&#8220;信号&#8221;是进程异常退出的直接原因</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">信号与进程异常退出有着紧密的关系：第一类情况是因为外部环境向进程发送信号，这种情况下发送的信号是异步信号，信号的到来与进程的运行是异步的；第二类情况是进程非法操作触发处理器异常，然后异常处理函数在内核态向进程发送信号，这种情况下发送的信号是同步信号，信号的到来与进程的运行是同步的。这两种情况都有信号产生，并且最终都是信号处理程序终止进程运行。它们的区别是信号产生的信号源不同，前者是外部信号源产生异步信号，后者是进程自身作为信号源产生同步信号。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">所以，信号是进程异常退出的直接原因。当进程异常退出时，进程必然接收到了信号。</p><div style="clear: both; background-image: url(http://1.www.s81c.com/i/solid.gif); background-color: #ffffff; height: 1px; font-family: Simsun; font-size: medium; line-height: normal; background-repeat: repeat no-repeat;"></div><p ibm-back-to-top"="" style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 5px; font-size: 0.76em; clear: both; text-align: right; height: 15px; line-height: normal; background-color: #ffffff;"><a href="http://www.ibm.com/developerworks/cn/aix/library/1206_xiejd_unixexception/#ibm-pcon" style="color: #996699; display: inline; margin: 0px; padding: 0px 0px 0px 18px; text-decoration: none; background-image: url(http://1.www.s81c.com/i/v16/icons/u_bold.gif); font-weight: bold; background-position: 0px -1px; background-repeat: no-repeat no-repeat;">回页首</a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="major5"><span style="font-size: 1.5em; font-weight: bold;">避免和调试进程异常退出</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor5.1"><span style="font-size: 1.2em; font-weight: bold;">建议</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">软件开发过程中，我们应当避免进程异常退出，针对导致进程异常退出的这两类问题，对软件开发者的几点建议：</p><ol type="1" style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-right: 5px; padding-bottom: 5px; font-size: 0.76em; font-family: Simsun; line-height: normal; background-color: #ffffff;"><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">通常情况无需屏蔽外部信号。信号作为进程间的一种通信方式，异步信号到来意味着外部要求进程的退出；</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">绑定自定义信号处理程序做清理工作，当外部信号到来时，确保进程异常退出前，自定义信号处理程序被调用做清理工作，比如删除创建的临时文件。</li><li style="font-family: arial, nsimsun, sans-serif; margin-top: 0px; margin-bottom: 0px; padding-top: 5px; padding-right: 5px; padding-bottom: 3px;">针对第二类情况，编程过程中确保进程不要做非法操作，尤其是在访问内存时，确保内存已经分配给进程（映射入进程虚拟地址空间），不要向只读区写入数据。</li></ol><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;"><a name="minor5.2"><span style="font-size: 1.2em; font-weight: bold;">问题调试和定位</span></a></p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">进程异常退出时，操作系统会产生 core dump 文件，cored ump 文件是进程异常退出前内存状态的快照，运行 GDB 分析 core dump 文件可以帮助调试和定位问题。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">1) 首先，分析 core dump 查看导致进程异常退出的具体信号和退出原因。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">使用 GDB 调试实例一（代码清单 3）的分析结果如下：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# gdb demoSegfault core.24065   ( 已省略不相干文本 )   Core was generated by `./demoSegfault'.   Program terminated with signal 11, Segmentation fault.  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">分析结果显示，终止进程运行的信号是 11，SIGSEGV，原因是内存非法访问。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">2) 然后，定位错误代码。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在 GDB 分析 core dump 时，输入&#8220;bt&#8221;指令打印进程退出时的代码调用链，即 backtrace，就可以定位到错误代码。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">用 gcc 编译程序时加入参数 -g 可以生成符号文件，帮助调试。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">重新编译、执行实例一，并且分析 core dump 文件，定位错误代码：</p><table width="100%" cellpadding="0" summary="这一表格包含代码清单。" cellspacing="0" border="0" style="font-size: 0.8em; color: #000000; font-family: Simsun; background-color: #ffffff;"><tbody><tr><td style="font-family: arial, nsimsun, sans-serif; border-style: solid; border-color: #cccccc; padding: 2px 2px 5px; background-color: #f7f7f7 !important;"><pre style="width: 694px; margin-top: 0px; margin-bottom: 0px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; font-size: 11px; overflow: auto;"> [root@machine ~]# gcc -o demoSegfault demoSegfault.c -g   [root@machine ~]# ./demoSegfault  &amp;   [1] 28066   [1]+  Segmentation fault      (core dumped) ./demoSegfault   [root@machine ~]# gdb demoSegfault /corefiles/core.28066   ( 已省略不相干文本 )   Core was generated by `./demoSegfault'.   Program terminated with signal 11, Segmentation fault.   #0  0x0804835a in main () at demoSegfault.c:5   5               str[0] = 'H';   (gdb) bt   #0  0x0804835a in main () at demoSegfault.c:5   (gdb)  </pre></td></tr></tbody></table><br style="font-family: Simsun; font-size: medium; line-height: normal; background-color: #ffffff;" /><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">在加了参数 -g 编译后，我们可以用 gdb 解析出更多的信息帮助我们调试。在输入&#8220;bt&#8221;后，GDB 输出提示错误出现在第 5 行。</p><p style="font-family: arial, nsimsun, sans-serif; margin: 0px; padding: 0.3em 5px 0.7em; font-size: 0.76em; line-height: normal; background-color: #ffffff;">3) 最后，在定位到错误代码行后，就可以很快知道根本原因，并且修改错误代码。</p><div></div><img src ="http://www.cppblog.com/API/aggbug/203505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-09-30 16:50 <a href="http://www.cppblog.com/API/archive/2013/09/30/203505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用epoll 在 linux 上开发高性能应用服务器 </title><link>http://www.cppblog.com/API/archive/2013/09/17/203284.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Tue, 17 Sep 2013 09:31:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/09/17/203284.html</guid><wfw:comment>http://www.cppblog.com/API/comments/203284.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/09/17/203284.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/203284.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/203284.html</trackback:ping><description><![CDATA[<p>概述<br /></p>
<p>epoll是linux提供一种多路复用的技术，类似各个平台都支持的select，只是epoll在内核的实现做了更多地优化，可以支持比select更多的文件描述符，当然也支持 socket这种网络的文件描述符。linux上的大并发的接入服务器，目前的实现方式肯定都通过epoll实现。</p>
<p><br /></p>
<p>epoll和线程</p>
<p>有很多开发人员用epoll的时候，会开多个线程来进行数据通信，比如一个线程专门accept(我个人早些年在FreeBSD用kqueue的时候，由于对内部机制没有基本了解也这样搞)，一个线程收发，或者接收和发送都用各自独立的线程。</p>
<p>通常情况下，accept独立线程是没必要的，accept对于内核而言，就应用程序从内核的未完成的SYN队列读取一点数据而已。具体参见 accept部分: </p>
<h3><a name="t0"></a><span class="link_title"><a href="http://blog.csdn.net/herm_lib/article/details/6008281"></a></span></h3>
<h3><a name="t1"></a><span class="link_title"><a href="http://blog.csdn.net/herm_lib/article/details/8192452">TCP三次握手过程与对应的Berkeley Socket APIs的介绍</a></span></h3>
<p>&nbsp;</p>
<p>收发独立成两个线程也没有必要，因为大部分的应用服务器，通常情况下，启动一个线程收发数据，最大数据的收发量瓶颈在于网卡，而不是CPU；像网游接入服务器配置一个KM的网卡，很少有游戏会申请1G的带宽，那一台机器得有多少数据输入和输出。所以我们通信线程为epoll服务器就够了。</p>
<p><br /></p>
<p>epoll的基本原理</p>
<p>为了让某些朋友能读得更连惯，我还是说一下epoll基本原理。</p>
<p>epoll外部表现和select是一样的。他提供READ, WRITE和ERROR等事件。</p>
<p>大致流程像下面这样:<br /></p>
<p>1. 应用注册感兴趣的事件到内核；</p>
<p>2. 内核在某种条件下，将事件通知应用程序；</p>
<p>3. 应用程序收到事件后，根据事件类型做对应的逻辑处理。</p>
<p><br /></p>
<p>原理部分我再说一下，不容易理解的地方，包括水平触发和边缘触发，WRITE事件的事件利用(这个可以结合参考文献1的kqueue的WRITE事件，原理一致的)和修改事件的细节。</p>
<p>水平触发</p>
<p>READ事件，socket recv buff有数据，将一直向应用程序通知，直到buff为空。</p>
<p>WRITE事件，socket send buff从满的状态到可发送数据，将一直通知应用程序，直到buff满。</p>
<p><br /></p>
<p>边缘触发</p>
<p>READ事件，socket recv buff有数据了，只通知应用一次，不管应用程序有没有调用read api，接下来都不通知了。</p>
<p>WRITE事件，socket send buff从满的状态到可以发送数据，只通知一次。</p>
<p>上面这个解释不知道大家能否理解，也只能这样说了。有疑问的做一下试验。另外，这些细节的东西，前几年固定下来后，这几年做的项目，是直接用的，也就很少在涉及细节，是凭理解和记忆写下的文字，万一有错请指正^-^。</p>
<p><br /></p>
<p>WRITE事件的利用</p>
<p>这个还一下不好描述。大概描述一下，详细看参考文献1。大致这样:</p>
<p>1. 逻辑层写数据到应用层的发送buff，向epoll注册一下WRITE事件；</p>
<p>2. 这时epoll会通知应用程序一个WRITE事件；</p>
<p>3. 在WRITE事件响应函数里，从应用层的发送buff读数据，然后用socket send api发送。</p>
<p>因为我在很多实际项目中，看到大家没有利用epoll的WRITE的事件来发数据，特意地说一下。大部分的项目，是直接轮询应用程序的发送队列，我早期项目也是这么干的。</p>
<p><br /></p>
<p>epoll的修改事件</p>
<p>对于这个我的映像比较深刻。epoll的修改事件比较坑爹，不能单独修改某个事件！怎么说呢？比如epoll里已经注册了READ&amp;WRITE事件，你如果想单单重注册一下WRITE事件而且READ事件不变，epoll的epoll_ctl API是做不到的，你必须同时注册READ&amp;WRITE，这个在下面的代码中可以看到。FreeBSD的kqueue在这一点完全满足我们程序员的要求。</p>
<p><br /></p>
<p>抽象epoll API</p>
<p>我把herm socket epoll封装部分贴出来，让朋友们参考一下epoll的用法。大部分错误抛异常代码被我去掉了。</p>
<p>&nbsp;</p>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span class="keyword">class</span><span> Multiplexor </span></li><li><span>{ </span></li><li class="alt"><span class="keyword">public</span><span>: </span></li><li><span>Multiplexor(<span class="datatypes">int</span><span> size, </span><span class="datatypes">int</span><span> timeout = -1, </span><span class="datatypes">bool</span><span> lt = </span><span class="keyword">true</span><span>); </span></span></li><li class="alt"><span>~Multiplexor(); </span></li><li><span></span></li><li class="alt"><span class="keyword">void</span><span> Run(); </span></li><li><span class="keyword">void</span><span> Register(ISockHandler* eh, MultiplexorMask mask); </span></li><li class="alt"><span class="keyword">void</span><span> Remove(ISockHandler* eh); </span></li><li><span class="keyword">void</span><span> EnableMask(ISockHandler* eh, MultiplexorMask mask); </span></li><li class="alt"><span class="keyword">void</span><span> DisableMask(ISockHandler* eh, MultiplexorMask mask); </span></li><li><span class="keyword">private</span><span>: </span></li><li class="alt"><span class="keyword">inline</span><span> </span><span class="datatypes">bool</span><span> OperateHandler(</span><span class="datatypes">int</span><span> op, ISockHandler* eh, MultiplexorMask mask) </span></li><li><span>{ </span></li><li class="alt"><span class="keyword">struct</span><span> epoll_event evt; </span></li><li><span>evt.data.ptr = eh; </span></li><li class="alt"><span>evt.events = mask; </span></li><li><span class="keyword">return</span><span> epoll_ctl(m_epfd, op, eh-&gt;GetHandle(), &amp;evt) != -1; </span></li><li class="alt"><span>} </span></li><li><span class="keyword">private</span><span>: </span></li><li class="alt"><span class="datatypes">int</span><span> m_epfd; </span></li><li><span class="keyword">struct</span><span> epoll_event* m_evts; </span></li><li class="alt"><span class="datatypes">int</span><span> m_size; </span></li><li><span class="datatypes">int</span><span> m_timeout; </span></li><li class="alt"><span>__uint32_t m_otherMasks; </span></li><li><span>}; </span></li><li class="alt"><span></span></li></ol></div><pre style="display: none" class="cpp" name="code">class Multiplexor
{
public:
	Multiplexor(int size, int timeout = -1, bool lt = true);
	~Multiplexor();

	void Run();
	void Register(ISockHandler* eh, MultiplexorMask mask);
	void Remove(ISockHandler* eh);
	void EnableMask(ISockHandler* eh, MultiplexorMask mask);
	void DisableMask(ISockHandler* eh, MultiplexorMask mask);
private:
	inline bool OperateHandler(int op, ISockHandler* eh, MultiplexorMask mask)
	{
		struct epoll_event evt;
		evt.data.ptr = eh;
		evt.events = mask;
		return epoll_ctl(m_epfd, op, eh-&gt;GetHandle(), &amp;evt) != -1;
	}
private:
	int m_epfd;
	struct epoll_event* m_evts;
	int m_size;
	int m_timeout;
	__uint32_t m_otherMasks;
};
 </pre>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span>Multiplexor::Multiplexor(</span><span class="datatypes">int</span><span> size, </span><span class="datatypes">int</span><span> timeout, </span><span class="datatypes">bool</span><span> lt) </span></li><li><span>{ </span></li><li class="alt"><span>m_epfd = epoll_create(size); </span></li><li><span class="keyword">if</span><span> (m_epfd == -1) </span></li><li class="alt"><span class="keyword">throw</span><span> HERM_SOCKET_EXCEPTION(ST_OTHER); </span></li><li><span></span></li><li class="alt"><span>m_size = size; </span></li><li><span>m_evts = <span class="keyword">new</span><span> </span><span class="keyword">struct</span><span> epoll_event[size]; </span></span></li><li class="alt"><span></span></li><li><span>m_timeout = timeout; </span></li><li class="alt"><span></span></li><li><span class="comment">// sys/epoll.h is no EPOLLRDHUP(0X2000), don't add EPOLLRDHUP</span><span> </span></li><li class="alt"><span>m_otherMasks = EPOLLERR | EPOLLHUP; </span></li><li><span class="keyword">if</span><span> (!lt) </span></li><li class="alt"><span>m_otherMasks |= EPOLLET; </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span>Multiplexor::~Multiplexor() </span></li><li class="alt"><span>{ </span></li><li><span>close(m_epfd); </span></li><li class="alt"><span class="keyword">delete</span><span>[] m_evts; </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">void</span><span> Multiplexor::Run() </span></li><li class="alt"><span>{ </span></li><li><span class="datatypes">int</span><span> fds = epoll_wait(m_epfd, m_evts, m_size, m_timeout); </span></li><li class="alt"><span class="keyword">if</span><span> (fds == -1) </span></li><li><span>{ </span></li><li class="alt"><span class="keyword">if</span><span> (errno == EINTR) </span></li><li><span class="keyword">return</span><span>; </span></li><li class="alt"><span>} </span></li><li><span></span></li><li class="alt"><span class="keyword">for</span><span> (</span><span class="datatypes">int</span><span> i = 0; i &lt; fds; ++i) </span></li><li><span>{ </span></li><li class="alt"><span>__uint32_t evts = m_evts[i].events; </span></li><li><span>ISockHandler* eh = <span class="keyword">reinterpret_cast</span><span>&lt;ISockHandler*&gt;(m_evts[i].data.ptr); </span></span></li><li class="alt"><span class="datatypes">int</span><span> stateType = ST_SUCCESS; </span></li><li><span class="keyword">if</span><span> (evts &amp; EPOLLIN) </span></li><li class="alt"><span>stateType = eh-&gt;OnReceive(); </span></li><li><span></span></li><li class="alt"><span class="keyword">if</span><span> (evts &amp; EPOLLOUT) </span></li><li><span>stateType = eh-&gt;OnSend(); </span></li><li class="alt"><span></span></li><li><span class="keyword">if</span><span> (evts &amp; EPOLLERR || evts &amp; EPOLLHUP) </span></li><li class="alt"><span>stateType = ST_EXCEPT_FAILED; </span></li><li><span></span></li><li class="alt"><span class="keyword">if</span><span> (stateType != ST_SUCCESS) </span></li><li><span>eh-&gt;OnError(stateType, errno); </span></li><li class="alt"><span>} </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">void</span><span> Multiplexor::Register(ISockHandler* eh, MultiplexorMask mask) </span></li><li class="alt"><span>{ </span></li><li><span>MultiplexorMask masks = mask | m_otherMasks; </span></li><li class="alt"><span>OperateHandler(EPOLL_CTL_ADD, eh, masks); </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">void</span><span> Multiplexor::Remove(ISockHandler* eh) </span></li><li class="alt"><span>{ </span></li><li><span class="comment">// Delete fd from epoll, don't need masks</span><span> </span></li><li class="alt"><span>OperateHandler(EPOLL_CTL_DEL, eh, ALL_EVENTS_MASK); </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">void</span><span> Multiplexor::EnableMask(ISockHandler* eh, MultiplexorMask mask) </span></li><li class="alt"><span>{ </span></li><li><span>MultiplexorMask masks = mask | Herm::READ_MASK | Herm::WRITE_MASK; </span></li><li class="alt"><span>OperateHandler(EPOLL_CTL_MOD, eh, masks | m_otherMasks); </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">void</span><span> Multiplexor::DisableMask(ISockHandler* eh, MultiplexorMask mask) </span></li><li class="alt"><span>{ </span></li><li><span>MultiplexorMask masks = (Herm::READ_MASK | Herm::WRITE_MASK) &amp; (~mask); </span></li><li class="alt"><span class="keyword">if</span><span> (!OperateHandler(EPOLL_CTL_MOD, eh, masks | m_otherMasks)) </span></li><li><span class="keyword">throw</span><span> HERM_SOCKET_EXCEPTION(ST_OTHER); </span></li><li class="alt"><span>} </span></li></ol></div><pre style="display: none" class="cpp" name="code">Multiplexor::Multiplexor(int size, int timeout, bool lt) 
{
	m_epfd = epoll_create(size);
	if (m_epfd == -1)
		throw HERM_SOCKET_EXCEPTION(ST_OTHER);
	
	m_size = size;
	m_evts = new struct epoll_event[size];

	m_timeout = timeout;

	// sys/epoll.h is no EPOLLRDHUP(0X2000), don't add EPOLLRDHUP
	m_otherMasks = EPOLLERR | EPOLLHUP;
	if (!lt)
		m_otherMasks |= EPOLLET;
}

Multiplexor::~Multiplexor()
{
	close(m_epfd);
	delete[] m_evts;
}

void Multiplexor::Run()
{
	int fds = epoll_wait(m_epfd, m_evts, m_size, m_timeout); 
	if (fds == -1)
	{
		if (errno == EINTR)
			return;
	}
	
	for (int i = 0; i &lt; fds; ++i)
	{
		__uint32_t evts = m_evts[i].events;
		ISockHandler* eh = reinterpret_cast&lt;ISockHandler*&gt;(m_evts[i].data.ptr);
		int stateType = ST_SUCCESS;
		if (evts &amp; EPOLLIN)
			stateType = eh-&gt;OnReceive();

		if (evts &amp; EPOLLOUT)
			stateType = eh-&gt;OnSend();

		if (evts &amp; EPOLLERR || evts &amp; EPOLLHUP)
			stateType = ST_EXCEPT_FAILED;

		if (stateType != ST_SUCCESS)
			eh-&gt;OnError(stateType, errno);
	}
}

void Multiplexor::Register(ISockHandler* eh, MultiplexorMask mask)
{
	MultiplexorMask masks = mask | m_otherMasks;
	OperateHandler(EPOLL_CTL_ADD, eh, masks);
}

void Multiplexor::Remove(ISockHandler* eh)
{
	// Delete fd from epoll, don't need masks
	OperateHandler(EPOLL_CTL_DEL, eh, ALL_EVENTS_MASK);
}

void Multiplexor::EnableMask(ISockHandler* eh, MultiplexorMask mask)
{
	MultiplexorMask masks = mask | Herm::READ_MASK | Herm::WRITE_MASK;
	OperateHandler(EPOLL_CTL_MOD, eh, masks | m_otherMasks);
}

void Multiplexor::DisableMask(ISockHandler* eh, MultiplexorMask mask)
{
	MultiplexorMask masks = (Herm::READ_MASK | Herm::WRITE_MASK) &amp; (~mask);
	if (!OperateHandler(EPOLL_CTL_MOD, eh, masks | m_otherMasks))
		throw HERM_SOCKET_EXCEPTION(ST_OTHER);
}
</pre>
<p>上面类就用到epoll_create(), epoll_ctl()和epoll_wait()，以及几种事件。epoll用起来比select清爽一些。</p>
<p><br /></p>
<p>大致用法类似下面这样：</p>
<p>先定义一个Handler</p>
<p>&nbsp;</p>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span class="keyword">class</span><span> StreamHandler : </span><span class="keyword">public</span><span> Herm::ISockHandler </span></li><li><span>{ </span></li><li class="alt"><span class="keyword">public</span><span>: </span></li><li><span class="keyword">virtual</span><span> Herm::Handle GetHandle() </span><span class="keyword">const</span><span>; </span></li><li class="alt"><span class="keyword">virtual</span><span> </span><span class="datatypes">int</span><span> OnReceive(</span><span class="datatypes">int</span><span>); </span></li><li><span class="keyword">virtual</span><span> </span><span class="datatypes">int</span><span> OnSend(</span><span class="datatypes">int</span><span>); </span></li><li class="alt"><span>}; </span></li></ol></div><pre style="display: none" class="cpp" name="code">class StreamHandler : public Herm::ISockHandler
{  
public:	
	virtual Herm::Handle GetHandle() const;
    virtual int OnReceive(int); 
	virtual int OnSend(int);
};
</pre>
<p>&nbsp;</p>
<p>在OnReceive()处理收到数据的动作，在OnSend()。。。。</p>
<p>在通信线程中，大概类似这样的代码，实际看情况。</p>
<p>&nbsp;</p>
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/herm_lib/article/details/8192371#"><u><font color="#000080">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span>Multiplexor multiplexor; </span></li><li><span>StreamHandler sh; </span></li><li class="alt"><span>multiplexor.Register(&amp;sh, READ_EVT); </span></li><li><span>multiplexor.Run(...); </span></li></ol></div><pre style="display: none" class="cpp" name="code">Multiplexor multiplexor;
StreamHandler sh;
multiplexor.Register(&amp;sh, READ_EVT);
multiplexor.Run(...);</pre>
<p><br /></p>
<p>&nbsp;</p>
<p>参考文献<br /></p>
<p>1. <br /></p>
<p>&nbsp;</p>
<h3><a name="t2"></a><span class="link_title"><a href="http://blog.csdn.net/herm_lib/article/details/6047038">使用 kqueue 在 FreeBSD 上开发高性能应用服务器</a></span><br /></h3>
<h3><a name="t3"></a>FreeBSD上kqueue和epoll是类似的，有兴趣的朋友请参考。<br /></h3>
<h3><a name="t4"></a>http://blog.csdn.net/herm_lib/article/details/6047038</h3>
<p>&nbsp;</p>
<p>2. </p>
<h3><a name="t5"></a><span class="link_title"><a href="http://blog.csdn.net/herm_lib/article/details/5980657">【Herm程序员开发指导】第2章 Herm Framework和网络通信组件</a></span></h3>这里涉及到epoll的通信层如何和逻辑数据交互的问题<br />
<p>&nbsp;</p>
<p>http://blog.csdn.net/herm_lib/article/details/5980657</p><img src ="http://www.cppblog.com/API/aggbug/203284.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-09-17 17:31 <a href="http://www.cppblog.com/API/archive/2013/09/17/203284.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll 的accept , read, write(重要)</title><link>http://www.cppblog.com/API/archive/2013/09/13/203220.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 13 Sep 2013 08:28:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/09/13/203220.html</guid><wfw:comment>http://www.cppblog.com/API/comments/203220.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/09/13/203220.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/203220.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/203220.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK)从字面上看, 意思是:* EAGAIN: 再试一次* EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block* perror输出: &nbsp;Resource temporarily unavailable...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/09/13/203220.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/203220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-09-13 16:28 <a href="http://www.cppblog.com/API/archive/2013/09/13/203220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux errno 错误对照表 </title><link>http://www.cppblog.com/API/archive/2013/08/31/202889.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sat, 31 Aug 2013 05:33:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/08/31/202889.html</guid><wfw:comment>http://www.cppblog.com/API/comments/202889.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/08/31/202889.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/202889.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/202889.html</trackback:ping><description><![CDATA[<p>errno 在 &lt;errno.h&gt; 中定义，错误 Exx 的宏定义在 /usr/include/asm-generic 文件夹下面的 errno-base.h 和 errno.h，分别定义了 1-34 、35-132 的错误定义。</p>
<p>strerror() 函数依据 errno 值返回错误描述字符串，下面程序打印对照表：</p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /><span style="color: #000000">01</span><span style="color: #000000">.#include&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">errno.h</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /></span><span style="color: #000000">02</span><span style="color: #000000">.#include&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #0000ff">string</span><span style="color: #000000">.h</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /></span><span style="color: #000000">03</span><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">&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /></span><span style="color: #000000">04</span><span style="color: #000000">.&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /></span><span style="color: #000000">05</span><span style="color: #000000">.</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;main()&nbsp;&nbsp;<br /><img id="Codehighlighter1_98_284_Open_Image" onclick="this.style.display='none'; Codehighlighter1_98_284_Open_Text.style.display='none'; Codehighlighter1_98_284_Closed_Image.style.display='inline'; Codehighlighter1_98_284_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif"><img style="display: none" id="Codehighlighter1_98_284_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_98_284_Closed_Text.style.display='none'; Codehighlighter1_98_284_Open_Image.style.display='inline'; Codehighlighter1_98_284_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif"></span><span style="color: #000000">06</span><span style="color: #000000">.</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_98_284_Closed_Text"><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_98_284_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif"  alt="" /></span><span style="color: #000000">07</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;i;&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif"  alt="" /></span><span style="color: #000000">08</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">(i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;&nbsp;i&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">140</span><span style="color: #000000">;&nbsp;</span><span style="color: #000000">++</span><span style="color: #000000">i)&nbsp;&nbsp;<br /><img id="Codehighlighter1_159_258_Open_Image" onclick="this.style.display='none'; Codehighlighter1_159_258_Open_Text.style.display='none'; Codehighlighter1_159_258_Closed_Image.style.display='inline'; Codehighlighter1_159_258_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_159_258_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_159_258_Closed_Text.style.display='none'; Codehighlighter1_159_258_Open_Image.style.display='inline'; Codehighlighter1_159_258_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif"></span><span style="color: #000000">09</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_159_258_Closed_Text"><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /></span><span id="Codehighlighter1_159_258_Open_Text"><span style="color: #000000">{&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif"  alt="" /></span><span style="color: #000000">10</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;errno&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;i;&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif"  alt="" /></span><span style="color: #000000">11</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="color: #000000">"</span><span style="color: #000000">errno&nbsp;%d&nbsp;:\t\t%s\n</span><span style="color: #000000">"</span><span style="color: #000000">,i,strerror(errno));&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif"  alt="" /></span><span style="color: #000000">12</span><span style="color: #000000">.&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000">&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif"  alt="" /></span><span style="color: #000000">13</span><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">;&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif"  alt="" /></span><span style="color: #000000">14</span><span style="color: #000000">.}</span></span><span style="color: #000000">&nbsp;&nbsp;<br /><img align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif"  alt="" /></span></div>
<p>&nbsp;</p><br />错误对照表： 
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>errno0 : Success</p>
<p>errno1 : Operation not permitted</p>
<p>errno2 : No such file or directory</p>
<p>errno3 : No such process</p>
<p>errno4 : Interrupted system call</p>
<p>errno5 : Input/output error</p>
<p>errno6 : No such device or address</p>
<p>errno7 : Argument list too long</p>
<p>errno8 : Exec format error</p>
<p>errno9 : Bad file descriptor</p>
<p>errno10 : No child processes</p>
<p>errno11 : Resource temporarily unavailable</p>
<p>errno12 : Cannot allocate memory</p>
<p>errno13 : Permission denied</p>
<p>errno14 : Bad address</p>
<p>errno15 : Block device required</p>
<p>errno16 : Device or resource busy</p>
<p>errno17 : File exists</p>
<p>errno18 : Invalid cross-device link</p>
<p>errno19 : No such device</p>
<p>errno20 : Not a directory</p>
<p>errno21 : Is a directory</p>
<p>errno22 : Invalid argument</p>
<p>errno23 : Too many open files in system</p>
<p>errno24 : Too many open files</p>
<p>errno25 : Inappropriate ioctl for device</p>
<p>errno26 : Text file busy</p>
<p>errno27 : File too large</p>
<p>errno28 : No space left on device</p>
<p>errno29 : Illegal seek</p>
<p>errno30 : Read-only file system</p>
<p>errno31 : Too many links</p>
<p>errno32 : Broken pipe</p>
<p>errno33 : Numerical argument out of domain</p>
<p>errno34 : Numerical result out of range</p>
<p>errno35 : Resource deadlock avoided</p>
<p>errno36 : File name too long</p>
<p>errno37 : No locks available</p>
<p>errno38 : Function not implemented</p>
<p>errno39 : Directory not empty</p>
<p>errno40 : Too many levels of symbolic links</p>
<p>errno41 : Unknown error 41</p>
<p>errno42 : No message of desired type</p>
<p>errno43 : Identifier removed</p>
<p>errno44 : Channel number out of range</p>
<p>errno45 : Level 2 not synchronized</p>
<p>errno46 : Level 3 halted</p>
<p>errno47 : Level 3 reset</p>
<p>errno48 : Link number out of range</p>
<p>errno49 : Protocol driver not attached</p>
<p>errno50 : No CSI structure available</p>
<p>errno51 : Level 2 halted</p>
<p>errno52 : Invalid exchange</p>
<p>errno53 : Invalid request descriptor</p>
<p>errno54 : Exchange full</p>
<p>errno55 : No anode</p>
<p>errno56 : Invalid request code</p>
<p>errno57 : Invalid slot</p>
<p>errno58 : Unknown error 58</p>
<p>errno59 : Bad font file format</p>
<p>errno60 : Device not a stream</p>
<p>errno61 : No data available</p>
<p>errno62 : Timer expired</p>
<p>errno63 : Out of streams resources</p>
<p>errno64 : Machine is not on the network</p>
<p>errno65 : Package not installed</p>
<p>errno66 : Object is remote</p>
<p>errno67 : Link has been severed</p>
<p>errno68 : Advertise error</p>
<p>errno69 : Srmount error</p>
<p>errno70 : Communication error on send</p>
<p>errno71 : Protocol error</p>
<p>errno72 : Multihop attempted</p>
<p>errno73 : RFS specific error</p>
<p>errno74 : Bad message</p>
<p>errno75 : Value too large for defined datatype</p>
<p>errno76 : Name not unique on network</p>
<p>errno77 : File descriptor in bad state</p>
<p>errno78 : Remote address changed</p>
<p>errno79 : Can not access a needed sharedlibrary</p>
<p>errno80 : Accessing a corrupted sharedlibrary</p>
<p>errno81 : .lib section in a.out corrupted</p>
<p>errno82 : Attempting to link in too manyshared libraries</p>
<p>errno83 : Cannot exec a shared librarydirectly</p>
<p>errno84 : Invalid or incomplete multibyte orwide character</p>
<p>errno85 : Interrupted system call should berestarted</p>
<p>errno86 : Streams pipe error</p>
<p>errno87 : Too many users</p>
<p>errno88 : Socket operation on non-socket</p>
<p>errno89 : Destinationaddress required</p>
<p>errno90 : Message too long</p>
<p>errno91 : Protocol wrong type for socket</p>
<p>errno92 : Protocol not available</p>
<p>errno93 : Protocol not supported</p>
<p>errno94 : Socket type not supported</p>
<p>errno95 : Operation not supported</p>
<p>errno96 : Protocol family not supported</p>
<p>errno97 : Address family not supported byprotocol</p>
<p>errno98 : Address already in use</p>
<p>errno99 : Cannot assign requested address</p>
<p>errno100 : Network is down</p>
<p>errno101 : Network is unreachable</p>
<p>errno102 : Network dropped connection onreset</p>
<p>errno103 : Software caused connection abort</p>
<p>errno104 : Connection reset by peer</p>
<p>errno105 : No buffer space available</p>
<p>errno106 : Transport endpoint is alreadyconnected</p>
<p>errno107 : Transport endpoint is notconnected</p>
<p>errno108 : Cannot send after transportendpoint shutdown</p>
<p>errno109 : Too many references: cannot splice</p>
<p>errno110 : Connection timed out</p>
<p>errno111 : Connection refused</p>
<p>errno112 : Host is down</p>
<p>errno113 : No route to host</p>
<p>errno114 : Operation already in progress</p>
<p>errno115 : Operation now in progress</p>
<p>errno116 : Stale NFS file handle</p>
<p>errno117 : Structure needs cleaning</p>
<p>errno118 : Not a XENIX named type file</p>
<p>errno119 : No XENIX semaphores available</p>
<p>errno120 : Is a named type file</p>
<p>errno121 : Remote I/O error</p>
<p>errno122 : Disk quota exceeded</p>
<p>errno123 : No medium found</p>
<p>errno124 : Wrong medium type</p>
<p>errno125 : Operation canceled</p>
<p>errno126 : Required key not available</p>
<p>errno127 : Key has expired</p>
<p>errno128 : Key has been revoked</p>
<p>errno129 : Key was rejected by service</p>
<p>errno130 : Owner died</p>
<p>errno131 : State not recoverable</p>
<p>errno132 : Operation not possible due toRF-kill</p>
<p>errno133 : Unknown error 133</p>
<p>errno134 : Unknown error 134</p>
<p>errno135 : Unknown error 135</p>
<p>errno136 : Unknown error 136</p>
<p>errno137 : Unknown error 137</p>
<p>errno138 : Unknown error 138</p>
<p>errno139 : Unknown error 139</p>
<p><br /></p>
<p>由上可见Linux对错误宏的定义。</p>
<p><strong>头文件 /usr/include/asm-generic/errno-base.h 的源码：</strong></p>
<p>#ifndef _ASM_GENERIC_ERRNO_BASE_H<br />#define _ASM_GENERIC_ERRNO_BASE_H<br /><br />#define<span style="white-space: pre"> </span>EPERM<span style="white-space: pre"> </span>1<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Operation not permitted */<br />#define<span style="white-space: pre"> </span>ENOENT<span style="white-space: pre"></span>2<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No such file or directory */<br />#define<span style="white-space: pre"> </span>ESRCH<span style="white-space: pre"> </span>3<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No such process */<br />#define<span style="white-space: pre"> </span>EINTR<span style="white-space: pre"> </span>4<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Interrupted system call */<br />#define<span style="white-space: pre"> </span>EIO<span style="white-space: pre"> </span>5<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* I/O error */<br />#define<span style="white-space: pre"> </span>ENXIO<span style="white-space: pre"> </span>6<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No such device or address */<br />#define<span style="white-space: pre"> </span>E2BIG<span style="white-space: pre"> </span>7<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Argument list too long */<br />#define<span style="white-space: pre"> </span>ENOEXEC<span style="white-space: pre"></span>8<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Exec format error */<br />#define<span style="white-space: pre"> </span>EBADF<span style="white-space: pre"> </span>9<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Bad file number */<br />#define<span style="white-space: pre"> </span>ECHILD<span style="white-space: pre"></span>10<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No child processes */<br />#define<span style="white-space: pre"> </span>EAGAIN<span style="white-space: pre"></span>11<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Try again */<br />#define<span style="white-space: pre"> </span>ENOMEM<span style="white-space: pre"></span>12<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Out of memory */<br />#define<span style="white-space: pre"> </span>EACCES<span style="white-space: pre"></span>13<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Permission denied */<br />#define<span style="white-space: pre"> </span>EFAULT<span style="white-space: pre"></span>14<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Bad address */<br />#define<span style="white-space: pre"> </span>ENOTBLK<span style="white-space: pre"></span>15<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Block device required */<br />#define<span style="white-space: pre"> </span>EBUSY<span style="white-space: pre"> </span>16<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Device or resource busy */<br />#define<span style="white-space: pre"> </span>EEXIST<span style="white-space: pre"></span>17<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* File exists */<br />#define<span style="white-space: pre"> </span>EXDEV<span style="white-space: pre"> </span>18<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Cross-device link */<br />#define<span style="white-space: pre"> </span>ENODEV<span style="white-space: pre"></span>19<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No such device */<br />#define<span style="white-space: pre"> </span>ENOTDIR<span style="white-space: pre"></span>20<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Not a directory */<br />#define<span style="white-space: pre"> </span>EISDIR<span style="white-space: pre"></span>21<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Is a directory */<br />#define<span style="white-space: pre"> </span>EINVAL<span style="white-space: pre"></span>22<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Invalid argument */<br />#define<span style="white-space: pre"> </span>ENFILE<span style="white-space: pre"></span>23<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* File table overflow */<br />#define<span style="white-space: pre"> </span>EMFILE<span style="white-space: pre"></span>24<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Too many open files */<br />#define<span style="white-space: pre"> </span>ENOTTY<span style="white-space: pre"></span>25<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Not a typewriter */<br />#define<span style="white-space: pre"> </span>ETXTBSY<span style="white-space: pre"></span>26<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Text file busy */<br />#define<span style="white-space: pre"> </span>EFBIG<span style="white-space: pre"> </span>27<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* File too large */<br />#define<span style="white-space: pre"> </span>ENOSPC<span style="white-space: pre"></span>28<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No space left on device */<br />#define<span style="white-space: pre"> </span>ESPIPE<span style="white-space: pre"></span>29<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Illegal seek */<br />#define<span style="white-space: pre"> </span>EROFS<span style="white-space: pre"> </span>30<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Read-only file system */<br />#define<span style="white-space: pre"> </span>EMLINK<span style="white-space: pre"></span>31<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Too many links */<br />#define<span style="white-space: pre"> </span>EPIPE<span style="white-space: pre"> </span>32<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Broken pipe */<br />#define<span style="white-space: pre"> </span>EDOM<span style="white-space: pre"> </span>33<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Math argument out of domain of func */<br />#define<span style="white-space: pre"> </span>ERANGE<span style="white-space: pre"></span>34<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Math result not representable */<br /><br />#endif<br /></p>
<p><br /></p>
<p><strong>头文件/usr/include/asm-generic/erno.h源码：</strong></p>
<p>#ifndef _ASM_GENERIC_ERRNO_H<br />#define _ASM_GENERIC_ERRNO_H<br /><br />#include &lt;asm-generic/errno-base.h&gt;<br /><br />#define<span style="white-space: pre"> </span>EDEADLK<span style="white-space: pre"></span>35<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Resource deadlock would occur */<br />#define<span style="white-space: pre"> </span>ENAMETOOLONG<span style="white-space: pre"></span>36<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* File name too long */<br />#define<span style="white-space: pre"> </span>ENOLCK<span style="white-space: pre"></span>37<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No record locks available */<br />#define<span style="white-space: pre"> </span>ENOSYS<span style="white-space: pre"></span>38<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Function not implemented */<br />#define<span style="white-space: pre"> </span>ENOTEMPTY<span style="white-space: pre"></span>39<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Directory not empty */<br />#define<span style="white-space: pre"> </span>ELOOP<span style="white-space: pre"> </span>40<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Too many symbolic links encountered */<br />#define<span style="white-space: pre"> </span>EWOULDBLOCK<span style="white-space: pre"></span>EAGAIN<span style="white-space: pre"> </span>/* Operation would block */<br />#define<span style="white-space: pre"> </span>ENOMSG<span style="white-space: pre"></span>42<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* No message of desired type */<br />#define<span style="white-space: pre"> </span>EIDRM<span style="white-space: pre"> </span>43<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Identifier removed */<br />#define<span style="white-space: pre"> </span>ECHRNG<span style="white-space: pre"></span>44<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Channel number out of range */<br />#define<span style="white-space: pre"> </span>EL2NSYNC<span style="white-space: pre"></span>45<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Level 2 not synchronized */<br />#define<span style="white-space: pre"> </span>EL3HLT<span style="white-space: pre"></span>46<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Level 3 halted */<br />#define<span style="white-space: pre"> </span>EL3RST<span style="white-space: pre"></span>47<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Level 3 reset */<br />#define<span style="white-space: pre"> </span>ELNRNG<span style="white-space: pre"></span>48<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Link number out of range */<br />#define<span style="white-space: pre"> </span>EUNATCH<span style="white-space: pre"></span>49<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Protocol driver not attached */<br />#define<span style="white-space: pre"> </span>ENOCSI<span style="white-space: pre"></span>50<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* No CSI structure available */<br />#define<span style="white-space: pre"> </span>EL2HLT<span style="white-space: pre"></span>51<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Level 2 halted */<br />#define<span style="white-space: pre"> </span>EBADE<span style="white-space: pre"> </span>52<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Invalid exchange */<br />#define<span style="white-space: pre"> </span>EBADR<span style="white-space: pre"> </span>53<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Invalid request descriptor */<br />#define<span style="white-space: pre"> </span>EXFULL<span style="white-space: pre"></span>54<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Exchange full */<br />#define<span style="white-space: pre"> </span>ENOANO<span style="white-space: pre"></span>55<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No anode */<br />#define<span style="white-space: pre"> </span>EBADRQC<span style="white-space: pre"></span>56<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Invalid request code */<br />#define<span style="white-space: pre"> </span>EBADSLT<span style="white-space: pre"></span>57<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Invalid slot */<br /><br />#define<span style="white-space: pre"> </span>EDEADLOCK<span style="white-space: pre"></span>EDEADLK<br /><br />#define<span style="white-space: pre"> </span>EBFONT<span style="white-space: pre"></span>59<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Bad font file format */<br />#define<span style="white-space: pre"> </span>ENOSTR<span style="white-space: pre"></span>60<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Device not a stream */<br />#define<span style="white-space: pre"> </span>ENODATA<span style="white-space: pre"></span>61<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* No data available */<br />#define<span style="white-space: pre"> </span>ETIME<span style="white-space: pre"> </span>62<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Timer expired */<br />#define<span style="white-space: pre"> </span>ENOSR<span style="white-space: pre"> </span>63<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Out of streams resources */<br />#define<span style="white-space: pre"> </span>ENONET<span style="white-space: pre"></span>64<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Machine is not on the network */<br />#define<span style="white-space: pre"> </span>ENOPKG<span style="white-space: pre"></span>65<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Package not installed */<br />#define<span style="white-space: pre"> </span>EREMOTE<span style="white-space: pre"></span>66<span style="white-space: pre"> </span><span style="white-space: pre"></span>/* Object is remote */<br />#define<span style="white-space: pre"> </span>ENOLINK<span style="white-space: pre"></span>67<span style="white-space: pre"> <span style="white-space: pre"></span></span>/* Link has been severed */<br />#define<span style="white-space: pre"> </span>EADV<span style="white-space: pre"> </span>68<span style="white-space: pre"> </span>/* Advertise error */<br />#define<span style="white-space: pre"> </span>ESRMNT<span style="white-space: pre"></span>69<span style="white-space: pre"> </span>/* Srmount error */<br />#define<span style="white-space: pre"> </span>ECOMM<span style="white-space: pre"> </span>70<span style="white-space: pre"> </span>/* Communication error on send */<br />#define<span style="white-space: pre"> </span>EPROTO<span style="white-space: pre"></span>71<span style="white-space: pre"> </span>/* Protocol error */<br />#define<span style="white-space: pre"> </span>EMULTIHOP<span style="white-space: pre"></span>72<span style="white-space: pre"> </span>/* Multihop attempted */<br />#define<span style="white-space: pre"> </span>EDOTDOT<span style="white-space: pre"></span>73<span style="white-space: pre"> </span>/* RFS specific error */<br />#define<span style="white-space: pre"> </span>EBADMSG<span style="white-space: pre"></span>74<span style="white-space: pre"> </span>/* Not a data message */<br />#define<span style="white-space: pre"> </span>EOVERFLOW<span style="white-space: pre"></span>75<span style="white-space: pre"> </span>/* Value too large for defined data type */<br />#define<span style="white-space: pre"> </span>ENOTUNIQ<span style="white-space: pre"></span>76<span style="white-space: pre"> </span>/* Name not unique on network */<br />#define<span style="white-space: pre"> </span>EBADFD<span style="white-space: pre"></span>77<span style="white-space: pre"> </span>/* File descriptor in bad state */<br />#define<span style="white-space: pre"> </span>EREMCHG<span style="white-space: pre"></span>78<span style="white-space: pre"> </span>/* Remote address changed */<br />#define<span style="white-space: pre"> </span>ELIBACC<span style="white-space: pre"></span>79<span style="white-space: pre"> </span>/* Can not access a needed shared library */<br />#define<span style="white-space: pre"> </span>ELIBBAD<span style="white-space: pre"></span>80<span style="white-space: pre"> </span>/* Accessing a corrupted shared library */<br />#define<span style="white-space: pre"> </span>ELIBSCN<span style="white-space: pre"></span>81<span style="white-space: pre"> </span>/* .lib section in a.out corrupted */<br />#define<span style="white-space: pre"> </span>ELIBMAX<span style="white-space: pre"></span>82<span style="white-space: pre"> </span>/* Attempting to link in too many shared libraries */<br />#define<span style="white-space: pre"> </span>ELIBEXEC<span style="white-space: pre"></span>83<span style="white-space: pre"> </span>/* Cannot exec a shared library directly */<br />#define<span style="white-space: pre"> </span>EILSEQ<span style="white-space: pre"></span>84<span style="white-space: pre"> </span>/* Illegal byte sequence */<br />#define<span style="white-space: pre"> </span>ERESTART<span style="white-space: pre"></span>85<span style="white-space: pre"> </span>/* Interrupted system call should be restarted */<br />#define<span style="white-space: pre"> </span>ESTRPIPE<span style="white-space: pre"></span>86<span style="white-space: pre"> </span>/* Streams pipe error */<br />#define<span style="white-space: pre"> </span>EUSERS<span style="white-space: pre"></span>87<span style="white-space: pre"> </span>/* Too many users */<br />#define<span style="white-space: pre"> </span>ENOTSOCK<span style="white-space: pre"></span>88<span style="white-space: pre"> </span>/* Socket operation on non-socket */<br />#define<span style="white-space: pre"> </span>EDESTADDRREQ<span style="white-space: pre"></span>89<span style="white-space: pre"> </span>/* Destination address required */<br />#define<span style="white-space: pre"> </span>EMSGSIZE<span style="white-space: pre"></span>90<span style="white-space: pre"> </span>/* Message too long */<br />#define<span style="white-space: pre"> </span>EPROTOTYPE<span style="white-space: pre"></span>91<span style="white-space: pre"> </span>/* Protocol wrong type for socket */<br />#define<span style="white-space: pre"> </span>ENOPROTOOPT<span style="white-space: pre"></span>92<span style="white-space: pre"> </span>/* Protocol not available */<br />#define<span style="white-space: pre"> </span>EPROTONOSUPPORT<span style="white-space: pre"></span>93<span style="white-space: pre"> </span>/* Protocol not supported */<br />#define<span style="white-space: pre"> </span>ESOCKTNOSUPPORT<span style="white-space: pre"></span>94<span style="white-space: pre"> </span>/* Socket type not supported */<br />#define<span style="white-space: pre"> </span>EOPNOTSUPP<span style="white-space: pre"></span>95<span style="white-space: pre"> </span>/* Operation not supported on transport endpoint */<br />#define<span style="white-space: pre"> </span>EPFNOSUPPORT<span style="white-space: pre"></span>96<span style="white-space: pre"> </span>/* Protocol family not supported */<br />#define<span style="white-space: pre"> </span>EAFNOSUPPORT<span style="white-space: pre"></span>97<span style="white-space: pre"> </span>/* Address family not supported by protocol */<br />#define<span style="white-space: pre"> </span>EADDRINUSE<span style="white-space: pre"></span>98<span style="white-space: pre"> </span>/* Address already in use */<br />#define<span style="white-space: pre"> </span>EADDRNOTAVAIL<span style="white-space: pre"></span>99<span style="white-space: pre"> </span>/* Cannot assign requested address */<br />#define<span style="white-space: pre"> </span>ENETDOWN<span style="white-space: pre"></span>100<span style="white-space: pre"> </span>/* Network is down */<br />#define<span style="white-space: pre"> </span>ENETUNREACH<span style="white-space: pre"></span>101<span style="white-space: pre"> </span>/* Network is unreachable */<br />#define<span style="white-space: pre"> </span>ENETRESET<span style="white-space: pre"></span>102<span style="white-space: pre"> </span>/* Network dropped connection because of reset */<br />#define<span style="white-space: pre"> </span>ECONNABORTED<span style="white-space: pre"></span>103<span style="white-space: pre"> </span>/* Software caused connection abort */<br />#define<span style="white-space: pre"> </span>ECONNRESET<span style="white-space: pre"></span>104<span style="white-space: pre"> </span>/* Connection reset by peer */<br />#define<span style="white-space: pre"> </span>ENOBUFS<span style="white-space: pre"></span>105<span style="white-space: pre"> </span>/* No buffer space available */<br />#define<span style="white-space: pre"> </span>EISCONN<span style="white-space: pre"></span>106<span style="white-space: pre"> </span>/* Transport endpoint is already connected */<br />#define<span style="white-space: pre"> </span>ENOTCONN<span style="white-space: pre"></span>107<span style="white-space: pre"> </span>/* Transport endpoint is not connected */<br />#define<span style="white-space: pre"> </span>ESHUTDOWN<span style="white-space: pre"></span>108<span style="white-space: pre"> </span>/* Cannot send after transport endpoint shutdown */<br />#define<span style="white-space: pre"> </span>ETOOMANYREFS<span style="white-space: pre"></span>109<span style="white-space: pre"> </span>/* Too many references: cannot splice */<br />#define<span style="white-space: pre"> </span>ETIMEDOUT<span style="white-space: pre"></span>110<span style="white-space: pre"> </span>/* Connection timed out */<br />#define<span style="white-space: pre"> </span>ECONNREFUSED<span style="white-space: pre"></span>111<span style="white-space: pre"> </span>/* Connection refused */<br />#define<span style="white-space: pre"> </span>EHOSTDOWN<span style="white-space: pre"></span>112<span style="white-space: pre"> </span>/* Host is down */<br />#define<span style="white-space: pre"> </span>EHOSTUNREACH<span style="white-space: pre"></span>113<span style="white-space: pre"> </span>/* No route to host */<br />#define<span style="white-space: pre"> </span>EALREADY<span style="white-space: pre"></span>114<span style="white-space: pre"> </span>/* Operation already in progress */<br />#define<span style="white-space: pre"> </span>EINPROGRESS<span style="white-space: pre"></span>115<span style="white-space: pre"> </span>/* Operation now in progress */<br />#define<span style="white-space: pre"> </span>ESTALE<span style="white-space: pre"></span>116<span style="white-space: pre"> </span>/* Stale NFS file handle */<br />#define<span style="white-space: pre"> </span>EUCLEAN<span style="white-space: pre"></span>117<span style="white-space: pre"> </span>/* Structure needs cleaning */<br />#define<span style="white-space: pre"> </span>ENOTNAM<span style="white-space: pre"></span>118<span style="white-space: pre"> </span>/* Not a XENIX named type file */<br />#define<span style="white-space: pre"> </span>ENAVAIL<span style="white-space: pre"></span>119<span style="white-space: pre"> </span>/* No XENIX semaphores available */<br />#define<span style="white-space: pre"> </span>EISNAM<span style="white-space: pre"></span>120<span style="white-space: pre"> </span>/* Is a named type file */<br />#define<span style="white-space: pre"> </span>EREMOTEIO<span style="white-space: pre"></span>121<span style="white-space: pre"> </span>/* Remote I/O error */<br />#define<span style="white-space: pre"> </span>EDQUOT<span style="white-space: pre"></span>122<span style="white-space: pre"> </span>/* Quota exceeded */<br /><br />#define<span style="white-space: pre"> </span>ENOMEDIUM<span style="white-space: pre"></span>123<span style="white-space: pre"> </span>/* No medium found */<br />#define<span style="white-space: pre"> </span>EMEDIUMTYPE<span style="white-space: pre"></span>124<span style="white-space: pre"> </span>/* Wrong medium type */<br />#define<span style="white-space: pre"> </span>ECANCELED<span style="white-space: pre"></span>125<span style="white-space: pre"> </span>/* Operation Canceled */<br />#define<span style="white-space: pre"> </span>ENOKEY<span style="white-space: pre"></span>126<span style="white-space: pre"> </span>/* Required key not available */<br />#define<span style="white-space: pre"> </span>EKEYEXPIRED<span style="white-space: pre"></span>127<span style="white-space: pre"> </span>/* Key has expired */<br />#define<span style="white-space: pre"> </span>EKEYREVOKED<span style="white-space: pre"></span>128<span style="white-space: pre"> </span>/* Key has been revoked */<br />#define<span style="white-space: pre"> </span>EKEYREJECTED<span style="white-space: pre"></span>129<span style="white-space: pre"> </span>/* Key was rejected by service */<br /><br />/* for robust mutexes */<br />#define<span style="white-space: pre"> </span>EOWNERDEAD<span style="white-space: pre"></span>130<span style="white-space: pre"> </span>/* Owner died */<br />#define<span style="white-space: pre"> </span>ENOTRECOVERABLE<span style="white-space: pre"></span>131<span style="white-space: pre"> </span>/* State not recoverable */<br /><br />#define ERFKILL<span style="white-space: pre"> </span>132<span style="white-space: pre"></span>/* Operation not possible due to RF-kill */<br /><br />#endif</p><br /><img src ="http://www.cppblog.com/API/aggbug/202889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-08-31 13:33 <a href="http://www.cppblog.com/API/archive/2013/08/31/202889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux c md5加密 使用 openssl</title><link>http://www.cppblog.com/API/archive/2013/08/09/202427.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 09 Aug 2013 03:24:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/08/09/202427.html</guid><wfw:comment>http://www.cppblog.com/API/comments/202427.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/08/09/202427.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/202427.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/202427.html</trackback:ping><description><![CDATA[方法一：<br />#include&lt;stdio.h&gt;<br />#include&lt;openssl/md5.h&gt;<br />#include&lt;string.h&gt;<br /><br />int main( int argc, char **argv )<br />{<br />MD5_CTX ctx;<br />unsigned char *data="123";<br />unsigned char md[16];<br />char buf[33]={'\0'};<br />char tmp[3]={'\0'};<br />int i;<br /><br />MD5_Init(&amp;ctx);<br />MD5_Update(&amp;ctx,data,strlen(data));<br />MD5_Final(md,&amp;ctx);<br /><br />for( i=0; i&lt;16; i++ ){<br />sprintf(tmp,"%02X",md[i]);<br />strcat(buf,tmp);<br />}<br />printf("%s\n",buf);<br />return 0;<br />}<br /><br />输出：<br />202CB962AC59075B964B07152D234B70<br /><br />方法二：<br />#include&lt;stdio.h&gt;<br />#include&lt;openssl/md5.h&gt;<br />#include&lt;string.h&gt;<br /><br />int main( int argc, char **argv )<br />{<br />unsigned char *data = "123";<br />unsigned char md[16];<br />int i;<br />char tmp[3]={'\0'},buf[33]={'\0'};<br />MD5(data,strlen(data),md);<br />for (i = 0; i &lt; 16; i++){<br />sprintf(tmp,"%2.2x",md[i]);<br />strcat(buf,tmp);<br />}<br />printf("%s\n",buf);<br />return 0;<br />}<br />输出：<br />202cb962ac59075b964b07152d234b70<br /><br />总结：<br />两种实现方式，一样的结果。相比较，第二种方法更直观简单些。<br />遇到的问题：<br />1.sprintf时的格式，%02X和%2.2格式是一样的，强制输出两位，比如十进制的8，十六进制也是8，这个格式是控制输出两位，08。如果加密结果要小写字母显示，就是"%2.2x",大写就是"2.2X"。<br />2.一定要使用unsigned char,虽然是char，其实就是个int,当然，它只有一个字节，和char的区别是，unsigned char 0~255 ,char -127~127。使用char会出问题。这个md5加密函数，返回16个十进制数，范围在0～255间，把它format为十六进制就是32为md5编码了。<br />3.gcc编译的时候，后面加上参数 -lcrypto 如果系统没有安装libcrypto，是不能用这个方法的。<br /><br />如果不想使用openssl库函数，前面我的文章里有md5加密的源码，可以直接使用。 <img src ="http://www.cppblog.com/API/aggbug/202427.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-08-09 11:24 <a href="http://www.cppblog.com/API/archive/2013/08/09/202427.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll例子</title><link>http://www.cppblog.com/API/archive/2013/08/09/202425.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 09 Aug 2013 02:30:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/08/09/202425.html</guid><wfw:comment>http://www.cppblog.com/API/comments/202425.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/08/09/202425.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/202425.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/202425.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#include&nbsp;&lt;sys/epoll.h&gt;#include&nbsp;&lt;unistd.h&gt;//#include&nbsp;&lt;linux/tcp.h&gt;#inc...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/08/09/202425.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/202425.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-08-09 10:30 <a href="http://www.cppblog.com/API/archive/2013/08/09/202425.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux程序调试</title><link>http://www.cppblog.com/API/archive/2013/07/13/201774.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sat, 13 Jul 2013 12:18:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/13/201774.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201774.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/13/201774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201774.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201774.html</trackback:ping><description><![CDATA[Linux下的段错误产生的原因及调试方法 
<p>&nbsp;&nbsp;&nbsp;原文地址：<a href="http://www.upsdn.net/html/2006-11/775.html">http://www.upsdn.net/html/2006-11/775.html</a>&nbsp;<br />&nbsp;&nbsp;&nbsp;参考地址：<a href="http://www.cnblogs.com/khler/archive/2010/09/16/1828349.html">http://www.cnblogs.com/khler/archive/2010/09/16/1828349.html</a>&nbsp;</p>
<p>简而言之,产生段错误就是访问了错误的内存段，一般是你没有权限，或者根本就不存在对应的物理内存,尤其常见的是访问0地址.<br /><br />一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间，通常这个值是由gdtr来保存的，他是一个48位的寄存器，其中的32位是保存由它指向的gdt表，后13位保存相应于gdt的下标，最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表，在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了越界访问，cpu就会产生相应的异常保护，于是segmentation fault就出现了.<br /><br />在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的<br /><br />1)访问系统数据区，尤其是往&nbsp; 系统保护的内存地址写数据<br />&nbsp;&nbsp; 最常见就是给一个指针以0地址<br />2)内存越界(数组越界，变量类型不一致等) 访问到不属于你的内存区域<br /><br />解决方法<br /><br />我们在用C/C++语言写程序的时侯，内存管理的绝大部分工作都是需要我们来做的。实际上，内存管理是一个比较繁琐的工作，无论你多高明，经验多丰富，难 免会在此处犯些小错误，而通常这些错误又是那么的浅显而易于消除。但是手工&#8220;除虫&#8221;（debug），往往是效率低下且让人厌烦的，本文将就"段错误"这个 内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。<br />下面将就以下的一个存在段错误的程序介绍几种调试方法：<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp; dummy_function (void)<br />&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br />&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br />&nbsp;&nbsp;&nbsp;&nbsp; 5&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp; 6<br />&nbsp;&nbsp;&nbsp;&nbsp; 7&nbsp; int main (void)<br />&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp; 9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br />&nbsp;&nbsp;&nbsp; 10<br />&nbsp;&nbsp;&nbsp; 11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />&nbsp;&nbsp;&nbsp; 12&nbsp; }<br /></td></tr></tbody></table>作为一个熟练的C/C++程序员，以上代码的bug应该是很清楚的，因为它尝试操作地址为0的内存区域，而这个内存区域通常是不可访问的禁区，当然就会出错了。我们尝试编译运行它:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ ./a.out<br />段错误<br /></td></tr></tbody></table>果然不出所料，它出错并退出了。<br /><span style="font-weight: bold">1.利用gdb逐步查找段错误:</span><br />这种方法也是被大众所熟知并广泛采用的方法，首先我们需要一个带有调试信息的可执行程序，所以我们加上&#8220;-g -rdynamic"的参数进行编译，然后用gdb调试运行这个新编译的程序,具体步骤如下:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic d.c<br />xiaosuo@gentux test $ gdb ./a.out<br />GNU gdb 6.5<br />Copyright (C) 2006 Free Software Foundation, Inc.<br />GDB is free software, covered by the GNU General Public License, and you are<br />welcome to change it and/or distribute copies of it under certain conditions.<br />Type "show copying" to see the conditions.<br />There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br />This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br /><br />(gdb) r<br />Starting program: /home/xiaosuo/test/a.out<br /><br />Program received signal SIGSEGV, Segmentation fault.<br />0x08048524 in dummy_function () at d.c:4<br />4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br />(gdb)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br /></td></tr></tbody></table>哦？！好像不用一步步调试我们就找到了出错位置d.c文件的第4行，其实就是如此的简单。<br />从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal)，我们知道SIGSEGV默认handler的动作是打印&#8221;段错误"的出错信息，并产生Core文件，由此我们又产生了方法二。<br /><span style="font-weight: bold">2.分析Core文件：</span><br />Core文件是什么呢？<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>The&nbsp; default action of certain signals is to cause a process to terminate and produce a core dump file, a disk file containing an image of the process's memory&nbsp; at the time of termination.&nbsp; A list of the signals which cause a process to dump core can be found in signal(7).</td></tr></tbody></table>以 上资料摘自man page(man 5 core)。不过奇怪了，我的系统上并没有找到core文件。后来，忆起为了渐少系统上的拉圾文件的数量（本人有些洁癖，这也是我喜欢Gentoo的原因 之一），禁止了core文件的生成，查看了以下果真如此，将系统的core文件的大小限制在512K大小，再试:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ ulimit -c<br />0<br />xiaosuo@gentux test $ ulimit -c 1000<br />xiaosuo@gentux test $ ulimit -c<br />1000<br />xiaosuo@gentux test $ ./a.out<br />段错误 (core dumped)<br />xiaosuo@gentux test $ ls<br />a.out&nbsp; core&nbsp; d.c&nbsp; f.c&nbsp; g.c&nbsp; pango.c&nbsp; test_iconv.c&nbsp; test_regex.c<br /></td></tr></tbody></table>core文件终于产生了，用gdb调试一下看看吧:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ gdb ./a.out core<br />GNU gdb 6.5<br />Copyright (C) 2006 Free Software Foundation, Inc.<br />GDB is free software, covered by the GNU General Public License, and you are<br />welcome to change it and/or distribute copies of it under certain conditions.<br />Type "show copying" to see the conditions.<br />There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br />This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br /><br /><br />warning: Can't read pathname for load map: 输入/输出错误.<br />Reading symbols from /lib/libc.so.6...done.<br />Loaded symbols for /lib/libc.so.6<br />Reading symbols from /lib/ld-linux.so.2...done.<br />Loaded symbols for /lib/ld-linux.so.2<br />Core was generated by `./a.out'.<br />Program terminated with signal 11, Segmentation fault.<br />#0&nbsp; 0x08048524 in dummy_function () at d.c:4<br />4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br /></td></tr></tbody></table>哇，好历害，还是一步就定位到了错误所在地，佩服一下Linux/Unix系统的此类设计。<br />接着考虑下去，以前用windows系统下的ie的时侯，有时打开某些网页，会出现&#8220;运行时错误&#8221;，这个时侯如果恰好你的机器上又装有windows的编译器的话，他会弹出来一个对话框，问你是否进行调试，如果你选择是，编译器将被打开，并进入调试状态，开始调试。<br />Linux下如何做到这些呢？我的大脑飞速地旋转着，有了，让它在SIGSEGV的handler中调用gdb，于是第三个方法又诞生了:<br /><span style="font-weight: bold">3.段错误时启动调试:</span><br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;<br />#include &lt;signal.h&gt;<br />#include &lt;string.h&gt;<br /><br />void dump(int signo)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[1024];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char cmd[1024];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FILE *fh;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!(fh = fopen(buf, "r")))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!fgets(buf, sizeof(buf), fh))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fclose(fh);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(buf[strlen(buf) - 1] == '\n')<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf[strlen(buf) - 1] = '\0';<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system(cmd);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void<br />dummy_function (void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br />}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int<br />main (void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal(SIGSEGV, &amp;dump);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />}<br /></td></tr></tbody></table>编译运行效果如下:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic f.c<br />xiaosuo@gentux test $ ./a.out<br />GNU gdb 6.5<br />Copyright (C) 2006 Free Software Foundation, Inc.<br />GDB is free software, covered by the GNU General Public License, and you are<br />welcome to change it and/or distribute copies of it under certain conditions.<br />Type "show copying" to see the conditions.<br />There is absolutely no warranty for GDB.&nbsp; Type "show warranty" for details.<br />This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".<br /><br />Attaching to program: /home/xiaosuo/test/a.out, process 9563<br />Reading symbols from /lib/libc.so.6...done.<br />Loaded symbols for /lib/libc.so.6<br />Reading symbols from /lib/ld-linux.so.2...done.<br />Loaded symbols for /lib/ld-linux.so.2<br />0xffffe410 in __kernel_vsyscall ()<br />(gdb) bt<br />#0&nbsp; 0xffffe410 in __kernel_vsyscall ()<br />#1&nbsp; 0xb7ee4b53 in waitpid () from /lib/libc.so.6<br />#2&nbsp; 0xb7e925c9 in strtold_l () from /lib/libc.so.6<br />#3&nbsp; 0x08048830 in dump (signo=11) at f.c:22<br />#4&nbsp; &lt;signal handler called&gt;<br />#5&nbsp; 0x0804884c in dummy_function () at f.c:31<br />#6&nbsp; 0x08048886 in main () at f.c:38<br /></td></tr></tbody></table>怎么样？是不是依旧很酷？<br />以上方法都是在系统上有gdb的前提下进行的，如果没有呢？其实glibc为我们提供了此类能够dump栈内容的函数簇，详见/usr/include/execinfo.h（这些函数都没有提供man page，难怪我们找不到），另外你也可以通过<a href="http://www.gnu.org/software/libc/manual/html_node/Backtraces.html" target="_blank"><strong><font color="#cc3333">gnu的手册</font></strong></a>进行学习。<br /><span style="font-weight: bold">4.利用backtrace和objdump进行分析:</span><br />重写的代码如下:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>#include &lt;execinfo.h&gt;<br />#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;<br />#include &lt;signal.h&gt;<br /><br />/* A dummy function to make the backtrace more interesting. */<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void<br />dummy_function (void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char *ptr = 0x00;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *ptr = 0x00;<br />}<br /><br />void dump(int signo)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *array[10];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t size;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char **strings;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t i;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size = backtrace (array, 10);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strings = backtrace_symbols (array, size);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("Obtained %zd stack frames.\n", size);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; size; i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("%s\n", strings[i]);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; free (strings);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<br />}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int<br />main (void)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; signal(SIGSEGV, &amp;dump);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dummy_function ();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br />}<br /></td></tr></tbody></table>编译运行结果如下：<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ gcc -g -rdynamic g.c<br />xiaosuo@gentux test $ ./a.out<br />Obtained 5 stack frames.<br />./a.out(dump+0x19) [0x80486c2]<br />[0xffffe420]<br />./a.out(main+0x35) [0x804876f]<br />/lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866]<br />./a.out [0x8048601]<br /></td></tr></tbody></table>这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:<br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>xiaosuo@gentux test $ objdump -d a.out<br /></td></tr></tbody></table><br />
<table style="border-bottom: rgb(153,153,153) 1px solid; border-left: rgb(153,153,153) 1px solid; width: 80%; font-size: 12px; border-top: rgb(153,153,153) 1px solid; border-right: rgb(153,153,153) 1px solid" align="center">
<tbody>
<tr>
<td>&nbsp;8048765:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e8 02 fe ff ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp; 804856c &lt;signal@plt&gt;<br />&nbsp;804876a:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e8 25 ff ff ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp; 8048694 &lt;dummy_function&gt;<br />&nbsp;<span style="color: rgb(255,1,2)">804876f</span>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b8 00 00 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp; $0x0,%eax<br />&nbsp;8048774:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leave<br /></td></tr></tbody></table>我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!<br /><span style="font-weight: bold">后记:</span><br />本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。<br /><br />部分资料来源于xiaosuo @ cnblog.cn, 特此致谢 </p>
<p>作者:upsdn整理 &nbsp; 更新日期:2006-11-03<br />来源:upsdn.net &nbsp; 浏览次数: <br /><br /><br /><br /><strong>其它调试办法</strong></p>
<ul><li><strong>添加日志&nbsp;</strong> </li></ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Linux上的使用开源C++<font color="#cc0033">日志库</font>---log4cplus<strong>&nbsp;</strong></p>
<p><br /><br /><strong>linux下追踪函数调用堆栈<br /></strong><span><br />一般察看函数运行时堆栈的方法是使用</span><span>GDB</span><span>之类的外部调试器</span><span>,</span><span>但是</span><span>,</span><span>有些时候为了分析程序的</span><span>BUG,(</span><span>主要针对长时间运行程序的分析</span><span>),</span><span>在程序出错时打印出函数的调用堆栈是非常有用的。</span></p>
<p>&nbsp;<span>在头文件</span><span>"execinfo.h"</span><span>中声明了三个函数用于获取当前线程的函数调用堆栈</span></p>
<p>&nbsp;<span>Function: int backtrace(void **buffer,int size)</span></p>
<p>&nbsp;<span>该函数用与获取当前线程的调用堆栈</span><span>,</span><span>获取的信息将会被存放在</span><span>buffer</span><span>中</span><span>,</span><span>它是一个指针列表。参数</span><span> size </span><span>用来指定</span><span>buffer</span><span>中可以保存多少个</span><span>void* </span><span>元素。函数返回值是实际获取的指针个数</span><span>,</span><span>最大不超过</span><span>size</span><span>大小</span></p>
<p>&nbsp;<span>在</span><span>buffer</span><span>中的指针实际是从堆栈中获取的返回地址</span><span>,</span><span>每一个堆栈框架有一个返回地址</span></p>
<p>&nbsp;<span>注意某些编译器的优化选项对获取正确的调用堆栈有干扰</span><span>,</span><span>另外内联函数没有堆栈框架</span><span>;</span><span>删除框架指针也会使无法正确解析堆栈内容</span></p>
<p>&nbsp;<span>Function: char ** backtrace_symbols (void *const *buffer, int size)</span></p>
<p>&nbsp;<span>backtrace_symbols</span><span>将从</span><span>backtrace</span><span>函数获取的信息转化为一个字符串数组</span><span>. </span><span>参数</span><span>buffer</span><span>应该是从</span><span>backtrace</span><span>函数获取的数组指针</span><span>,size</span><span>是该数组中的元素个数</span><span>(backtrace</span><span>的返回值</span><span>) </span></p>
<p>&nbsp;<span>函数返回值是一个指向字符串数组的指针</span><span>,</span><span>它的大小同</span><span>buffer</span><span>相同</span><span>.</span><span>每个字符串包含了一个相对于</span><span>buffer</span><span>中对应元素的可打印信息</span><span>.</span><span>它包括函数名，函数的偏移地址</span><span>,</span><span>和实际的返回地址</span></p>
<p>&nbsp;<span>现在</span><span>,</span><span>只有使用</span><span>ELF</span><span>二进制格式的程序和苦衷才能获取函数名称和偏移地址</span><span>.</span><span>在其他系统</span><span>,</span><span>只有</span><span>16</span><span>进制的返回地址能被获取</span><span>.</span><span>另外</span><span>,</span><span>你可能需要传递相应的标志给链接器</span><span>,</span><span>以能支持函数名功能</span><span>(</span><span>比如</span><span>,</span><span>在使用</span><span>GNU ld</span><span>的系统中</span><span>,</span><span>你需要传递</span><span>(-rdynamic))</span></p>
<p>&nbsp;<span>该函数的返回值是通过</span><span>malloc</span><span>函数申请的空间</span><span>,</span><span>因此调用这必须使用</span><span>free</span><span>函数来释放指针</span><span>.</span></p>
<p>&nbsp;<span>注意</span><span>:</span><span>如果不能为字符串获取足够的空间函数的返回值将会为</span><span>NULL</span></p>
<p>&nbsp;<span>Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)</span></p>
<p>&nbsp;<span>backtrace_symbols_fd</span><span>与</span><span>backtrace_symbols </span><span>函数具有相同的功能</span><span>,</span><span>不同的是它不会给调用者返回字符串数组</span><span>,</span><span>而是将结果写入文件描述符为</span><span>fd</span><span>的文件中</span><span>,</span><span>每个函数对应一行</span><span>.</span><span>它不需要调用</span><span>malloc</span><span>函数</span><span>,</span><span>因此适用于有可能调用该函数会失败的情况。</span></p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><span style="color: #000000">#include&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">execinfo.h</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;<br />#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">&nbsp;<br />#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">&nbsp;<br /><br /></span><span style="color: #008000">/*</span><span style="color: #008000">&nbsp;Obtain&nbsp;a&nbsp;backtrace&nbsp;and&nbsp;print&nbsp;it&nbsp;to&nbsp;stdout.&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000">&nbsp;<br /></span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;print_trace&nbsp;(</span><span style="color: #0000ff">void</span><span style="color: #000000">)&nbsp;<br />{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">array[</span><span style="color: #000000">10</span><span style="color: #000000">];&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;size;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">**</span><span style="color: #000000">strings;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;i;&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;backtrace&nbsp;(array,&nbsp;</span><span style="color: #000000">10</span><span style="color: #000000">);&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strings&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;backtrace_symbols&nbsp;(array,&nbsp;size);&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">Obtained&nbsp;%zd&nbsp;stack&nbsp;frames.\n</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;size);&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">for</span><span style="color: #000000">&nbsp;(i&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;&nbsp;i&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;size;&nbsp;i</span><span style="color: #000000">++</span><span style="color: #000000">)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf&nbsp;(</span><span style="color: #000000">"</span><span style="color: #000000">%s\n</span><span style="color: #000000">"</span><span style="color: #000000">,&nbsp;strings[i]);&nbsp;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;(strings);&nbsp;<br />}&nbsp;<br /><br /></span><span style="color: #008000">/*</span><span style="color: #008000">&nbsp;A&nbsp;dummy&nbsp;function&nbsp;to&nbsp;make&nbsp;the&nbsp;backtrace&nbsp;more&nbsp;interesting.&nbsp;</span><span style="color: #008000">*/</span><span style="color: #000000">&nbsp;<br /></span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;dummy_function&nbsp;(</span><span style="color: #0000ff">void</span><span style="color: #000000">)&nbsp;<br />{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print_trace&nbsp;();&nbsp;<br />}&nbsp;<br /><br /></span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;main&nbsp;(</span><span style="color: #0000ff">void</span><span style="color: #000000">)&nbsp;<br />{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dummy_function&nbsp;();&nbsp;<br />&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">0</span><span style="color: #000000">;&nbsp;<br />}</span></div>编译运行的结果如下： <br /># gcc bt.c -rdynamic -o bt <br /># ./bt <br /><script type="text/javascript" src="http://www.upsdn.net/view.php?id=775"></script>Obtained 5 stack frames.<br />./bt(_Z11print_tracev+0x19) [0x804870d]<br />./bt(_Z14dummy_functionv+0xb) [0x8048779]<br />./bt(main+0x16) [0x8048792]<br />/lib/libc.so.6(__libc_start_main+0xdc) [0x116e9c]<br />./bt(__gxx_personality_v0+0x31) [0x8048641]<br /><br /><br />注： addr2line - convert addresses into file names and line numbers.<br /><br /><img src ="http://www.cppblog.com/API/aggbug/201774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-13 20:18 <a href="http://www.cppblog.com/API/archive/2013/07/13/201774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>valgrind使用</title><link>http://www.cppblog.com/API/archive/2013/07/11/201694.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Thu, 11 Jul 2013 08:42:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/11/201694.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201694.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/11/201694.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201694.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201694.html</trackback:ping><description><![CDATA[<div>1.下载<a href="http://valgrind.org/">http://valgrind.org/</a><br />2.解压后进入目录执行./autogen.sh 
<p>3.执行./configure 在此可以指定安装目录加 --prefix=/usr/local指定</p>
<p>4.make</p>
<p>5.make install如果目录没有权限是需要超级用户的<br /><br /></p></div><img src ="http://www.cppblog.com/API/aggbug/201694.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-11 16:42 <a href="http://www.cppblog.com/API/archive/2013/07/11/201694.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll原理</title><link>http://www.cppblog.com/API/archive/2013/07/01/201432.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 11:13:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201432.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201432.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201432.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201432.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201432.html</trackback:ping><description><![CDATA[<font size="2">在linux网络编程中，很长时间使用select做事件触发。select会随着监听fd的数目的增长而降低效率，因为在实现中，它是采用轮询的方式处理的，轮询的fd数目越多，自然耗时就越多。对于IM服务器要支持上万个链接，就显得力不从心了。而且fd支持数目是有限的，在linux/posix/_types.h头文件中，有这样的声明：#defind __FD_SETSIZE 1024.</font> 
<p><font size="2"><span style="font-size: small"><span style="font-size: small">　　</span>epoll则没有这样的限制，epoll支持的最大链接数是最大可打开的文件的数目。epoll只对活跃的socket进行操作&#8212;&#8212;这是因为epoll是根据每个fd上面的callback函数实现的。那么，只有活跃的socket才会主动地调用callback函数。</span><span style="font-size: small">在一个高速的LAN环境，如果几乎所有的socket都是活跃的，epoll的效率比select会稍微有下降。</span></font></p>
<p><span style="font-size: small"><font size="2"><span style="font-size: small">　　</span>使用mmap加速内核与用户空间的消息传递。无论select，poll都需要内核把FD消息通知给用户空间，如何避免不必要的内存拷贝，在这点上epoll通过内核与用户空间mmap同一块内存空间实现的。</font></span></p>
<p><span style="font-size: small"><font size="2"><span style="font-size: small">　　</span>epoll有两种工作方式LT（level triggered）和ET（edge triggered）。</font></span></p>
<p><span style="font-size: small"><font size="2"><span style="font-size: small">　　</span>LT（level triggered）是缺省的工作方式，支持block和non-block socket。在这种模式下，内核告诉你一个文件描述符已经就绪了，然后对这个描述符进行io操作。如果你不作任何操作，内核还会继续通知你的。所以，这种模式编程出错可能性要小一点。传统的select/poll都是这种模型的代表。</font></span></p>
<p><span style="font-size: small"><font size="2"><span style="font-size: small"><span style="font-size: small">　　ET（edge triggered）高速工作方式，只支持no-block描述符。</span></span>在这种模式下，当描述符由未就绪变为就绪时，内核通过epoll告诉你。然后它假设你知道文件描述符已经就绪，并且不再为那个描述符发送更多的就绪通知，直到你做了某些操作导致描述符不再是就绪状态了（比如，你在发送、接收或者接收请求、或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK错误）。但是请注意，如果一直不对这个fd作io操作（从而导致它再次变成未就绪），内核不会发送更多的通知（only once），不过在TCP协议中，ET模式的加速效果仍需要更多benchmark确认。</font></span></p>
<p><font size="2"></font></p>
<p><span style="font-size: medium"><strong><font size="3">epoll接口</font></strong></span></p>
<p><span style="font-size: small"><font size="2">int epoll_create(int size)</font></span></p>
<p><span style="font-size: small"><font size="2">创建一个epoll句柄，size告诉内核这个监听的数目一共有多大。这个参数不同于select的第一个参数，给出最大的fd+1值。需要注意的是，当创建好epoll句柄后，它就会占用一个fd值，在linux下如果查看/proc/prodid/fd/能够看到这个fd的，所以使用完epoll后，必须调用close关闭。</font></span></p>
<p><font size="2"></font></p>
<p><span style="font-size: small"><font size="2">int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)</font></span></p>
<p><span style="font-size: small"><font size="2">epoll事件注册函数，它不同于select是在监听事件时告诉内核要监听什么类型的事件，而是在这里先注册要监听的事件类型。</font></span></p>
<p><span style="font-size: small"><font size="2">epfd：epoll描述符</font></span></p>
<p><span style="font-size: small"><font size="2">op：EPOLL_CTL_ADD 注册新的fd</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLL_CTL_MOD 修改已注册的fd的监听事件</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLL_CTL_DEL 从epfd中删除一个fd</font></span></p>
<p><span style="font-size: small"><font size="2">struct epoll_event {</font></span></p>
<p><span style="font-size: small"><font size="2">__uint32_t event; /* EPOLLIN 可以读（包括对端socket正常关闭）</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLOUT 可以写</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLPRI 有紧急数据可读（这里应该表示有带外数据到来）</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLERR 对应文件描述符发生错误</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLHUP 对应的文件描述符被挂断</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLET ET工作模式</font></span></p>
<p><span style="font-size: small"><font size="2">EPOLLONESHOT 只监听一次事件，当监听完这次事件后，还需要继续监听的话，需要再次把fd加入到监听队列里。</font></span></p>
<p><span style="font-size: small"><font size="2">epoll_data_t data; /* user data */</font></span></p>
<p><span style="font-size: small"><font size="2">};</font></span></p>
<p><font size="2"></font></p>
<p><span style="font-size: small"><strong><font size="2"><span style="font-family: 宋体">typedef union epoll_data { <br />void ptr; <br />int fd; <br />__uint32_t u32; <br />__uint64_t u64; <br />} epoll_data_t; </span><br /></font></strong></span></p>
<p><font size="2"></font></p>
<p><span style="font-size: small"><font size="2">int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)</font></span></p>
<p><span style="font-size: small"><font size="2">等待事件的产生，类似于select。</font></span></p>
<p><span style="font-size: small"><font size="2">events，从内核得到事件的总和</font></span></p>
<p><span style="font-size: small"><font size="2">maxevents，告诉内核这个events有多大，不能大于epoll_create size 值</font></span></p>
<p><span style="font-size: small"><font size="2">timeout，超时时间（毫秒）。0：立即返回，-1 不确定或永久阻塞</font></span></p>
<p><span style="font-size: small"><font size="2">返回，0：超时；否则 事件个数</font></span></p>
<p><font size="2"></font></p>
<p><span style="font-size: small"><font size="2">假如有这样一个例子：<br />1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符<br />2. 这个时候从管道的另一端被写入了2KB的数据<br />3. 调用epoll_wait(2)，并且它会返回RFD，说明它已经准备好读取操作<br />4. 然后我们读取了1KB的数据<br />5. 调用epoll_wait(2)......<br /><br />Edge Triggered 工作模式：<br />如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志，那么在第5步调用epoll_wait(2)之后将有可能会挂起，因为剩余的数据还存在于文件的输入缓冲区内，而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候，调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。在上面的例子中，会有一个事件产生在RFD句柄上，因为在第2步执行了一个写操作，然后，事件将会在第3步被销毁。因为第4步的读取操作没有读空文件输入缓冲区内的数据，因此我们在第5步调用 epoll_wait(2)完成后，是否挂起是不确定的。epoll工作在ET模式的时候，必须使用非阻塞套接口，以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口，在后面会介绍避免可能的缺陷。<br />i 基于非阻塞文件句柄<br />ii 只有当read(2)或者write(2)返回EAGAIN时才需要挂起，等待。<span style="color: rgb(0,1,255); font-weight: bold">但这并不是说每次read()时都需要循环读，直到读到产生一个EAGAIN才认为此次事件处理完成，当read()返回的读到的数据长度小于请求的数据长度时，就可以确定此时缓冲中已没有数据了，也就可以认为此事读事件已处理完成。</span><br /><br />另外，当使用epoll的ET模型来工作时，当产生了一个EPOLLIN事件后，<br /><span style="color: rgb(255,1,2)">读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小，那么很有可能是缓冲区还有数据未读完，也意味着该次事件还没有处理完，所以还需要再次读取</span>：<br />while(rs)<br />{<br />buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);<br />if(buflen &lt; 0)<br />{<br />// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读<br />// 在这里就当作是该次事件已处理处.<br />if(errno == EAGAIN)<br />break;<br />else<br />return;<br />}<br />else if(buflen == 0)<br />{<br />// 这里表示对端的socket已正常关闭.<br />}<br /><span style="color: rgb(255,1,2)">if(buflen == sizeof(buf)</span><br style="color: rgb(255,1,2)" /><span style="color: rgb(255,1,2)">rs = 1; // 需要再次读取</span><br />else<br />rs = 0;<br />}<br /><br /><br /><span style="color: rgb(255,1,2); font-weight: bold">还有，假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发，当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回，返回-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.</span><br /><br />ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)<br />{<br />ssize_t tmp;<br />size_t total = buflen;<br />const char *p = buffer;<br /><br />while(1)<br />{<br />tmp = send(sockfd, p, total, 0);<br />if(tmp &lt; 0)<br />{<br />// 当send收到信号时,可以继续写,但这里返回-1.<br />if(errno == EINTR)<br />return -1;<br /><br />// 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,<br />// 在这里做延时后再重试.<br />if(errno == EAGAIN)<br />{<br />usleep(1000);<br />continue;<br />}<br /><br />return -1;<br />}<br /><br />if((size_t)tmp == total)<br />return buflen;<br /><br />total -= tmp;<br />p += tmp;<br />}<br /><br />return tmp;<br />} </font></span></p><img src ="http://www.cppblog.com/API/aggbug/201432.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 19:13 <a href="http://www.cppblog.com/API/archive/2013/07/01/201432.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll 经验谈 </title><link>http://www.cppblog.com/API/archive/2013/07/01/201431.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 11:02:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201431.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201431.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201431.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201431.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201431.html</trackback:ping><description><![CDATA[<span style="font-family: 宋体; font-size: small"><font size="2">1、首先需要一个内存池，目的在于：<br />&#183;减少频繁的分配和释放，提高性能的同时，还能避免内存碎片的问题；<br />&#183;能够存储变长的数据，不要很傻瓜地只能预分配一个最大长度；<br />&#183;基于SLAB算法实现内存池是一个好的思路：分配不同大小的多个块，请求时返回大于请求长度的最小块即可，对于容器而言，处理固定块的分配和回收，相当容易实现。当然，还要记得需要设计成线程安全的，自旋锁比较好，使用读写自旋锁就更好了。<br />&#183;分配内容的增长管理是一个问题，比如第一次需要1KB空间，随着数据源源不断的写入，第二次就需要4KB空间了。扩充空间容易实现，可是扩充的时候必然 涉及数据拷贝。甚至，扩充的需求很大，上百兆的数据，这样就不好办了。暂时没更好的想法，可以像STL一样，指数级增长的分配策略，拷贝数据虽不可避免， 但是起码重分配的几率越来越小了。<br />&#183;上面提到的，如果是上百兆的数据扩展需要，采用内存映射文件来管理是一个好的办法：映射文件后，虽然占了很大的虚拟内存，但是物理内存仅在写入的时候才会被分配，加上madvice()来加上顺序写的优化建议后，物理内存的消耗也会变小。<br />&#183;用string或者vector去管理内存并不明智，虽然很简单，但服务器软件开发中不适合使用STL，特别是对稳定性和性能要求很高的情况下。<br /><br />2、第二个需要考虑的是对象池，与内存池类似：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;减少对象的分配和释放。其实C++对象也就是struct，把构造和析构脱离出来手动初始化和清理，保持对同一个缓冲区的循环利用，也就不难了。<br />&#183;可以设计为一个对象池只能存放一种对象，则对象池的实现实际就是固定内存块的池化管理，非常简单。毕竟，对象的数量非常有限。<br /><br />3、第三个需要的是队列：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;如果可以预料到极限的处理能力，采用固定大小的环形队列来作为缓冲区是比较不错的。一个生产者一个消费者是常见的应用场景，环形队列有其经典的&#8220;锁无关&#8221;算法，在一个线程读一个线程写的场景下，实现简单，性能还高，还不涉及资源的分配和释放。好啊，实在是好！<br />&#183;涉及多个生产者消费者的时候，tbb::concurent_queue是不错的选择，线程安全，并发性也好，就是不知道资源的分配释放是否也管理得足够好。<br /><br />4、第四个需要的是映射表，或者说hash表：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;因为epoll是事件触发的，而一系列的流程可能是分散在多个事件中的，因此，必须保留下中间状态，使得下一个事件触发的时候，能够接着上次处理的位置继续处理。要简单的话，STL的hash_map还行，不过得自己处理锁的问题，多线程环境下使用起来很麻烦。<br />&#183;多线程环境下的hash表，最好的还是tbb::concurent_hash_map。<br /><br />5、核心的线程是事件线程：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;事件线程是调用epoll_wait()等待事件的线程。例子代码里面，一个线程干了所有的事情，而需要开发一个高性能的服务器的时候，事件线程应该专注于事件本身的处理，将触发事件的socket句柄放到对应的处理队列中去，由具体的处理线程负责具体的工作。<br /><br />6、accept()单独一个线程：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;服务端的socket句柄（就是调用bind()和listen()的这个）最好在单独的一个线程里面做accept()，阻塞还是非阻塞都无所谓，相比整个服务器的通讯，用户接入的动作只是很小一部分。而且，accept()不放在事件线程的循环里面，减少了判断。<br /><br />7、接收线程单独一个：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;接收线程从发生EPOLLIN事件的队列中取出socket句柄，然后在这个句柄上调用recv接收数据，直到缓冲区没有数据为止。接收到的数据写入以socket为键的hash表中，hash表中有一个自增长的缓冲区，保存了客户端发过来的数据。<br />&#183;这样的处理方式适合于客户端发来的数据很小的应用，比如HTTP服务器之类；假设是文件上传的服务器，则接受线程会一直处理某个连接的海量数据，其他客户端的数据处理产生了饥饿。所以，如果是文件上传服务器一类的场景，就不能这样设计。<br /><br />8、发送线程单独一个：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;发送线程从发送队列获取需要发送数据的SOCKET句柄，在这些句柄上调用send()将数据发到客户端。队列中指保存了SOCKET句柄，具体的信息 还需要通过socket句柄在hash表中查找，定位到具体的对象。如同上面所讲，客户端信息的对象不但有一个变长的接收数据缓冲区，还有一个变长的发送 数据缓冲区。具体的工作线程发送数据的时候并不直接调用send()函数，而是将数据写到发送数据缓冲区，然后把SOCKET句柄放到发送线程队列。<br />&#183;SOCKET句柄放到发送线程队列的另一种情况是：事件线程中发生了EPOLLOUT事件，说明TCP的发送缓冲区又有了可用的空间，这个时候可以把SOCKET句柄放到发送线程队列，一边触发send()的调用；<br />&#183;需要注意的是：发送线程发送大量数据的时候，当频繁调用send()直到TCP的发送缓冲区满后，便无法再发送了。这个时候如果循环等待，则其他用户的 发送工作受到影响；如果不继续发送，则EPOLL的ET模式可能不会再产生事件。解决这个问题的办法是在发送线程内再建立队列，或者在用户信息对象上设置 标志，等到线程空闲的时候，再去继续发送这些未发送完成的数据。<br /><br />9、需要一个定时器线程：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;一位将epoll使用的高手说道：&#8220;单纯靠epoll来管理描述符不泄露几乎是不可能的。完全解决方案很简单，就是对每个fd设置超时时间，如果超过timeout的时间，这个fd没有活跃过，就close掉&#8221;。<br />&#183;所以，定时器线程定期轮训整个hash表，检查socket是否在规定的时间内未活动。未活动的SOCKET认为是超时，然后服务器主动关闭句柄，回收资源。<br /><br />10、多个工作线程：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;工作线程由接收线程去触发：每次接收线程收到数据后，将有数据的SOCKET句柄放入一个工作队列中；工作线程再从工作队列获取SOCKET句柄，查询hash表，定位到用户信息对象，处理业务逻辑。<br />&#183;工作线程如果需要发送数据，先把数据写入用户信息对象的发送缓冲区，然后把SOCKET句柄放到发送线程队列中去。<br />&#183;对于任务队列，接收线程是生产者，多个工作线程是消费者；对于发送线程队列，多个工作线程是生产者，发送线程是消费者。在这里需要注意锁的问题，如果采用tbb::concurrent_queue，会轻松很多。<br /><br />11、仅仅只用scoket句柄作为hash表的键，并不够：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;假设这样一种情况：事件线程刚把某SOCKET因发生EPOLLIN事件放入了接收队列，可是随即客户端异常断开了，事件线程又因为EPOLLERR事 件删除了hash表中的这一项。假设接收队列很长，发生异常的SOCKET还在队列中，等到接收线程处理到这个SOCKET的时候，并不能通过 SOCKET句柄索引到hash表中的对象。<br />&#183;索引不到的情况也好处理，难点就在于，这个SOCKET句柄立即被另一个客户端使用了，接入线程为这个SCOKET建立了hash表中的某个对象。此时，句柄相同的两个SOCKET，其实已经是不同的两个客户端了。极端情况下，这种情况是可能发生的。<br />&#183;解决的办法是，使用socket fd + sequence为hash表的键，sequence由接入线程在每次accept()后将一个整型值累加而得到。这样，就算SOCKET句柄被重用，也不会发生问题了。<br /><br />12、监控，需要考虑：</font></span> <span style="font-family: 宋体; font-size: small"><br /><font size="2">&#183;框架中最容易出问题的是工作线程：工作线程的处理速度太慢，就会使得各个队列暴涨，最终导致服务器崩溃。因此必须要限制每个队列允许的最大大小，且需要监视每个工作线程的处理时间，超过这个时间就应该采用某个办法结束掉工作线程。</font></span><img src ="http://www.cppblog.com/API/aggbug/201431.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 19:02 <a href="http://www.cppblog.com/API/archive/2013/07/01/201431.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux非阻塞的socket EAGAIN的错误处理</title><link>http://www.cppblog.com/API/archive/2013/07/01/201430.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201430.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201430.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201430.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201430.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201430.html</trackback:ping><description><![CDATA[<span style="widows: 2; border-spacing: 0px; border-collapse: separate; font-family: Tahoma; orphans: 2; font-size: 14px"><span style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">在Linux中使用非阻塞的socket的情形下。</span></span> 
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">（一）发送时</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">　　当客户通过Socket提供的send函数发送大的数据包时，就可能返回一个EAGAIN的错误。该错误产生的原因是由于send 函数中的size变量大小超过了tcp_sendspace的值。tcp_sendspace定义了应用在调用send之前能够在kernel中缓存的数据量。当应用程序在socket中设置了O_NDELAY或者O_NONBLOCK属性后，如果发送缓存被占满，send就会返回EAGAIN的错误。 <br /></p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">　　为了消除该错误，有三种方法可以选择： <br />　　1.调大tcp_sendspace，使之大于send中的size参数 <br />　　---no -p -o tcp_sendspace=65536 <br /><br />　　2.在调用send前，在setsockopt函数中为SNDBUF设置更大的值 <br /><br />　　3.使用write替代send，因为write没有设置O_NDELAY或者O_NONBLOCK</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">（二）接收时</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">接收数据时常遇到Resource temporarily unavailable的提示，errno代码为11(EAGAIN)。这表明你在非阻塞模式下调用了阻塞操作，在该操作没有完成就返回这个错误，这个错误不会破坏socket的同步，不用管它，下次循环接着recv就可以。对非阻塞socket而言，EAGAIN不是一种错误。在VxWorks和Windows上，EAGAIN的名字叫做EWOULDBLOCK。其实这算不上错误，只是一种异常而已。</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">　　另外，如果出现EINTR即errno为4，错误描述Interrupted system call，操作也应该继续。</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">　　最后，如果recv的返回值为0，那表明对方已将连接断开，我们的接收操作也应该结束。</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">（三）以下是另一种解释</p>
<p style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px">假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发，当缓冲区满后会产生EAGAIN错误(参考man send),同时,不理会这次请求发送的数据.所以,</p>
<div style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px"><span style="font-weight: bold"><span style="color: rgb(0,0,255)"></span>需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回，返回-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.</span></div>
<div style="line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px"><span style="font-weight: bold">这种方法类似于readn和writen的封装(自己写过，在《UNIX环境高级编程》中也有介绍)</span><br /><br />
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/tianmo2010/article/details/8691644#"><u><font color="#000080">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/tianmo2010/article/details/8691644#"><u><font color="#000080">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/tianmo2010/article/details/8691644#"><u><font color="#000080">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/tianmo2010/article/details/8691644#"><u><font color="#000080">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span class="datatypes">size_t</span><span> socket_send(</span><span class="datatypes">int</span><span> sockfd, </span><span class="keyword">const</span><span> </span><span class="datatypes">char</span><span>* buffer, </span><span class="datatypes">size_t</span><span> buflen) </span></li><li><span>{ </span></li><li class="alt"><span class="datatypes">size_t</span><span> tmp; </span></li><li><span class="datatypes">size_t</span><span> total = buflen; </span></li><li class="alt"><span class="keyword">const</span><span> </span><span class="datatypes">char</span><span> *p = buffer; </span></li><li><span></span></li><li class="alt"><span class="keyword">while</span><span>(1) </span></li><li><span>{ </span></li><li class="alt"><span>tmp = send(sockfd, p, total, 0); </span></li><li><span></span></li><li class="alt"><span class="keyword">if</span><span>(tmp &lt; 0) </span></li><li><span>{ </span></li><li class="alt"><span class="comment">// 当send收到信号时,可以继续写,但这里返回-1.</span><span> </span></li><li><span class="keyword">if</span><span>(errno == EINTR) </span></li><li class="alt"><span>{ </span></li><li><span class="keyword">return</span><span> -1; </span></li><li class="alt"><span>} </span></li><li><span></span></li><li class="alt"><span class="comment">// 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,</span><span> </span></li><li><span class="comment">// 在这里做延时后再重试.</span><span> </span></li><li class="alt"><span class="keyword">if</span><span>(errno == EAGAIN) </span></li><li><span>{ </span></li><li class="alt"><span>usleep(1000); </span></li><li><span class="keyword">continue</span><span>; </span></li><li class="alt"><span>} </span></li><li><span></span></li><li class="alt"><span class="keyword">return</span><span> -1; </span></li><li><span>} </span></li><li class="alt"><span></span></li><li><span class="keyword">if</span><span>((</span><span class="datatypes">size_t</span><span>)tmp == total) </span></li><li class="alt"><span>{ </span></li><li><span class="keyword">return</span><span> buflen; </span></li><li class="alt"><span>} </span></li><li><span></span></li><li class="alt"><span>total -= tmp; </span></li><li><span>p += tmp; </span></li><li class="alt"><span>} </span></li><li><span></span></li><li class="alt"><span class="keyword">return</span><span> tmp; </span></li><li><span>} </span></li><li class="alt"><span></span></li></ol></div><pre style="display: none" class="cpp" name="code">size_t socket_send(int sockfd, const char* buffer, size_t buflen)
{
	size_t tmp;
	size_t total = buflen;
	const char *p = buffer;

	while(1)
	{
		tmp = send(sockfd, p, total, 0);

		if(tmp &lt; 0)
		{
			// 当send收到信号时,可以继续写,但这里返回-1.
			if(errno == EINTR)
			{
				return -1;
			}

			// 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,
			// 在这里做延时后再重试.
			if(errno == EAGAIN)
			{
				usleep(1000);
				continue;
			}

			return -1;
		}

		if((size_t)tmp == total)
		{
			return buflen;
		}

		total -= tmp;
		p += tmp;
	}

	return tmp;
}
 </pre><br /></div><img src ="http://www.cppblog.com/API/aggbug/201430.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 18:53 <a href="http://www.cppblog.com/API/archive/2013/07/01/201430.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll完整实例</title><link>http://www.cppblog.com/API/archive/2013/07/01/201424.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 07:38:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201424.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201424.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201424.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201424.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201424.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#include&nbsp;&lt;deque&gt;#include&nbsp;&lt;map&gt;#include&nbsp;&lt;vector&gt;#include&nbsp;&lt;pthr...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/07/01/201424.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/201424.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 15:38 <a href="http://www.cppblog.com/API/archive/2013/07/01/201424.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll调用</title><link>http://www.cppblog.com/API/archive/2013/07/01/201420.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 06:35:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201420.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201420.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201420.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201420.html</trackback:ping><description><![CDATA[<h2 class="title content-title">epoll调用</h2>
<div id="content" class="content mod-cs-content text-content clearfix">在linux的网络编程中，很长的时间都在使用select来做事件触发。在linux新的内核中，有了一种替换它的机制，就是epoll。<br />相比于select，epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中，它是采用轮询来处理的，轮询的fd数目越多，自然耗时越多。并且，在linux/posix_types.h头文件有这样的声明：<br />#define __FD_SETSIZE 1024<br />表示select最多同时监听1024个fd，当然，可以通过修改头文件再重编译内核来扩大这个数目，但这似乎并不治本。<br /><br />epoll的接口非常简单，一共就三个函数：<br />1. int epoll_create(int size);<br />创建一个epoll的句柄，size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数，给出最大监听的fd+1的值。需要注意的是，当创建好epoll句柄后，它就是会占用一个fd值，在linux下如果查看/proc/进程id/fd/，是能够看到这个fd的，所以在使用完epoll后，必须调用close()关闭，否则可能导致fd被耗尽。<br /><br /><br />2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);<br />epoll的事件注册函数，它不同与select()是在监听事件时告诉内核要监听什么类型的事件，而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值，第二个参数表示动作，用三个宏来表示：<br />EPOLL_CTL_ADD：注册新的fd到epfd中；<br />EPOLL_CTL_MOD：修改已经注册的fd的监听事件；<br />EPOLL_CTL_DEL：从epfd中删除一个fd；<br />第三个参数是需要监听的fd，第四个参数是告诉内核需要监听什么事，struct epoll_event结构如下：<br />struct epoll_event {<br />__uint32_t events; /* Epoll events */<br />epoll_data_t data; /* User data variable */<br />};<br /><br />events可以是以下几个宏的集合：<br />EPOLLIN ：表示对应的文件描述符可以读（包括对端SOCKET正常关闭）；<br />EPOLLOUT：表示对应的文件描述符可以写；<br />EPOLLPRI：表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）；<br />EPOLLERR：表示对应的文件描述符发生错误；<br />EPOLLHUP：表示对应的文件描述符被挂断；<br />EPOLLET： 将EPOLL设为边缘触发(Edge Triggered)模式，这是相对于水平触发(Level Triggered)来说的。<br />EPOLLONESHOT：只监听一次事件，当监听完这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里<br /><br /><br />3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);<br />等待事件的产生，类似于select()调用。参数events用来返回从内核得到事件的集合，maxevents告之内核这个events有多大，这个maxevents的值不能大于创建epoll_create()时的size，参数timeout是超时时间（毫秒，0会立即返回，-1将不确定，也有说法说是永久阻塞）。该函数返回需要处理的事件数目，如返回0表示已超时。<br /><br />使用epoll的注意事项 <br />1. ET模式比LT模式高效，但比较难控制。 <br />2. 如果某个句柄期待的事件不变，不需要EPOLL_CTL_MOD，但每次读写后将该句柄modify一次有助于提高稳定性，特别在ET模式。 <br />3. socket关闭后最好将该句柄从epoll中delete（EPOLL_CTL_DEL），虽然epoll自身有处理，但会使epoll的hash的节点数增多，影响搜索hash的速度。 </div><img src ="http://www.cppblog.com/API/aggbug/201420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 14:35 <a href="http://www.cppblog.com/API/archive/2013/07/01/201420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux 套接字编程中的 5 个隐患</title><link>http://www.cppblog.com/API/archive/2013/07/01/201418.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 01 Jul 2013 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/07/01/201418.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201418.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/07/01/201418.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201418.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201418.html</trackback:ping><description><![CDATA[<p>在 4.2 BSD UNIX&#174; 操作系统中首次引入，Sockets API 现在是任何操作系统的标准特性。事实上，很难找到一种不支持 Sockets API 的现代语言。该 API 相当简单，但新的开发人员仍然会遇到一些常见的隐患。</p>
<p>本文识别那些隐患并向您显示如何避开它们。</p>
<p><a name="N10057"><span class="atitle">隐患 1．忽略返回状态</span></a></p>
<p>第一个隐患很明显，但它是开发新手最容易犯的一个错误。如果您忽略函数的返回状态，当它们失败或部分成功的时候，您也许会迷失。反过来，这可能传播错误，使定位问题的源头变得困难。</p>
<p>捕获并检查每一个返回状态，而不是忽略它们。考虑清单 1 显示的例子，一个套接字 <code>send</code> 函数。</p><br /><a name="N1006B"><strong>清单 1. 忽略 API 函数返回状态</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">int status, sock, mode;
/* Create a new stream (TCP) socket */
sock = <strong>socket</strong>( AF_INET, SOCK_STREAM, 0 );
...
status = <strong>send</strong>( sock, buffer, buflen, MSG_DONTWAIT );
if (status == -1) {
  /* send failed */
  printf( "send failed: %s\n", strerror(errno) );
} else {
  /* send succeeded -- or did it? */
}
</pre></td></tr></tbody></table><br />
<p>清单 1 探究一个函数片断，它完成套接字 <code>send</code> 操作（通过套接字发送数据）。函数的错误状态被捕获并测试，但这个例子忽略了 <code>send</code> 在无阻塞模式（由 <code>MSG_DONTWAIT</code> 标志启用）下的一个特性。</p>
<p><code>send</code> API 函数有三类可能的返回值：</p>
<ul><li>如果数据成功地排到传输队列，则返回 0。</li><li>如果排队失败，则返回 -1（通过使用 <code>errno</code> 变量可以了解失败的原因）。</li><li>如果不是所有的字符都能够在函数调用时排队，则最终的返回值是发送的字符数。</li></ul>
<p>由于 <code>send</code> 的 <code>MSG_DONTWAIT</code> 变量的无阻塞性质，函数调用在发送完所有的数据、一些数据或没有发送任何数据后返回。在这里忽略返回状态将导致不完全的发送和随后的数据丢失。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N100A7"><span class="atitle">隐患 2．对等套接字闭包</span></a></p>
<p>UNIX 有趣的一面是您几乎可以把任何东西看成是一个文件。文件本身、目录、管道、设备和套接字都被当作文件。这是新颖的抽象，意味着一整套的 API 可以用在广泛的设备类型上。</p>
<p>考虑 <code>read</code> API 函数，它从文件读取一定数量的字节。<code>read</code> 函数返回读取的字节数（最高为您指定的最大值）；或者 -1，表示错误；或者 0，如果已经到达文件末尾。</p>
<p>如果在一个套接字上完成一个 <code>read</code> 操作并得到一个为 0 的返回值，这表明远程套接字端的对等层调用了 <code>close</code> API 方法。该指示与文件读取相同 &#8212;&#8212; 没有多余的数据可以通过描述符读取（参见 清单 2）。</p><br /><a name="N100CA"><strong>清单 2．适当处理 read API 函数的返回值</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">int sock, status;
sock = <strong>socket</strong>( AF_INET, SOCK_STREAM, 0 );
...
status = <strong>read</strong>( sock, buffer, buflen );
if (status &gt; 0) {
  /* Data read from the socket */
} else if (status == -1) {
  /* Error, check errno, take action... */
} else if (status == 0) {
  /* Peer closed the socket, finish the close */
  <strong>close</strong>( sock );
  /* Further processing... */
}
</pre></td></tr></tbody></table><br />
<p>同样，可以用 <code>write</code> API 函数来探测对等套接字的闭包。在这种情况下，接收 <code>SIGPIPE</code> 信号，或如果该信号阻塞，<code>write</code> 函数将返回 -1 并设置 <code>errno</code> 为 <code>EPIPE</code>。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N100F0"><span class="atitle">隐患 3．地址使用错误（EADDRINUSE）</span></a></p>
<p>您可以使用 <code>bind</code> API 函数来绑定一个地址（一个接口和一个端口）到一个套接字端点。可以在服务器设置中使用这个函数，以便限制可能有连接到来的接口。也可以在客户端设置中使用这个函数，以便限制应当供出去的连接所使用的接口。<code>bind</code> 最常见的用法是关联端口号和服务器，并使用通配符地址（<code>INADDR_ANY</code>），它允许任何接口为到来的连接所使用。</p>
<p><code>bind</code> 普遍遭遇的问题是试图绑定一个已经在使用的端口。该陷阱是也许没有活动的套接字存在，但仍然禁止绑定端口（<code>bind</code> 返回 <code>EADDRINUSE</code>），它由 TCP 套接字状态 <code>TIME_WAIT</code> 引起。该状态在套接字关闭后约保留 2 到 4 分钟。在 <code>TIME_WAIT</code> 状态退出之后，套接字被删除，该地址才能被重新绑定而不出问题。</p>
<p>等待 <code>TIME_WAIT</code> 结束可能是令人恼火的一件事，特别是如果您正在开发一个套接字服务器，就需要停止服务器来做一些改动，然后重启。幸运的是，有方法可以避开 <code>TIME_WAIT</code> 状态。可以给套接字应用 <code>SO_REUSEADDR</code> 套接字选项，以便端口可以马上重用。</p>
<p>考虑清单 3 的例子。在绑定地址之前，我以 <code>SO_REUSEADDR</code> 选项调用 <code>setsockopt</code>。为了允许地址重用，我设置整型参数（<code>on</code>）为 1 （不然，可以设为 0 来禁止地址重用）。 </p><br /><a name="N1013D"><strong>清单 3．使用 SO_REUSEADDR 套接字选项避免地址使用错误</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">int sock, ret, on;
struct sockaddr_in servaddr;
/* Create a new stream (TCP) socket */
sock = <strong>socket</strong>( AF_INET, SOCK_STREAM, 0 ):
/* Enable address reuse */
<em>on = 1;
ret = <strong>setsockopt</strong>( sock, SOL_SOCKET, SO_REUSEADDR, &amp;on, sizeof(on) );</em>
/* Allow connections to port 8080 from any available interface */
memset( &amp;servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = <strong>htonl</strong>( INADDR_ANY );
servaddr.sin_port = <strong>htons</strong>( 45000 );
/* Bind to the address (interface/port) */
ret = <strong>bind</strong>( sock, (struct sockaddr *)&amp;servaddr, sizeof(servaddr) );
</pre></td></tr></tbody></table><br />
<p>在应用了 <code>SO_REUSEADDR</code> 选项之后，<code>bind</code> API 函数将允许地址的立即重用。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N10160"><span class="atitle">隐患 4．发送结构化数据</span></a></p>
<p>套接字是发送无结构二进制字节流或 ASCII 数据流（比如 HTTP 上的 HTTP 页面，或 SMTP 上的电子邮件）的完美工具。但是如果试图在一个套接字上发送二进制数据，事情将会变得更加复杂。</p>
<p>比如说，您想要发送一个整数：您可以肯定，接收者将使用同样的方式来解释该整数吗？运行在同一架构上的应用程序可以依赖它们共同的平台来对该类型的数据做出相同的解释。但是，如果一个运行在高位优先的 IBM PowerPC 上的客户端发送一个 32 位的整数到一个低位优先的 Intel x86，那将会发生什么呢？字节排列将引起不正确的解释。</p>
<div class="ibm-container ibm-alt-header dw-container-sidebar">
<h2>字节交换还是不呢？</h2>
<div class="ibm-container-body">
<p><em>Endianness</em> 是指内存中字节的排列顺序。<em>高位优先（big endian）</em> 按最高有效字节在前排列，然而 <em>低位优先（little endian）</em> 按照最低有效字节在前排序。</p>
<p>高位优先架构（比如 PowerPC&#174;）比低位优先架构（比如 Intel&#174; Pentium&#174; 系列，其网络字节顺序是高位优先）有优势。这意味着，对高位优先的机器来说，在 TCP/IP 内控制数据是自然有序的。低位优先架构要求字节交换 &#8212;&#8212; 对网络应用程序来说，这是一个轻微的性能弱点。</p></div></div>
<p>通过套接字发送一个 C 结构会怎么样呢？这里，也会遇到麻烦，因为不是所有的编译器都以相同的方式排列一个结构的元素。结构也可能被压缩以便使浪费的空间最少，这进一步使结构中的元素错位。</p>
<p>幸好，有解决这个问题的方案，能够保证两端数据的一致解释。过去，远程过程调用（Remote Procedure Call，RPC）套装工具提供所谓的外部数据表示（External Data Representation，XDR）。XDR 为数据定义一个标准的表示来支持异构网络应用程序通信的开发。</p>
<p>现在，有两个新的协议提供相似的功能。可扩展标记语言/远程过程调用（XML/RPC）以 XML 格式安排 HTTP 上的过程调用。数据和元数据用 XML 进行编码并作为字符串传输，并通过主机架构把值和它们的物理表示分开。SOAP 跟随 XML-RPC，以更好的特性和功能扩展了它的思想。参见 <a href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#resources">参考资料</a> 小节，获取更多关于每个协议的信息。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N10190"><span class="atitle">隐患 5．TCP 中的帧同步假定</span></a></p>
<p>TCP 不提供帧同步，这使得它对于面向字节流的协议是完美的。这是 TCP 与 UDP（User Datagram Protocol，用户数据报协议）的一个重要区别。UDP 是面向消息的协议，它保留发送者和接收者之间的消息边界。TCP 是一个面向流的协议，它假定正在通信的数据是无结构的，如图 1 所示。</p><br /><a name="N1019B"><strong>图 1．UDP 的帧同步能力和缺乏帧同步的 TCP</strong></a><br /><img alt="帧同步能力" src="http://www.ibm.com/developerworks/cn/linux/l-sockpit/figure1.gif" width="565" height="503" /><br />
<p>图 1 的上部说明一个 UDP 客户端和服务器。左边的对等层完成两个套接字的写操作，每个 100 字节。协议栈的 UDP 层追踪写的数量，并确保当右边的接收者通过套接字获取数据时，它以同样数量的字节到达。换句话说，为读者保留了写者提供的消息边界。</p>
<p>现在，看图 1 的底部．它为 TCP 层演示了相同粒度的写操作。两个独立的写操作（每个 100 字节）写入流套接字。但在本例中，流套接字的读者得到的是 200 字节。协议栈的 TCP 层聚合了两次写操作。这种聚合可以发生在 TCP/IP 协议栈的发送者或接收者中任何一方。重要的是，要注意到聚合也许不会发生 &#8212;&#8212; TCP 只保证数据的有序发送。</p>
<p>对大多数开发人员来说，该陷阱会引起困惑。您想要获得 TCP 的可靠性和 UDP 的帧同步。除非改用其他的传输协议，比如流传输控制协议（STCP），否则就要求应用层开发人员来实现缓冲和分段功能。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N101B2"><span class="atitle">调试套接字应用程序的工具</span></a></p>
<p>GNU/Linux 提供几个工具，它们可以帮助您发现套接字应用程序中的一些问题。此外，使用这些工具还有教育意义，而且能够帮助解释应用程序和 TCP/IP 协议栈的行为。在这里，您将看到对几个工具的概述。查阅下面的 <a href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#resources">参考资料</a> 了解更多的信息。</p>
<p><a name="N101BF"><span class="smalltitle">查看网络子系统的细节</span></a></p>
<p><code>netstat</code> 工具提供查看 GNU/Linux 网络子系统的能力。使用 <code>netstat</code>，可以查看当前活动的连接（按单个协议进行查看），查看特定状态的连接（比如处于监听状态的服务器套接字）和许多其他的信息。清单 4 显示了 <code>netstat</code> 提供的一些选项和它们启用的特性。</p><br /><a name="N101D6"><strong>清单 4．netstat 实用程序的用法模式</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">View all TCP sockets currently active
$ netstat --tcp
View all UDP sockets
$ netstat --udp
View all TCP sockets in the listening state
$ netstat --listening
View the multicast group membership information
$ netstat --groups
Display the list of masqueraded connections
$ netstat --masquerade
View statistics for each protocol
$ netstat --statistics
</pre></td></tr></tbody></table><br />
<p>尽管存在许多其他的实用程序，但 <code>netstat</code> 的功能很全面，它覆盖了 <code>route</code>、<code>ifconfig</code> 和其他标准 GNU/Linux 工具的功能。</p>
<p><a name="N101EB"><span class="smalltitle">监视流量</span></a></p>
<p>可以使用 GNU/Linux 的几个工具来检查网络上的低层流量。<code>tcpdump</code> 工具是一个比较老的工具，它从网上&#8220;嗅探&#8221;网络数据包，打印到 <code>stdout</code> 或记录在一个文件中。该功能允许查看应用程序产生的流量和 TCP 生成的低层流控制机制。一个叫做 <code>tcpflow</code> 的新工具与 <code>tcpdump</code> 相辅相成，它提供协议流分析和适当地重构数据流的方法，而不管数据包的顺序或重发。清单 5 显示 <code>tcpdump</code> 的两个用法模式。</p><br /><a name="N1020C"><strong>清单 5．tcpdump 工具的用法模式</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody>
<tr>
<td class="code-outline"><pre class="displaycode">Display all traffic on the eth0 interface for the local host
$ tcpdump -l -i eth0
Show all traffic on the network coming from or going to host plato
$ tcpdump host plato
Show all HTTP traffic for host camus
$ tcpdump host camus and (port http)
View traffic coming from or going to TCP port 45000 on the local host
$ tcpdump tcp port 45000
</pre></td></tr></tbody></table><br />
<p><code>tcpdump</code> 和 <code>tcpflow</code> 工具有大量的选项，包括创建复杂过滤表达式的能力。查阅下面的 <a href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#resources">参考资料</a> 获取更多关于这些工具的信息。 </p>
<p><code>tcpdump</code> 和 <code>tcpflow</code> 都是基于文本的命令行工具。如果您更喜欢图形用户界面（GUI），有一个开放源码工具 <code>Ethereal</code> 也许适合您的需要。<code>Ethereal</code> 是一个专业的协议分析软件，它可以帮助调试应用层协议。它的插入式架构（plug-in architecture）可以分解协议，比如 HTTP 和您能想到的任何协议（写本文的时候共有 637 个协议）。</p>
<div class="ibm-alternate-rule">
<hr />
</div>
<p class="ibm-ind-link ibm-back-to-top"><a class="ibm-anchor-up-link" href="http://www.ibm.com/developerworks/cn/linux/l-sockpit/#ibm-pcon">回页首</a></p>
<p><a name="N10232"><span class="atitle">总结</span></a></p>
<p>套接字编程是容易而有趣的，但是您要避免引入错误或至少使它们容易被发现，这就需要考虑本文中描述的这 5 个常见的陷阱，并且采用标准的防错性程序设计实践。GNU/Linux 工具和实用程序还可以帮助发现一些程序中的小问题。记住：在查看实用程序的帮助手册时候，跟踪相关的或&#8220;请参见&#8221;工具。您也许会发现一个必要的新工具。</p><!-- CMA ID: 95420 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file:  dw-article-6.0-beta.xsl --><br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<p><strong>学习</strong></p>
<ul><li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/linux/library/l-sockpit/?S_TACT=105AGX52&amp;S_CMP=cn-a-l" target="_blank">英文原文</a>。<br /><br /></li><li>TCP 状态机有 11 个状态。参见 W. Richard Steven 的 <a href="http://www.research.umbc.edu/~jeehye/cmsc491b/lectures/tcpstate/sld001.htm">illustration from TCP/IP Illustrated, Volume 1</a> 一书。<br /><br /></li><li>在 Wikipedia 上探究 <a href="http://en.wikipedia.org/wiki/Endian">Endianness</a> 的历史和含意。<br /><br /></li><li>了解更多关于 IBM 的开放式、可伸缩和可定制的 <a href="http://www.ibm.com/developerworks/power?S_TACT=105AGX52&amp;S_CMP=cn-a-l">Power Architecture</a> 的信息。<br /><br /></li><li>从 <a href="http://www.cs.cf.ac.uk/Dave/C/node33.html">Programming in C</a> 课件阅读 RPC/XDR 简介。<br /><br /></li><li>获取更多关于 XML-RPC 以及在 Java&#8482; 应用程序中如何使用它的信息，请阅读 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-xmlrpc/index.html">Java 编程中的 XML-RPC</a>&#8221; （developerWorks，2004 年 1 月）。<br /><br /></li><li>SOAP 以 XML-RPC 的特性为基础。请在 <a href="http://www.soapware.org/">SoapWare.Org</a> 上查找规范、工具、教程和文章。<br /><br /></li><li><a href="http://www.sctp.org/">SCTP</a> 兼有 TCP 和 UDP 的特性，以及可用性和可靠性。<br /><br /></li><li>教程 &#8220;<a href="https://www14.software.ibm.com/webapp/iwm/web/preLogin.do?lang=zh_CN&amp;source=dw-cn-l-l-sock">Linux Socket 编程，第一部分</a>&#8221; （developerWorks，2003 年 10 月）讲解如何开始套接字编程以及如何构建一个通过 TCP/IP 连接的 echo 服务器和客户端。&#8220;<a href="https://www14.software.ibm.com/webapp/iwm/web/preLogin.do?lang=zh_CN&amp;source=dw-cn-l-l-sock2">Linux Socket 编程，第二部分</a>&#8221; （developerWorks，2004 年 1 月）集中讨论 UDP 并且讲解如何用 C 和 Python 编写 UDP 套接字应用程序（尽管代码会翻译为其他语言）。<br /><br /></li><li><a href="http://man.linuxquestions.org/?query=netstat&amp;section=0&amp;type=2">netstat 手册页</a> 提供有关各种使用 netstat 的方法的细节。<br /><br /></li><li><a href="http://www.charlesriver.com/Books/BookDetail.aspx?productID=70637"><em>BSD Sockets Programming from a Multilanguage Perspective</em></a> （作者 M. Tim Jones），用 6 种不同的语言介绍了套接字编程的技巧。<br /><br /></li><li>在 <a href="http://www.ibm.com/developerworks/cn/linux/">developerWorks Linux 专区</a> 上查找更多为 Linux 开发人员提供的参考资料。<br /><br /></li></ul>
<p><strong>获得产品和技术</strong></p>
<ul><li><a href="http://www.tcpdump.org/">tcpdump</a> 和 <a href="http://freshmeat.net/projects/tcpflow/">tcpflow</a> 实用程序可以用来监控网络流量。<br /><br /></li><li><a href="http://www.ethereal.com/">Ethereal network protocol analyzer</a> 提供 tcpdump 的功能，它具有图形用户界面和可伸缩的插入式架构。<br /><br /></li><li><a href="http://www.ibm.com/developerworks/offers/sek/?S_TACT=105AGX52&amp;S_CMP=cn-a-l">免费索取 SEK for Linux</a>（两张 DVD），它包含来自 DB2&#174;、Lotus&#174;、Rational&#174;、Tivoli&#174; 以及 WebSphere&#174; 的最新 IBM 试用软件的 Linux 版本。<br /><br /></li><li>用 <a href="http://www.ibm.com/developerworks/downloads/?S_TACT=105AGX52&amp;S_CMP=cn-a-l">IBM 试用软件</a> 在 Linux 上构建您的下一个开发项目，可以直接在 developerWorks 上下载。<br /><br /><br /></li></ul>
<p><strong>讨论</strong></p>
<ul><li>通过参与 <a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-l">developerWorks blog</a> 加入 developerWorks 社区。<br /></li></ul><img src ="http://www.cppblog.com/API/aggbug/201418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-07-01 10:44 <a href="http://www.cppblog.com/API/archive/2013/07/01/201418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SO_REUSEADDR例解</title><link>http://www.cppblog.com/API/archive/2013/06/28/201365.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 28 Jun 2013 07:16:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/06/28/201365.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201365.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/06/28/201365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201365.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201365.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 其实这个问题在Richard Stevens的《Unix网络编程指南》卷一里有很详细的解答(中文版P166-168页)。这里我只是写几个基本的例子来验证这个问题。 首先声明一个问题：当两个socket的address和port相冲突，而你又想重用地址和端口，则旧的socket和新的socket都要已经被设置了SO_REUSEADDR特性，只有两者之一有这个特性还是有问题的。 SO_REUSEADD...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/06/28/201365.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/201365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-06-28 15:16 <a href="http://www.cppblog.com/API/archive/2013/06/28/201365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>socket描述符选项[SOL_SOCKET]</title><link>http://www.cppblog.com/API/archive/2013/06/28/201364.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 28 Jun 2013 07:14:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/06/28/201364.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201364.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/06/28/201364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201364.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201364.html</trackback:ping><description><![CDATA[<p><code>#include &lt;sys/socket.h&gt;<br />int setsockopt( int socket, int level, int option_name,<br />const void *option_value, size_t option_len);</code></p><br />第一个参数socket是套接字描述符。第二个参数level是被设置的选项的级别，如果想要在套接字级别上设置选项，就必须把level设置为 SOL_SOCKET。option_name指定准备设置的选项，option_name可以有哪些取值，这取决于level，以linux 2.6内核为例（在不同的平台上，这种关系可能会有不同），在套接字级别上(SOL_SOCKET)，option_name可以有以下取值： 
<ol><li>SO_DEBUG，打开或关闭调试信息。<br />当option_value不等于0时，打开调试信息，否则，关闭调试信息。它实际所做的工作是在sock-&gt;sk-&gt;sk_flag中置SOCK_DBG(第10)位，或清SOCK_DBG位。</li><li>SO_REUSEADDR，打开或关闭地址复用功能。<br />当option_value不等于0时，打开，否则，关闭。它实际所做的工作是置sock-&gt;sk-&gt;sk_reuse为1或0。</li><li>SO_DONTROUTE，打开或关闭路由查找功能。<br />当option_value不等于0时，打开，否则，关闭。它实际所做的工作是在sock-&gt;sk-&gt;sk_flag中置或清SOCK_LOCALROUTE位。</li><li>SO_BROADCAST，允许或禁止发送广播数据。<br />当option_value不等于0时，允许，否则，禁止。它实际所做的工作是在sock-&gt;sk-&gt;sk_flag中置或清SOCK_BROADCAST位。</li><li>SO_SNDBUF，设置发送缓冲区的大小。<br />发送缓冲区的大小是有上下限的，其上限为256 * (sizeof(struct sk_buff) + 256)，下限为2048字节。该操作将sock-&gt;sk-&gt;sk_sndbuf设置为val * 2，之所以要乘以2，是防<br />止大数据量的发送，突然导致缓冲区溢出。最后，该操作完成后，因为对发送缓冲的大小作了改变，要检查sleep队列，如果有进程正在等待写，将它们唤醒。</li><li>SO_RCVBUF，设置接收缓冲区的大小。<br />接收缓冲区大小的上下限分别是：256 * (sizeof(struct sk_buff) + 256)和256字节。该操作将sock-&gt;sk-&gt;sk_rcvbuf设置为val * 2。</li><li>SO_KEEPALIVE，套接字保活。<br />如果协议是TCP，并且当前的套接字状态不是侦听(listen)或关闭(close)，那么，当option_value不是零时，启用TCP保活定时器，否则关闭保活定时器。对于所有协议，该操<br />作都会根据option_value置或清sock-&gt;sk-&gt;sk_flag中的 SOCK_KEEPOPEN位。</li><li>SO_OOBINLINE，紧急数据放入普通数据流。<br />该操作根据option_value的值置或清sock-&gt;sk-&gt;sk_flag中的SOCK_URGINLINE位。</li><li>SO_NO_CHECK，打开或关闭校验和。<br />该操作根据option_value的值，设置sock-&gt;sk-&gt;sk_no_check。</li><li>SO_PRIORITY，设置在套接字发送的所有包的协议定义优先权。Linux通过这一值来排列网络队列。<br />这个值在0到6之间（包括0和6），由option_value指定。赋给sock-&gt;sk-&gt;sk_priority。</li><li>SO_LINGER，如果选择此选项, close或 shutdown将等到所有套接字里排队的消息成功发送或到达延迟时间后&gt;才会返回. 否则, 调用将立即返回。<br />该选项的参数（option_value)是一个linger结构：<br />struct linger {<br />int l_onoff; /* 延时状态（打开/关闭） */<br />int l_linger; /* 延时多长时间 */<br />};<br />如果linger.l_onoff值为0(关闭），则清sock-&gt;sk-&gt;sk_flag中的SOCK_LINGER位；否则，置该位，并赋sk-&gt;sk_lingertime值为linger.l_linger。</li><li>SO_PASSCRED，允许或禁止SCM_CREDENTIALS 控制消息的接收。<br />该选项根据option_value的值，清或置sock-&gt;sk-&gt;sk_flag中的SOCK_PASSCRED位。</li><li>SO_TIMESTAMP，打开或关闭数据报中的时间戳接收。<br />该选项根据option_value的值，清或置sock-&gt;sk-&gt;sk_flag中的SOCK_RCVTSTAMP位，如果打开，则还需设sock-&gt;sk-&gt;sk_flag中的SOCK_TIMESTAMP位，同时，将全局变量<br />netstamp_needed加1。</li><li>SO_RCVLOWAT，设置接收数据前的缓冲区内的最小字节数。<br />在Linux中，缓冲区内的最小字节数是固定的，为1。即将sock-&gt;sk-&gt;sk_rcvlowat固定赋值为1。</li><li>SO_RCVTIMEO，设置接收超时时间。<br />该选项最终将接收超时时间赋给sock-&gt;sk-&gt;sk_rcvtimeo。</li><li>SO_SNDTIMEO，设置发送超时时间。<br />该选项最终将发送超时时间赋给sock-&gt;sk-&gt;sk_sndtimeo。</li><li>SO_BINDTODEVICE，将套接字绑定到一个特定的设备上。<br />该选项最终将设备赋给sock-&gt;sk-&gt;sk_bound_dev_if。</li><li>SO_ATTACH_FILTER和SO_DETACH_FILTER。<br />关于数据包过滤，它们最终会影响sk-&gt;sk_filter。<br />以上所介绍的都是在SOL_SOCKET层的一些套接字选项，如果超出这个范围，给出一些不在这一level的选项作为参数，最终会得到- ENOPROTOOPT的返回值。但以上的分析仅限<br />于这些选项对sock-sk的值的影响，这些选项真正如何发挥作用，我们的探索道路将漫漫其修远。</li></ol><img src ="http://www.cppblog.com/API/aggbug/201364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-06-28 15:14 <a href="http://www.cppblog.com/API/archive/2013/06/28/201364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux编程之mprotect </title><link>http://www.cppblog.com/API/archive/2013/06/27/201338.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Thu, 27 Jun 2013 02:05:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/06/27/201338.html</guid><wfw:comment>http://www.cppblog.com/API/comments/201338.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/06/27/201338.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/201338.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/201338.html</trackback:ping><description><![CDATA[<h1 id="mprotect:设置内存访问权限">mprotect: 设置内存访问权限</h1>
<p>mmap 的第三个参数指定对内存区域的保护，由标记读、写、执行权限的 PROT_READ、PROT_WRITE 和 PROT_EXEC 按位与操作获得，或者是限制没有访问权限的 PROT_NONE。如果程序尝试在不允许这些权限的本地内存上操作，它将被 SIGSEGV 信号（Segmentation fault，段错误）终止。</p>
<p>在内存映射完成后，这些权限仍可以被 mprotect 系统调用所修改。mprotect 的参数分别为内存区间的地址，区间的大小，新的保护标志设置。所指定的内存区间必须包含整个页：区间地址必须和整个系统页大小对齐，而区间长度必须是页大小的整数倍。这些页的保护标记被这里指定的新保护模式替换。</p>
<blockquote>
<p><strong>获得页面对齐的内存</strong><br />应注意的是， malloc 返回的内存区域通常并不与内存页面对齐，甚至在内存的大小是页大小整数倍的情况下也一样。如果您想保护从 malloc 获得的内存，您不得不分配一个更大的内存区域并在其中找到一个与页对齐的区间。<br />您可以选择使用 mmap 系统调用来绕过 malloc 并直接从 Linux 内核中分配页面对齐内存。</p></blockquote>
<p>mmap通过映射 /dev/zero 来分配内存页。内存将被初始化为可读和可写模式。</p><pre class="wiki" name="code">int fd = open (&#8220;/dev/zero&#8221;, O_RDONLY);
char* memory = mmap (NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
close (fd);
</pre>
<p>然后，您的程序可以使用 mprotect 把它变成只读：</p><pre class="wiki" name="code">mprotect (memory, page_size, PROT_READ);
</pre>
<p>有一种监控内存访问的高级技巧，可以通过利用 mmap 和 mprotect 保护目标内存区间，然后当程序访问时候接收并处理 Linux 系统发送的 SIGSEGV 信号。代码 展示了这个技巧。</p>
<p>代码使用mprotect检测内存访问</p><pre class="wiki" name="code">#include &lt;fcntl.h&gt;
#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;

static int alloc_size;
static char* memory;

void segv_handler (int signal_number)
{
  printf (&#8220;memory accessed!\n&#8221;);
  mprotect (memory, alloc_size, PROT_READ | PROT_WRITE);
}

int main ()
{
  int fd;
  struct sigaction sa;

  /* 初始化segv_handler为SIGSEGV的句柄。*/
  memset (&amp;sa, 0, sizeof (sa));
  sa.sa_handler = &amp;segv_handler;
  sigaction (SIGSEGV, &amp;sa, NULL);

  /* 使用映射/dev/zero分配内存页。最初映射的内存为只写。*/
  alloc_size = getpagesize ();
  fd = open (&#8220;/dev/zero&#8221;, O_RDONLY);
  memory = mmap (NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
  close (fd);
  /* 写页来获得一个私有复制。 */
  memory[0] = 0;
  /* 使内存为不可写。 */
  mprotect (memory, alloc_size, PROT_NONE);

  /* 写分配内存区域。 */
  memory[0] = 1;
  /* 所有工作都结束；unmap内存映射。 */
  printf (&#8220;all done\n&#8221;);
  munmap (memory, alloc_size);

  return 0;
}
</pre>
<p>上述程序按照如下步骤执行：</p>
<ol><li>程序为 SIGSEGV 建立一个信号处理句柄。</li><li>程序通过映射 /dev/zero 分配一个内存分页，然后通过写入数据的方式获得一个私有复本。</li><li>程序通过调用带 PROT_NONE 权限的 mprotect 保护了内存。</li><li>当程序在后续执行中写入内存时，Linux 向进程发送 SIGSEGV，这个信号被 segv_handler 句柄接收处理。这个句柄将解除内存保护，因而程序内存访问得以继续。</li><li>当信号句柄执行完成时，程序控制权返回 main 函数，程序将使用 munmap 来释放内存。</li></ol><img src ="http://www.cppblog.com/API/aggbug/201338.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-06-27 10:05 <a href="http://www.cppblog.com/API/archive/2013/06/27/201338.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>epoll精髓</title><link>http://www.cppblog.com/API/archive/2013/01/07/197066.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 07 Jan 2013 06:17:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2013/01/07/197066.html</guid><wfw:comment>http://www.cppblog.com/API/comments/197066.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2013/01/07/197066.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/197066.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/197066.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在linux的网络编程中，很长的时间都在使用select来做事件触发。在linux新的内核中，有了一种替换它的机制，就是epoll。相比于select，epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中，它是采用轮询来处理的，轮询的fd数目越多，自然耗时越多。并且，在linux/posix_types.h头文件有这样的声明：#define __FD_S...&nbsp;&nbsp;<a href='http://www.cppblog.com/API/archive/2013/01/07/197066.html'>阅读全文</a><img src ="http://www.cppblog.com/API/aggbug/197066.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2013-01-07 14:17 <a href="http://www.cppblog.com/API/archive/2013/01/07/197066.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下发生段错误时如何产生core文件</title><link>http://www.cppblog.com/API/archive/2012/10/22/193644.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sun, 21 Oct 2012 16:11:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2012/10/22/193644.html</guid><wfw:comment>http://www.cppblog.com/API/comments/193644.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2012/10/22/193644.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/193644.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/193644.html</trackback:ping><description><![CDATA[<div>Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误)，此时如果系统core dump功能是打开的，那么将会有内存映像转储到硬盘上来，之后可以用gdb对core文件进行分析，还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。</div><div></div><div>使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的core文件大小，例如</div><div></div><div>ulimit -c 0 不产生core文件</div><div></div><div>ulimit -c 100 设置core文件最大为100k</div><div></div><div>ulimit -c unlimited 不限制core文件大小</div><div></div><div>先看一段会造成段错误的程序：</div><div></div><div>#include</div><div></div><div>int main()</div><div></div><div>{</div><div></div><div>char *ptr="linuxers.cn";</div><div></div><div>*ptr=0;</div><div></div><div>}</div><div></div><div>编译运行后结果如下：</div><div></div><div>[leconte@localhost test]$ gcc -g -o test a.c</div><div></div><div>[leconte@localhost test]$ ./test</div><div></div><div>段错误</div><div></div><div>此时并没有产生core文件，接下来使用ulimit -c设置core文件大小为无限制，再执行./test程序，结果如下：</div><div></div><div>[leconte@localhost ~]$ ulimit -a</div><div></div><div>core file size (blocks, -c) 0</div><div></div><div>[leconte@localhost test]$ ulimit -c unlimited</div><div></div><div>[leconte@localhost test]$ ulimit -a</div><div></div><div>core file size (blocks, -c) unlimited</div><div></div><div>[leconte@localhost test]$ ./test</div><div></div><div>段错误 (core dumped)</div><div></div><div>[leconte@localhost test]$ ls -al core.*</div><div></div><div>-rw------- 1 leconte leconte 139264 01-06 22:31 core.2065</div><div></div><div>可见core文件已经生成，接下来可以用gdb分析，查看堆栈情况：</div><div></div><div>[leconte@localhost test]$ gdb ./test core.2065</div><div></div><div>GNU gdb Fedora (6.8-27.el5)</div><div></div><div>Copyright (C) 2008 Free Software Foundation, Inc.</div><div></div><div>License GPLv3+: GNU GPL version 3 or later</div><div></div><div>This is free software: you are free to change and redistribute it.</div><div></div><div>There is NO WARRANTY, to the extent permitted by law. Type "show copying"</div><div></div><div>and "show warranty" for details.</div><div></div><div>This GDB was configured as "i386-redhat-linux-gnu"...</div><div></div><div>warning: exec file is newer than core file.</div><div></div><div>warning: Can't read pathname for load map: Input/output error.</div><div></div><div>Reading symbols from /lib/libc.so.6...done.</div><div></div><div>Loaded symbols for /lib/libc.so.6</div><div></div><div>Reading symbols from /lib/ld-linux.so.2...done.</div><div></div><div>Loaded symbols for /lib/ld-linux.so.2</div><div></div><div>Core was generated by `./test'.</div><div></div><div>Program terminated with signal 11, Segmentation fault.</div><div></div><div>[New process 2065]</div><div></div><div>#0 0x0804836f in main () at a.c:6</div><div></div><div>6 *ptr=0;</div><div></div><div>从上述输出可以清楚的看到，段错误出现在a.c的第6行，问题已经清晰地定位到了。</div><div></div><div>很多系统默认的core文件大小都是0，我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入 ulimit -c 命令来指定core文件大小，从而确保core文件能够生成。</div><div></div><div>除此之外，还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板，详情请看core的官方man手册。</div><img src ="http://www.cppblog.com/API/aggbug/193644.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2012-10-22 00:11 <a href="http://www.cppblog.com/API/archive/2012/10/22/193644.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached的删除机制和发展方向</title><link>http://www.cppblog.com/API/archive/2012/10/08/192976.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Mon, 08 Oct 2012 01:39:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2012/10/08/192976.html</guid><wfw:comment>http://www.cppblog.com/API/comments/192976.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2012/10/08/192976.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/192976.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/192976.html</trackback:ping><description><![CDATA[<div class="content">
<h1 class="entry-title"><a title="Permalink to memcached全面剖析&#8211;3.memcached的删除机制和发展方向" href="http://tech.idv2.com/2008/07/16/memcached-003/" rel="bookmark">memcached全面剖析&#8211;3.memcached的删除机制和发展方向</a></h1>
<div class="entry-content">
<p>下面是《memcached全面剖析》的第三部分。</p>
<ul><li>发表日：2008/7/16</li><li>作者：前坂徹(Toru Maesaka)</li><li>原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0003">http://gihyo.jp/dev/feature/01/memcached/0003</a></li></ul>
<p>前几次的文章在这里：</p>
<ul><li>第1次：<a href="http://tech.idv2.com/2008/07/10/memcached-001/">http://tech.idv2.com/2008/07/10/memcached-001/</a></li><li>第2次：<a href="http://tech.idv2.com/2008/07/11/memcached-002/">http://tech.idv2.com/2008/07/11/memcached-002/</a></li></ul>
<p><span id="more-600"></span></p>
<p>memcached是缓存，所以数据不会永久保存在服务器上，这是向系统中引入memcached的前提。 本次介绍memcached的数据删除机制，以及memcached的最新发展方向&#8212;&#8212;二进制协议（Binary Protocol） 和外部引擎支持。</p>
<h1>memcached在数据删除方面有效利用资源</h1>
<h2>数据不会真正从memcached中消失</h2>
<p><a href="http://tech.idv2.com/2008/07/11/memcached-002/">上次</a>介绍过，memcached不会释放已分配的内存。记录超时后，客户端就无法再看见该记录（invisible，透明）， 其存储空间即可重复使用。</p>
<h2>Lazy Expiration</h2>
<p>memcached内部不会监视记录是否过期，而是在get时查看记录的时间戳，检查记录是否过期。 这种技术被称为lazy（惰性）expiration。因此，memcached不会在过期监视上耗费CPU时间。</p>
<h1>LRU：从缓存中有效删除数据的原理</h1>
<p>memcached会优先使用已超时的记录的空间，但即使如此，也会发生追加新记录时空间不足的情况， 此时就要使用名为 Least Recently Used（LRU）机制来分配空间。 顾名思义，这是删除&#8220;最近最少使用&#8221;的记录的机制。 因此，当memcached的内存空间不足时（无法从<a href="http://tech.idv2.com/2008/07/11/memcached-002/">slab class</a>获取到新的空间时），就从最近未被使用的记录中搜索，并将其空间分配给新的记录。 从缓存的实用角度来看，该模型十分理想。</p>
<p>不过，有些情况下LRU机制反倒会造成麻烦。memcached启动时通过&#8220;-M&#8221;参数可以禁止LRU，如下所示：</p><pre><code>$ memcached -M -m 1024
</code></pre>
<p>启动时必须注意的是，小写的&#8220;-m&#8221;选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。</p>
<p>指定&#8220;-M&#8221;参数启动后，内存用尽时memcached会返回错误。 话说回来，memcached毕竟不是存储器，而是缓存，所以推荐使用LRU。</p>
<h1>memcached的最新发展方向</h1>
<p>memcached的roadmap上有两个大的目标。一个是二进制协议的策划和实现，另一个是外部引擎的加载功能。</p>
<h2>关于二进制协议</h2>
<p>使用二进制协议的理由是它不需要文本协议的解析处理，使得原本高速的memcached的性能更上一层楼， 还能减少文本协议的漏洞。目前已大部分实现，开发用的代码库中已包含了该功能。memcached的下载页面上有代码库的链接。</p>
<ul><li><a href="http://danga.com/memcached/download.bml">http://danga.com/memcached/download.bml</a></li></ul>
<h2>二进制协议的格式</h2>
<p>协议的包为24字节的帧，其后面是键和无结构数据（Unstructured Data）。 实际的格式如下（引自协议文档）：</p><pre><code> Byte/     0       |       1       |       2       |       3       |   
    /              |               |               |               |   
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0/ HEADER                                                        /   
   /                                                               /   
   /                                                               /   
   /                                                               /   
   +---------------+---------------+---------------+---------------+
 24/ COMMAND-SPECIFIC EXTRAS (as needed)                           /   
  +/  (note length in th extras length header field)               /   
   +---------------+---------------+---------------+---------------+
  m/ Key (as needed)                                               /   
  +/  (note length in key length header field)                     /   
   +---------------+---------------+---------------+---------------+
  n/ Value (as needed)                                             /   
  +/  (note length is total body length header field, minus        /   
  +/   sum of the extras and key length body fields)               /   
   +---------------+---------------+---------------+---------------+
  Total 24 bytes
</code></pre>
<p>如上所示，包格式十分简单。需要注意的是，占据了16字节的头部(HEADER)分为 请求头（Request Header）和响应头（Response Header）两种。 头部中包含了表示包的有效性的Magic字节、命令种类、键长度、值长度等信息，格式如下：</p><pre><code>Request Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Reserved                      |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+

Response Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key Length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Status                        |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+
</code></pre>
<p>如希望了解各个部分的详细内容，可以checkout出memcached的二进制协议的代码树， 参考其中的docs文件夹中的protocol_binary.txt文档。</p>
<h2>HEADER中引人注目的地方</h2>
<p>看到HEADER格式后我的感想是，键的上限太大了！现在的memcached规格中，键长度最大为250字节， 但二进制协议中键的大小用2字节表示。因此，理论上最大可使用65536字节（2<sup>16</sup>）长的键。 尽管250字节以上的键并不会太常用，二进制协议发布之后就可以使用巨大的键了。</p>
<p>二进制协议从下一版本1.3系列开始支持。</p>
<h1>外部引擎支持</h1>
<p>我去年曾经试验性地将memcached的存储层改造成了可扩展的（pluggable）。</p>
<ul><li><a href="http://alpha.mixi.co.jp/blog/?p=129">http://alpha.mixi.co.jp/blog/?p=129</a></li></ul>
<p>MySQL的Brian Aker看到这个改造之后，就将代码发到了memcached的邮件列表。memcached的开发者也十分感兴趣，就放到了roadmap中。现在由我和memcached的开发者Trond Norbye协同开发（规格设计、实现和测试）。 和国外协同开发时时差是个大问题，但抱着相同的愿景， 最后终于可以将可扩展架构的原型公布了。 代码库可以从<a href="http://danga.com/memcached/download.bml">memcached的下载页面</a>上访问。</p>
<h2>外部引擎支持的必要性</h2>
<p>世界上有许多memcached的派生软件，其理由是希望永久保存数据、实现数据冗余等， 即使牺牲一些性能也在所不惜。我在开发memcached之前，在mixi的研发部也曾经 考虑过重新发明memcached。</p>
<p>外部引擎的加载机制能封装memcached的网络功能、事件处理等复杂的处理。 因此，现阶段通过强制手段或重新设计等方式使memcached和存储引擎合作的困难 就会烟消云散，尝试各种引擎就会变得轻而易举了。</p>
<h2>简单API设计的成功的关键</h2>
<p>该项目中我们最重视的是API设计。函数过多，会使引擎开发者感到麻烦； 过于复杂，实现引擎的门槛就会过高。因此，最初版本的接口函数只有13个。 具体内容限于篇幅，这里就省略了，仅说明一下引擎应当完成的操作：</p>
<ul><li>引擎信息（版本等）</li><li>引擎初始化</li><li>引擎关闭</li><li>引擎的统计信息</li><li>在容量方面，测试给定记录能否保存</li><li>为item（记录）结构分配内存</li><li>释放item（记录）的内存</li><li>删除记录</li><li>保存记录</li><li>回收记录</li><li>更新记录的时间戳</li><li>数学运算处理</li><li>数据的flush</li></ul>
<p>对详细规格有兴趣的读者，可以checkout engine项目的代码，阅读器中的engine.h。</p>
<h2>重新审视现在的体系</h2>
<p>memcached支持外部存储的难点是，网络和事件处理相关的代码（核心服务器）与 内存存储的代码紧密关联。这种现象也称为tightly coupled（紧密耦合）。 必须将内存存储的代码从核心服务器中独立出来，才能灵活地支持外部引擎。 因此，基于我们设计的API，memcached被重构成下面的样子：</p>
<p><img alt="memcached-0003-001.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0003-001.png" /></p>
<p>重构之后，我们与1.2.5版、二进制协议支持版等进行了性能对比，证实了它不会造成性能影响。</p>
<p>在考虑如何支持外部引擎加载时，让memcached进行并行控制（concurrency control）的方案是最为容易的， 但是对于引擎而言，并行控制正是性能的真谛，因此我们采用了将多线程支持完全交给引擎的设计方案。</p>
<p>以后的改进，会使得memcached的应用范围更为广泛。</p>
<h1>总结</h1>
<p>本次介绍了memcached的超时原理、内部如何删除数据等，在此之上又介绍了二进制协议和 外部引擎支持等memcached的最新发展方向。这些功能要到1.3版才会支持，敬请期待！</p>
<p>这是我在本连载中的最后一篇。感谢大家阅读我的文章！</p>
<p>下次由长野来介绍memcached的应用知识和应用程序兼容性等内容。</p></div></div><img src ="http://www.cppblog.com/API/aggbug/192976.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2012-10-08 09:39 <a href="http://www.cppblog.com/API/archive/2012/10/08/192976.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gcc与g++区别</title><link>http://www.cppblog.com/API/archive/2012/09/28/192279.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 28 Sep 2012 08:37:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2012/09/28/192279.html</guid><wfw:comment>http://www.cppblog.com/API/comments/192279.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2012/09/28/192279.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/192279.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/192279.html</trackback:ping><description><![CDATA[不经意间，GCC已发展到了4.3的版本，尽管在软件开发社区之外乏人闻问，但因为GCC在几乎所有开源软件和自由软件中都会用到，因此它的编译性能的涨 落会直接影响到Linux 、Firefox 乃至于OpenOffice.org和Apache等几千个项目的开发。因此，把GCC摆在开源软件的核心地位是一点也不为过。另一方面，GCC4.3的 出现，正在牵引着广大程序员们的心。如果我们非要用一个词来说明GCC与程序员之间的关系，那无疑是"心随心动"。<br />历史篇<br />作为自由软件的旗舰项目，Richard Stallman 在十多年前刚开始写作 GCC 的时候，还只是把它当作仅仅一个 C 程序语言的编译器；GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年 的发展，GCC 已经不仅仅能支持 C 语言；它现在还支持Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言，以及支持函数式编程和逻辑编程的 Mercury 语言，等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了，而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面，说到 GCC 对于各种硬件平台的支持，概括起来就是一句话：无所不在。几乎所有有点实际用途的硬件平台，甚至包括有些不那么有实际用途的硬件平台。<br />Gcc 简介<br />Linux系统下的gcc（GNU C Compiler）是GNU推出的功能强大、性能优越的多平台编译器，是GNU的代表作品之一。Gcc是可以在多种硬体平台上编译出可执行程序的超级编译器，其执行效率与一般的编译器相比平均效率要高20%~30%。<br />官方网站：<a href="http://gcc.gnu.org/">http://gcc.gnu.org/</a> 
<p>gcc是linux的唯一编译器，没有gcc就没有linux，gcc的重要性就不可言喻啦。居然这么重要，那就很值得我们来好好研究下啦。好啦，开始我们的gcc之旅吧！</p>
<p><br />首先消除gcc和g++误区吧。<br />gcc和g++都是GNU(组织)的一个编译器。</p>
<p>误区一:gcc只能编译c代码,g++只能编译c++代码</p>
<p>两者都可以，但是请注意：<br />1.后缀为.c的，gcc把它当作是C程序，而g++当作是c++程序；后缀为.cpp的，两者都会认为是c++程序，注意，虽然c++是c的超集，但是两者对语法的要求是有区别的，例如：<br />#include &lt;stdio.h&gt;<br />int main(int argc, char* argv[]) {<br />if(argv == 0) return;<br />printString(argv);<br />return;<br />}<br />int printString(char* string) {<br />sprintf(string, "This is a test."n");<br />}<br />如果按照C的语法规则，OK，没问题，但是，一旦把后缀改为cpp，立刻报三个错：&#8220;printString未定义&#8221;；<br />&#8220;cannot convert `char**' to `char*&#8221;；<br />&#8221;return-statement with no value&#8220;；<br />分别对应前面红色标注的部分。可见C++的语法规则更加严谨一些。<br />2.编译阶段，g++会调用gcc，对于c++代码，两者是等价的，但是因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常用g++来完成链接，为了统一起见，干脆编译/链接统统用g++了，这就给人一种错觉，好像cpp程序只能用g++似的。<br /><br />误区二:gcc不会定义__cplusplus宏，而g++会</p>
<p>实际上，这个宏只是标志着编译器将会把代码按C还是C++语法来解释，如上所述，如果后缀为.c，并且采用gcc编译器，则该宏就是未定义的，否则，就是已定义。<br /><br />误区三:编译只能用gcc，链接只能用g++</p>
<p>严格来说，这句话不算错误，但是它混淆了概念，应该这样说：编译可以用gcc/g++，而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C＋＋程序使用的库联接，所以通常使用g++来完成联接。但在编译阶段，g++会自动调用gcc，二者等价。<br /><br />误区四:extern "C"与gcc/g++有关系</p>
<p>实际上并无关系，无论是gcc还是g++，用extern "c"时，都是以C的命名方式来为symbol命名，否则，都以c++方式命名。试验如下：<br />me.h：<br />extern "C" void CppPrintf(void);<br /><br />me.cpp:<br />#include &lt;iostream&gt;<br />#include "me.h"<br />using namespace std;<br />void CppPrintf(void)<br />{<br />cout &lt;&lt; "Hello"n";<br />}<br /><br />test.cpp:<br />#include &lt;stdlib.h&gt;<br />#include &lt;stdio.h&gt;<br />#include "me.h" <br />int main(void)<br />{<br />CppPrintf();<br />return 0;<br />}<br /><br />1. 先给me.h加上extern "C"，看用gcc和g++命名有什么不同</p>
<p>[root@root G++]# g++ -S me.cpp<br />[root@root G++]# less me.s<br />.globl _Z9CppPrintfv //注意此函数的命名<br />.type CppPrintf, @function<br />[root@root GCC]# gcc -S me.cpp<br />[root@root GCC]# less me.s<br />.globl _Z9CppPrintfv //注意此函数的命名<br />.type CppPrintf, @function<br />完全相同！<br /><br />2. 去掉me.h中extern "C"，看用gcc和g++命名有什么不同</p>
<p>[root@root GCC]# gcc -S me.cpp<br />[root@root GCC]# less me.s<br />.globl _Z9CppPrintfv //注意此函数的命名<br />.type _Z9CppPrintfv, @function<br />[root@root G++]# g++ -S me.cpp<br />[root@root G++]# less me.s<br />.globl _Z9CppPrintfv //注意此函数的命名<br />.type _Z9CppPrintfv, @function<br />完全相同！<br /><br /></p>
<p align="left">&nbsp;</p><img src ="http://www.cppblog.com/API/aggbug/192279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2012-09-28 16:37 <a href="http://www.cppblog.com/API/archive/2012/09/28/192279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linx 静态库与动态库</title><link>http://www.cppblog.com/API/archive/2012/09/07/189830.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Fri, 07 Sep 2012 08:44:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2012/09/07/189830.html</guid><wfw:comment>http://www.cppblog.com/API/comments/189830.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2012/09/07/189830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/189830.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/189830.html</trackback:ping><description><![CDATA[1.概念和区别：<br />静态库就是在编译过程中一些目标文件的集合。静态库在程序链接的时候使用，链接器会将程序中使用到函数的代码从库文件中拷贝到应用程序中。一旦链接完成，在执行程序的时候就不需要静态库了。 <br />由于每个使用静态库的应用程序都需要拷贝所用函数的代码，所以静态链接的文件会比较大。 
<p>相对于静态函数库，动态函数库在编译的时候并没有被编译进目标代码中，而只是作些标记。然后在程序开始启动运行的时候，动态地加载所需模块，因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序，而是程序运行时动态的申请并调用，所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序，所以动态函数库的升级比较方便。</p>
<p>2.命名：<br />静态库的名字一般为libxxxx.a，其中xxxx是该lib的名称。<br />动态库的名字一般为libxxxx.so.major.minor，xxxx是该lib的名称，major是主版本号，minor是副版本号。版本号也可以没有，一般都会建立个没有版本号的软连接文件链接到全名的库文件。</p>
<p>3.创建：<br />无论静态库还是动态库，创建都分为两步，第一步创建目标文件，第二步生产库。<br />1).静态库的创建：<br />#gcc -c test.c -o test.o<br />#ar rcs libtest.a test.o<br />名字为libtest.a的静态库就生产了，其中选项：<br />r 表明将模块加入到静态库中；<br />c 表示创建静态库；<br />s 表示生产索引；<br />还有更多选项像增加、删除库中的目标文件，包括将静态库解包等可以通过man来获得。<br />2).动态库的创建：<br />#gcc -fPIC -c test.c -o test.c<br />#gcc --share test.o -o libtest.so<br />-fPIC 为了跨平台</p>
<p>4.使用：<br />编译链接目标程序的方法是一样的：<br />#gcc main.c -L. -ltest -o main<br />-L.指定现在本目录下搜索库，如果没有，会到系统默认的目录下搜索，一般为/lib、/usr/lib下。<br />对于静态库，这个步骤之后就可以将libtest.a库删掉，因为它已经被编译进了目标程序，不再需要它了。<br />而对于动态库，libtest.so库只是在目标程序里做了标记，在运行程序时才会动态加载，那么从哪加载呢？加载目录会由/etc/ld.so.conf来指定，一般默认是/lib、/usr/lib，所以要想让动态库顺利加载，你可以将库文件copy到上面的两个目录下，或者设置export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/XXX/YYY，后面为你自己动态库的目录，再或者修改/etc/ld.so.conf文件，把库所在的路径加到文件末尾，并执行ldconfig刷新。这样，加入的目录下的所有库文件都可见。</p>
<p>另外还有个文件需要了解/etc/ld.so.cache，里面保存了常用的动态函数库，且会先把他们加载到内存中，因为内存的访问速度远远大于硬盘的访问速度，这样可以提高软件加载动态函数库的速度了。</p>
<p>最后提一点，当同一目录下既有动态库又有静态库，并且两个库的名字相同时，编译时会如何链接呢？</p>
<p>gcc编译时默认都是动态链接，如果要指定优先链接静态库，需要指定参数static。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/API/aggbug/189830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2012-09-07 16:44 <a href="http://www.cppblog.com/API/archive/2012/09/07/189830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>gdb 条件断点</title><link>http://www.cppblog.com/API/archive/2011/04/16/144342.html</link><dc:creator>C++技术中心</dc:creator><author>C++技术中心</author><pubDate>Sat, 16 Apr 2011 01:34:00 GMT</pubDate><guid>http://www.cppblog.com/API/archive/2011/04/16/144342.html</guid><wfw:comment>http://www.cppblog.com/API/comments/144342.html</wfw:comment><comments>http://www.cppblog.com/API/archive/2011/04/16/144342.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/API/comments/commentRss/144342.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/API/services/trackbacks/144342.html</trackback:ping><description><![CDATA[1.有时候，我们需要断点在循环的某个条件处时，比如以下代码：<br>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><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><br></span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;main(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;argc,</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">argv[])<br>{<br>&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(&nbsp;;&nbsp;i</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">100</span><span style="color: #000000;">;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;m;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;i;<br>&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br><br>&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></div>
2.开始编译<br>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">#cc&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">g&nbsp;main</span><span style="color: #000000;">.</span><span style="color: #000000;">c</span></div>
将生成a.out<br>3.开始断点<br>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">#gdb&nbsp;a</span><span style="color: #000000;">.</span><span style="color: #000000;">out<br>l&nbsp; #l命令用于查看代码<br></span></div>
4.通过以上命令可以看到代码 m = i 处为标记为"11"，那么，如何当i=50的时候，断点到 m = i处呢？<br>
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">(gdb)&nbsp;b&nbsp;</span><span style="color: #800000;">11</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">==</span><span style="color: #800000;">50</span><span style="color: #000000;"><br>(gdb) r<br>Starting program: /home/bluesky/a.out <br>Breakpoint 1, main (argc=1, argv=0xbffff3b4) at main.c:11<br>11&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m = i;<br>(gdb) p i<br>$1 = 50<br><br></span></div>
<br><br><img src ="http://www.cppblog.com/API/aggbug/144342.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/API/" target="_blank">C++技术中心</a> 2011-04-16 09:34 <a href="http://www.cppblog.com/API/archive/2011/04/16/144342.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>