﻿<?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++博客-ivy-jie-文章分类-linux</title><link>http://www.cppblog.com/ivy-jie/category/10521.html</link><description>progress ...</description><language>zh-cn</language><lastBuildDate>Thu, 17 Sep 2009 09:04:13 GMT</lastBuildDate><pubDate>Thu, 17 Sep 2009 09:04:13 GMT</pubDate><ttl>60</ttl><item><title>rpm安装包相关命令</title><link>http://www.cppblog.com/ivy-jie/articles/94173.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sun, 23 Aug 2009 08:19:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/94173.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/94173.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/94173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/94173.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/94173.html</trackback:ping><description><![CDATA[<strong>使用rpm命令查询软件包：</strong>
<p>　　1、查询系统中安装的所有RPM包</p>
<p>　　$ rpm -qa</p>
<p>　　查询当前linux系统中已经安装的软件包。</p>
<p>　　例：$ rpm -qa | grep -i x11 | head -3 察看系统中包含x11字符串的前3行软件包。</p>
<p>　　2、查询软件包是否安装</p>
<p>　　$ rpm &#8211;q rpm包名称 察看系统中指定软件包是否安。</p>
<p>　　例: $ rpm -q bash 察看系统中bash软件包是否安装。</p>
<p>　　"rpm -q"命令中指定的软件包名称需要准确的拼写，该命令不会在软件包的名称中进行局部匹配的查询。</p>
<p>　　3、查询已安装软件包详细信息</p>
<p>　　$ rpm &#8211;qi RPM包名称 查询linux系统中指定名称软件包的详细信息。</p>
<p>　　例：$ rpm -qi bash 察看bash软件包的详细信息。</p>
<p>　　"rpm -qi"命令的执行结果中包含较详细的信息，包括：软件名称，版本信息，包大小，描述，等。</p>
<p>　　4、查询已安装软件包中的文件列表</p>
<p>　　$ rpm &#8211;ql RPM包名称 查询已安装软件包在当前系统中安装了哪些文件。</p>
<p>　　例：$ rpm -ql bash | head -3 查看bash软件在系统中已安装文件的前3行文件列表。</p>
<p>　　$ rpm -ql bash | grep bin 用过滤方式察看bash中包含bin字符串的文件列表。</p>
<p>　　5、查询系统中文件所属的软件包</p>
<p>　　$ rpm &#8211;qf 文件名称 查询linux系统中指定文件所属的软件包。</p>
<p>　　例：$ rpm -qf /bin/bash 察看bash文件所属的软件包。</p>
<p>　　bash-3.0-19.2 显示结果。</p>
<p>　　6、查询RPM安装包文件中的信息 </p>
<p>　　$ rpm &#8211;qpi RPM包文件名 察看RPM包未安装前的详细信息。</p>
<p>　　$ rpm &#8211;qpl RPM包文件名 察看RPM包未安装前的文件列表。</p>
<p>　　"rpm -qpi和rpm -qpl 这两条命令可作为在安装软件包之前对其的了解。</p>
<p>　　7、rpm命令查询实例</p>
<p>　　$ which mount 获得mount命令的可执行文件路径。</p>
<p>　　$ rpm &#8211;qf /bin/mount 查询/bin/mount所属的软件包。</p>
<p>　　$ rpm &#8211;qi util-linux 查询/bin/mount所属软件包的详细信息。</p>
<p>　　$ rpm &#8211;qf util-linux | grep mount 查询/bin/mount所属软件包中包括mount相关所有文件。</p>
<p>　　<strong taggedby="hylanda">使用rpm命令安装软件包：</strong></p>
<p>　　1、rpm软件包地基本安装</p>
<p>　　$ rpm &#8211;i rpm安装包文件名 安装该软件包中的文件到当前系统，安装过程不提示任何信息。</p>
<p>　　2、在安装软件包的同时显示详细信息</p>
<p>　　$ rpm &#8211;ivh rpm安装包文件 安装该软件包中的文件到当前系统，安装过程会以百分比的形式显示安装的进度和一些其他信息。</p>
<p>　　3、RPM软件包安装的依赖关系</p>
<p>　　强制安装：$ rpm --force &#8211;i rpm包文件名</p>
<p>　　注：要先满足软件包的依赖关系后再进行软件包的安装，使用强制安装命令安装不能保证软件安装到系统后一定能</p>
<p>　　正常运行，因此建议慎重使用。</p>
<p>　　<strong taggedby="hylanda">使用rpm命令卸载软件包：</strong></p>
<p>　　1、RPM软件包的卸载</p>
<p>　　$ rpm -e 软件包名称</p>
<p>　　软件包的卸载，在卸载时不显示任何信息。</p>
<p>　　注：RPM软件包的卸载同样存在依赖关系，只有在没有依赖关系存在时才能对其进行卸载。</p>
<p>　　2、rpm软件包卸载的依赖关系</p>
<p>　　在使用RPM命令进行卸载时，RPM命令会分析要卸载的软件包的依赖关系，当存在依赖关系时会自动停止，并显由 </p>
<p>　　哪个软件造成的卸载失败。根据RPM提示的错误信息，确定先卸载的软件包，再卸载被依赖的软件包。</p>
<p>　　<strong taggedby="hylanda">使用rpm命令升级软件包：</strong></p>
<p>　　$ rpm - U rpm安装包文件名</p>
<p>　　注："rpm -u"命令中使用的升级软件包文件最好使用RED HAT公司针对当前的linux版本官方推出的升级文件，建议不要使用第三方提供的升级包。</p>
<p><!--分页--></p>
<p>　　<strong taggedby="hylanda">编译应用程序前的准备工作：</strong></p>
<p>　　1、确认系统中已经安装了编译环境</p>
<p>　　$ rpm -qa | grep gcc</p>
<p>　　确定当前系统中安装了gcc编译器环境。</p>
<p>　　2、下载prozilla程序的源代码安装包文件</p>
<p>　　3、释放已下载的源代码软件包文件</p>
<p>　　$ tar jxf prozilla-2.0.4.tar.bz2</p>
<p>　　释放以下载的源代码软件包文件到当前目录。解压后的文件名：prozilla-2.0.4</p>
<p>　　扩展：tar的xzvf参数用于释放以tar.gz格式的压缩包。</p>
<p>　　4、进入源代码目录</p>
<p>　　$ cd prozilla-2.0.4 进入目录。</p>
<p>　　$ pwd 显示当前目录路径。</p>
<p>　　/home/teacher/download/prozilla-2.0.4 显示结果。</p>
<p>　　编译软件安装的路径：</p>
<p>　　$ ./configure --prefix=/home/teacher/proz </p>
<p>　　在prozilla程序的配置中，使用"--prdfix"选项可以指定应用程序编译后的安装路径，如果不使用"--prefix"</p>
<p>　　选项指定安装路径，configure程序将配置prozilla的默认安装路径为"/usr/local/bin"目录。</p>
<p>　　5、程序编译过程</p>
<p>　　$ make</p>
<p>　　使用make命令进行程序的二进制编译。</p>
<p>　　6、程序安装过程</p>
<p>　　$ make install</p>
<p>　　"make install"命令将按照configuer命令的"--prefix"选项中设定的安装路径将已编译完成的应用程序安装到目标目录。</p>
<p>　　7、验证编译安装的程序</p>
<p>　　$ ls /home/teacher/proz </p>
<p>　　察看proz文件夹中的文件。</p>
<p>　　bin include lib man share</p>
<p>　　编译前的配置</p>
<p>　　$ ./configure - - help </p>
<p>　　<strong taggedby="hylanda">编译与安装：</strong></p>
<p>　　1、程序编译过程</p>
<p>　　$ make </p>
<p>　　2、程序安装过程</p>
<p>　　$ make install</p>
<p>　　3、验证编译安装的程序</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/94173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-08-23 16:19 <a href="http://www.cppblog.com/ivy-jie/articles/94173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux命令1</title><link>http://www.cppblog.com/ivy-jie/articles/89567.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Wed, 08 Jul 2009 13:41:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/89567.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/89567.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/89567.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/89567.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/89567.html</trackback:ping><description><![CDATA[&nbsp;linux指令大全: <a href="http://itbbs-arch.pconline.com.cn/topic.jsp?tid=2746314"><u>http://itbbs-arch.pconline.com.cn/topic.jsp?tid=2746314</u></a>&nbsp;<br><br><font color=#008000><font color=#000000>1</font>、安装以rpm方式提供的软件<br>Rpm(the&nbsp;red&nbsp;hat&nbsp;package&nbsp;manager)是一个开放的软件包管理系统。<br>功能：可以安装和卸载RPM包的软件<br>#rpm&nbsp;&#8211;ivh&nbsp;*.rpm&nbsp;安装RPM包；<br>#rpm&nbsp;&#8211;ivh&nbsp;&#8211;force&nbsp;*.rpm&nbsp;在原先安装的基础上再强行安装一次；<br>#rpm&nbsp;&#8211;Uvh&nbsp;*.rpm&nbsp;升级rpm包<br>#rpm&nbsp;&#8211;qa&nbsp;查找列出系统中所有安装的rpm包<br>#rpm&nbsp;&#8211;q&nbsp;sendmail：查看sendmail包的情况<br>#rpm&nbsp;&#8211;ql&nbsp;sendmail:查看sendmail安装的位置<br>#rpm&nbsp;&#8211;e&nbsp;*.rpm&nbsp;卸载rpm包<br>#rpm&nbsp;-&nbsp;qlp&nbsp;name.rpm&nbsp;查看name.rpm有哪些文件<br>#rpm&nbsp;-&nbsp;qf&nbsp;name.rpm&nbsp;查看已经装好的文件属于哪个rpm包<br>#rpm2cpio&nbsp;filename.rpm&nbsp;使用&#8220;rpm2cpio&#8221;来从RPM文档中提取文件<br></font><br>2. <font color=#008000>安装以源代码方式提供的软件<br>（1）、解包解压：<br>#tar&nbsp;&#8211;xzvf&nbsp;*.tar.gz&nbsp;解包解压后会在当前目录下建立一个子目录,如xxxx<br>（2）、#cd&nbsp;xxxx<br>（3）、#./configure<br>（4）、#make<br>（5）、#make&nbsp;install&nbsp;<br></font><br>3.<font color=#008000>Find使用范例<br>find&nbsp;.&nbsp;-name&nbsp;ls.txt<br>find&nbsp;.&nbsp;-name&nbsp;ls.txt&nbsp;&#8211;print<br>find&nbsp;/&nbsp;-name&nbsp;&#8216;c??&#8217;&nbsp;&#8211;print<br>find&nbsp;/&nbsp;-name&nbsp;&#8216;f*&#8217;&nbsp;&#8211;print<br>find&nbsp;.&nbsp;-name&nbsp;&#8216;f*&#8217;&nbsp;&#8211;exec&nbsp;ls&nbsp;&#8211;l&nbsp;{&nbsp;}&nbsp;\;&nbsp;<br>find&nbsp;.&nbsp;-name&nbsp;f\*&nbsp;&#8211;ok&nbsp;rm&nbsp;{&nbsp;}&nbsp;\;&nbsp;（交互式提问）<br>find&nbsp;.&nbsp;-perm&nbsp;644&nbsp;&#8211;mtime&nbsp;4&nbsp;<br>find&nbsp;.&nbsp;-name&nbsp;&#8216;c??&#8217;&nbsp;&#8211;o&nbsp;-name&nbsp;&#8216;d??&#8217;<br>4. grep<br>功能：在文件中搜寻匹配的行并进行输出<br>语法：grep&nbsp;[参数]&lt;要找的字串&gt;&lt;原文件&gt;<br>-num:输出匹配行前后各num行的内容<br>-A&nbsp;num:输出匹配行后num行的内容<br>-B&nbsp;num:输出匹配行前num行的内容<br>-i：忽略大小写的区别<br>-v:只显示出那些不包括某字串的行和文件，和默认的相反<br></font>5. <font color=#008000>mkdir<br>功能：创建一个目录（类似MSDOS下的md命令）。<br>语法：mkdir&nbsp;[选项]&nbsp;dir-name&nbsp;<br>-&nbsp;m&nbsp;对新建目录设置存取权限。也可以用chmod命令设置。<br>-&nbsp;p&nbsp;可以是一个路径名称。此时若路径中的某些目录尚不存在，&nbsp;加上此选项后，&nbsp;系统将自动建立好那些尚不存在的目录，即一次可以建立多个目录。<br>#mkdir&nbsp;&#8211;m&nbsp;a=rwx&nbsp;test<br>#mkdir&nbsp;&#8211;m&nbsp;u=rwx,g=rx,o=rx&nbsp;test1<br>#mkdir&nbsp;&#8211;m&nbsp;755&nbsp;test2<br>#mkdir&nbsp;&#8211;p&nbsp;test3/test4(test3和test4均为新目录）<br><br>6、rmdir<br>功能：删除空目录。<br>语法：rmdir&nbsp;[选项]&nbsp;dir-name&nbsp;<br>-&nbsp;p&nbsp;递归删除目录dirname，当子目录删除后其父目录为空时，也一同被删除。<br>7、file<br>功能：查看文件类型<br>语法：file&nbsp;文件名<br><br>8、cat<br>功能：查看文本文件的内容<br>语法：cat&nbsp;文件名<br>9、more<br>功能：分屏显示文本文件的内容。<br>10、less<br>功能：显示文本文件的内容，可使用pageup和pagedown上翻页下翻页。<br><br>11、head<br>功能：查看文件的开头部分内容<br>语法：head&nbsp;[行数]&nbsp;文件名<br>用法：#head&nbsp;test.txt:显示前10行内容<br>#head&nbsp;-20&nbsp;test.txt&nbsp;显示前20行内容。<br>12、tail<br>功能：查看文件的结尾部分内容。<br>语法：tail&nbsp;[行数]&nbsp;文件名<br>默认的行数为10行。<br><br>13、sort<br>功能：对文本文件中的各行进行排序<br>用法：sort&nbsp;文件名<br>例：#sort&nbsp;123.txt&nbsp;&gt;456.txt&nbsp;将123.txt排序后重定向到456.txt文件中。<br>14、uniq<br>功能：将重复行从输出文件中删除，只留下每条记录的唯一样本&nbsp;<br>语法：&nbsp;uniq&nbsp;文件名<br>#uniq&nbsp;456.txt文件中的重复行删除后输出</font><br>&nbsp;
<img src ="http://www.cppblog.com/ivy-jie/aggbug/89567.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-07-08 21:41 <a href="http://www.cppblog.com/ivy-jie/articles/89567.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vi命令 </title><link>http://www.cppblog.com/ivy-jie/articles/88479.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Thu, 25 Jun 2009 00:27:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/88479.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/88479.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/88479.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/88479.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/88479.html</trackback:ping><description><![CDATA[1 保存和模式切换&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;三种模式:<br>&nbsp; <font face="Courier New">Command（命令）模式，用于输入命令；<br>&nbsp;Insert（插入）模式，用于插入文本；<br>&nbsp;Visual（可视）模式，用于视化的的高亮并选定正文<br></font>
<p>&nbsp;&nbsp; 默认模式是Command,当我们按ESC键后，接着再输入:号时，进入command模式<br>&nbsp;&nbsp; :wq 强制性写入文件并退出。即使文件没有被修改也强制写入，并更新文件的修改时间。<br>&nbsp;&nbsp; :x 写入文件并退出。仅当文件被修改时才写入，并更新文件修改时间，否则不会更新文件修改时间。<br>&nbsp;&nbsp; 这两者一般情况下没什么不一样，但是在编程方面，对编辑源文件可能会产生重要影响。因为文件即使没有修改，":wq"强制更新文件的修改时间，这样会让make编译整个项目时以为文件被修改过了，然后就得重新编译链接生成可执行文件。这可能会产生让人误解的后果，当然也产生了不必要的系统资源花销。<span style="COLOR: red">建议用:x<br>&nbsp;&nbsp; <font face="Courier New" color=#000000>:w&nbsp; 保存；<br>&nbsp;:w&nbsp; filename 另存为filename；<br>&nbsp;:wq! 保存退出；<br>&nbsp;:wq! filename 注：以filename为文件名保存后退出；<br>&nbsp;:q! 不保存退出；<br>2 <br></font></span></p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/88479.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-25 08:27 <a href="http://www.cppblog.com/ivy-jie/articles/88479.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下C多线程编程</title><link>http://www.cppblog.com/ivy-jie/articles/87518.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 12 Jun 2009 11:47:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87518.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87518.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87518.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87518.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87518.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;Linux系统下的多线程遵循POSIX线程接口，称为pthread。编写Linux下的多线程程序，需要使用头文件pthread.h，连接时需要使用库libpthread.a。顺便说一下，Linux下pthread的实现是通过系统调用clone（）来实现的。clone（）是Linux所特有的系统调用，它的使用方式类似fork，关于clone（）的详细情况，有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的多线程程序example1.c。 <br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>/* example.c*/<br>#include &lt;stdio.h&gt;<br>#include &lt;pthread.h&gt;<br>void thread(void)<br>{<br>　int i;<br>　for(i=0;i&lt;3;i++)<br>　　printf("This is a pthread.n");<br>}<br><br>int main(void)<br>{<br>　pthread_t id;<br>　int i,ret;<br>　ret=pthread_create(&amp;id,NULL,(void *) thread,NULL);<br>　if(ret!=0){<br>　　printf ("Create pthread error!n");<br>　　exit (1);<br>　}<br>　for(i=0;i&lt;3;i++)<br>　　printf("This is the main process.n");<br>　pthread_join(id,NULL);<br>　return (0);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　我们编译此程序：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>gcc example1.c -lpthread -o example1</td>
        </tr>
    </tbody>
</table>
<br>　　运行example1，我们得到如下结果：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>This is the main process.<br>This is a pthread.<br>This is the main process.<br>This is the main process.<br>This is a pthread.<br>This is a pthread.</td>
        </tr>
    </tbody>
</table>
<br>　　再次运行，我们可能得到如下结果：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>This is a pthread.<br>This is the main process.<br>This is a pthread.<br>This is the main process.<br>This is a pthread.<br>This is the main process.</td>
        </tr>
    </tbody>
</table>
<br>　　前后两次结果不一样，这是两个线程争夺CPU资源的结果。上面的示例中，我们使用到了两个函数，pthread_create和pthread_join，并声明了一个pthread_t型的变量。<br><br>　　pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义：<br><br>　　typedef unsigned long int pthread_t;<br><br>　　它是一个线程的标识符。函数pthread_create用来创建一个线程，它的原型为：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void *), void *__arg));</td>
        </tr>
    </tbody>
</table>
<br>　　第一个参数为指向线程标识符的指针，第二个参数用来设置线程属性，第三个参数是线程运行函数的起始地址，最后一个参数是运行函数的参数。这里，我们的函数thread不需要参数，所以最后一个参数设为空指针。第二个参数我们也设为空指针，这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时，函数返回0，若不为0则说明创建线程失败，常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程，例如线程数目过多了；后者表示第二个参数代表的线程属性值非法。创建线程成功后，新创建的线程则运行参数三和参数四确定的函数，原来的线程则继续运行下一行代码。 <br><br>　　函数pthread_join用来等待一个线程的结束。函数原型为：<br><br>　　extern int pthread_join __P ((pthread_t __th, void **__thread_return));<br><br>　　第一个参数为被等待的线程标识符，第二个参数为一个用户定义的指针，它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数，调用它的函数将一直等待到被等待的线程结束为止，当函数返回时，被等待线程的资源被收回。一个线程的结束有两种途径，一种是象我们上面的例子一样，函数结束了，调用它的线程也就结束了；另一种方式是通过函数pthread_exit来实现。它的函数原型为：<br><br>　　extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));<br><br>　　唯一的参数是函数的返回代码，只要pthread_join中的第二个参数thread_return不是NULL，这个值将被传递给thread_return。最后要说明的是，一个线程不能被多个线程等待，否则第一个接收到信号的线程成功返回，其余调用pthread_join的线程则返回错误代码ESRCH。<br><br>　　在这一节里，我们编写了一个最简单的线程，并掌握了最常用的三个函数pthread_create，pthread_join和pthread_exit。下面，我们来了解线程的一些常用属性以及如何设置这些属性。<br><br>　　<strong>修改线程的属性</strong><br><br>　　在上一节的例子里，我们用pthread_create函数创建了一个线程，在这个线程中，我们使用了默认参数，即将该函数的第二个参数设为NULL。的确，对大多数程序来说，使用默认属性就够了，但我们还是有必要来了解一下线程的有关属性。<br><br>　　属性结构为pthread_attr_t，它同样在头文件/usr/include/pthread.h中定义，喜欢追根问底的人可以自己去查看。属性值不能直接设置，须使用相关函数进行操作，初始化的函数为pthread_attr_init，这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。<br><br>　　关于线程的绑定，牵涉到另外一个概念：轻进程（LWP：Light Weight Process）。轻进程可以理解为内核线程，它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的，一个轻进程可以控制一个或多个线程。默认状况下，启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的，这种状况即称为非绑定的。绑定状况下，则顾名思义，即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度，这是因为CPU时间片的调度是面向轻进程的，绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。<br><br>　　设置线程绑定状态的函数为pthread_attr_setscope，它有两个参数，第一个是指向属性结构的指针，第二个是绑定类型，它有两个取值：PTHREAD_SCOPE_SYSTEM（绑定的）和PTHREAD_SCOPE_PROCESS（非绑定的）。下面的代码即创建了一个绑定的线程。 <br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>#include &lt;pthread.h&gt;<br>pthread_attr_t attr;<br>pthread_t tid;<br><br>/*初始化属性值，均设为默认值*/<br>pthread_attr_init(&amp;attr);<br>pthread_attr_setscope(&amp;attr, PTHREAD_SCOPE_SYSTEM);<br><br>pthread_create(&amp;tid, &amp;attr, (void *) my_function, NULL);</td>
        </tr>
    </tbody>
</table>
<br>　　线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中，我们采用了线程的默认属性，即为非分离状态，这种情况下，原有的线程等待创建的线程结束。只有当pthread_join（）函数返回时，创建的线程才算终止，才能释放自己占用的系统资源。而分离线程不是这样子的，它没有被其他的线程所等待，自己运行结束了，线程也就终止了，马上释放系统资源。程序员应该根据自己的需要，选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate（pthread_attr_t *attr, int detachstate）。第二个参数可选为PTHREAD_CREATE_DETACHED（分离线程）和 PTHREAD _CREATE_JOINABLE（非分离线程）。这里要注意的一点是，如果设置一个线程为分离线程，而这个线程运行又非常快，它很可能在pthread_create函数返回之前就终止了，它终止以后就可能将线程号和系统资源移交给其他的线程使用，这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施，最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数，让这个线程等待一会儿，留出足够的时间让函数pthread_create返回。设置一段等待时间，是在多线程编程里常用的方法。但是注意不要使用诸如wait（）之类的函数，它们是使整个进程睡眠，并不能解决线程同步的问题。<br><br>　　另外一个可能常用的属性是线程的优先级，它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放，一般说来，我们总是先取优先级，对取得的值修改后再存放回去。下面即是一段简单的例子。 <br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>#include &lt;pthread.h&gt;<br>#include &lt;sched.h&gt;<br>pthread_attr_t attr;<br>pthread_t tid;<br>sched_param param;<br>int newprio=20;<br><br>pthread_attr_init(&amp;attr);<br>pthread_attr_getschedparam(&amp;attr, &amp;param);<br>param.sched_priority=newprio;<br>pthread_attr_setschedparam(&amp;attr, &amp;param);<br>pthread_create(&amp;tid, &amp;attr, (void *)myfunction, myarg);</td>
        </tr>
    </tbody>
</table>
<strong>线程的数据处理<br><br></strong>　　和进程相比，线程的最大优点之一是数据的共享性，各个进程共享父进程处沿袭的数据段，可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的，即同时不能运行一个函数的多个拷贝（除非使用不同的数据段）。在函数中声明的静态变量常常带来问题，函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址，则在一个线程调用该函数得到地址后使用该地址指向的数据时，别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义，这是为了防止编译器在优化时（如gcc中使用-OX参数）改变它们的使用方式。为了保护变量，我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面，我们就逐步介绍处理线程数据时的有关知识。<br><br>　　1、线程数据<br><br>　　在单线程的程序里，有两种基本的数据：全局变量和局部变量。但在多线程程序里，还有第三种数据类型：线程数据（TSD: Thread-Specific Data）。它和全局变量很象，在线程内部，各个函数可以象使用全局变量一样调用它，但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量errno，它返回标准的出错信息。它显然不能是一个局部变量，几乎每个函数都应该可以调用它；但它又不能是一个全局变量，否则在A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量，我们就必须使用线程数据。我们为每个线程数据创建一个键，它和这个键相关联，在各个线程里，都使用这个键来指代线程数据，但在不同的线程里，这个键代表的数据是不同的，在同一个线程里，它代表同样的数据内容。<br><br>　　和线程数据相关的函数主要有4个：创建一个键；为一个键指定线程数据；从一个键读取线程数据；删除键。<br><br>　　创建键的函数原型为：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>extern int pthread_key_create __P ((pthread_key_t *__key,void (*__destr_function) (void *)));</td>
        </tr>
    </tbody>
</table>
　　<br>　　第一个参数为指向一个键值的指针，第二个参数指明了一个destructor函数，如果这个参数不为空，那么当每个线程结束时，系统将调用这个函数来释放绑定在这个键上的<a href="http://diy.yesky.com/memoery/" target=_blank><font color=#000000><u>内存</u></font></a>块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用，为了让这个键只被创建一次。函数pthread_once声明一个初始化函数，第一次调用pthread_once时它执行这个函数，以后的调用将被它忽略。<br><br>　　在下面的例子中，我们创建一个键，并将它和某个数据相关联。我们要定义一个函数createWindow，这个函数定义一个图形窗口（数据类型为Fl_Window *，这是图形界面开发工具FLTK中的数据类型）。由于各个线程都会调用这个函数，所以我们使用线程数据。<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>/* 声明一个键*/<br>pthread_key_t myWinKey;<br>/* 函数 createWindow */<br>void createWindow ( void ) {<br>　Fl_Window * win;<br>　static pthread_once_t once= PTHREAD_ONCE_INIT;<br>　/* 调用函数createMyKey，创建键*/<br>　pthread_once ( &amp; once, createMyKey) ;<br>　/*win指向一个新建立的窗口*/<br>　win=new Fl_Window( 0, 0, 100, 100, "MyWindow");<br>　/* 对此窗口作一些可能的设置工作，如大小、位置、名称等*/<br>　setWindow(win);<br>　/* 将窗口指针值绑定在键myWinKey上*/<br>　pthread_setpecific ( myWinKey, win);<br>}<br><br>/* 函数 createMyKey，创建一个键，并指定了destructor */<br>void createMyKey ( void ) {<br>　pthread_keycreate(&amp;myWinKey, freeWinKey);<br>}<br><br>/* 函数 freeWinKey，释放空间*/<br>void freeWinKey ( Fl_Window * win){<br>　delete win;<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　这样，在不同的线程中调用函数createMyWin，都可以得到在线程内部均可见的窗口变量，这个变量通过函数pthread_getspecific得到。在上面的例子中，我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下：<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>　　extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));<br>　　extern void *pthread_getspecific __P ((pthread_key_t __key));</td>
        </tr>
    </tbody>
</table>
<br>　　这两个函数的参数意义和使用方法是显而易见的。要注意的是，用pthread_setspecific为一个键指定新的线程数据时，必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键，这个键占用的内存将被释放，但同样要注意的是，它只释放键占用的内存，并不释放该键关联的线程数据所占用的内存资源，而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。<br><br>　　2、互斥锁<br><br>　　互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见：假设各个线程向同一个文件顺序写入数据，最后得到的结果一定是灾难性的。<br><br>　　我们先看下面一段代码。这是一个读/写程序，它们公用一个缓冲区，并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态：有信息或没有信息。<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>void reader_function ( void );<br>void writer_function ( void ); <br><br>char buffer;<br>int buffer_has_item=0;<br>pthread_mutex_t mutex;<br>struct timespec delay;<br>void main ( void ){<br>　pthread_t reader;<br>　/* 定义延迟时间*/<br>　delay.tv_sec = 2;<br>　delay.tv_nec = 0;<br>　/* 用默认属性初始化一个互斥锁对象*/<br>　pthread_mutex_init (&amp;mutex,NULL);<br>　pthread_create(&amp;reader, pthread_attr_default, (void *)&amp;reader_function), NULL);<br>　writer_function( );<br>}<br><br>void writer_function (void){<br>　while(1){<br>　　/* 锁定互斥锁*/<br>　　pthread_mutex_lock (&amp;mutex);<br>　　if (buffer_has_item==0){<br>　　　buffer=make_new_item( );<br>　　　buffer_has_item=1;<br>　　}<br>　　/* 打开互斥锁*/<br>　　pthread_mutex_unlock(&amp;mutex);<br>　　pthread_delay_np(&amp;delay);<br>　}<br>}<br><br>void reader_function(void){<br>　while(1){<br>　　pthread_mutex_lock(&amp;mutex);<br>　　if(buffer_has_item==1){<br>　　　consume_item(buffer);<br>　　　buffer_has_item=0;<br>　　}<br>　　pthread_mutex_unlock(&amp;mutex);<br>　　pthread_delay_np(&amp;delay);<br>　}<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　这里声明了互斥锁变量mutex，结构pthread_mutex_t为不公开的数据类型，其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁，须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared，它有两个取值，PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步，后者用于同步本进程的不同线程。在上面的例子中，我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型，可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制，一般情况下，选用最后一个默认属性。<br><br>　　pthread_mutex_lock声明开始用互斥锁上锁，此后的代码直至调用pthread_mutex_unlock为止，均被上锁，即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时，如果该锁此时被另一个线程使用，那此线程被阻塞，即程序将等待到另一个线程释放此互斥锁。在上面的例子中，我们使用了pthread_delay_np函数，让线程睡眠一段时间，就是为了防止一个线程始终占据此函数。<br><br>　　上面的例子非常简单，就不再介绍了，需要提出的是在使用互斥锁的过程中很有可能会出现死锁：两个线程试图同时占用两个资源，并按不同的次序锁定相应的互斥锁，例如两个线程都需要锁定互斥锁1和互斥锁2，a线程先锁定互斥锁1，b线程先锁定互斥锁2，这时就出现了死锁。此时我们可以使用函数pthread_mutex_trylock，它是函数pthread_mutex_lock的非阻塞版本，当它发现死锁不可避免时，它会返回相应的信息，程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样，但最主要的还是要程序员自己在程序设计注意这一点。<br><br>　　3、条件变量<br><br>　　前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信，互斥锁一个明显的缺点是它只有两种状态：锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足，它常和互斥锁一起使用。使用时，条件变量被用来阻塞一个线程，当条件不满足时，线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量，它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来，条件变量被用来进行线承间的同步。 <br><br>　　条件变量的结构为pthread_cond_t，函数pthread_cond_init（）被用来初始化一个条件变量。它的原型为：<br><br>　　extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));<br><br>　　其中cond是一个指向结构pthread_cond_t的指针，cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构，和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用，默认值是PTHREAD_ PROCESS_PRIVATE，即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_ destroy（pthread_cond_t cond）。　<br><br>　　函数pthread_cond_wait（）使线程阻塞在一个条件变量上。它的函数原型为：<br><br>　　extern int pthread_cond_wait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));<br><br>　　线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒，但是要注意的是，条件变量只是起阻塞和唤醒线程的作用，具体的判断条件还需用户给出，例如一个变量是否为0等等，这一点我们从后面的例子中可以看到。线程被唤醒后，它将重新检查判断条件是否满足，如果还不满足，一般说来线程应该仍阻塞在这里，被等待被下一次唤醒。这个过程一般用while语句实现。<br><br>　　另一个用来阻塞线程的函数是pthread_cond_timedwait（），它的原型为：<br>　　extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex, __const struct timespec *__abstime));<br><br>　　它比函数pthread_cond_wait（）多了一个时间参数，经历abstime段时间后，即使条件变量不满足，阻塞也被解除。<br><br>　　函数pthread_cond_signal（）的原型为：<br><br>　　extern int pthread_cond_signal __P ((pthread_cond_t *__cond));<br><br>　　它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时，哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是，必须用保护条件变量的互斥锁来保护这个函数，否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出，从而造成无限制的等待。下面是使用函数pthread_cond_wait（）和函数pthread_cond_signal（）的一个简单的例子。<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>pthread_mutex_t count_lock;<br>pthread_cond_t count_nonzero;<br>unsigned count;<br>decrement_count　() {<br>　pthread_mutex_lock (&amp;count_lock);<br>　while(count==0) <br>　　pthread_cond_wait( &amp;count_nonzero, &amp;count_lock);<br>　　count=count -1;<br>　pthread_mutex_unlock (&amp;count_lock);<br>}<br><br>increment_count(){<br>　pthread_mutex_lock(&amp;count_lock);<br>　if(count==0)<br>　　pthread_cond_signal(&amp;count_nonzero);<br>　　count=count+1;<br>　pthread_mutex_unlock(&amp;count_lock);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　count值为0时，decrement函数在pthread_cond_wait处被阻塞，并打开互斥锁count_lock。此时，当调用到函数increment_count时，pthread_cond_signal（）函数改变条件变量，告知decrement_count（）停止阻塞。读者可以试着让两个线程分别运行这两个函数，看看会出现什么样的结果。<br><br>　　函数pthread_cond_broadcast（pthread_cond_t *cond）用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁，所以必须小心使用这个函数。<br><br>　　4、信号量<br><br>　　信号量本质上是一个非负的整数计数器，它被用来控制对公共资源的访问。当公共资源增加时，调用函数sem_post（）增加信号量。只有当信号量值大于０时，才能使用公共资源，使用后，函数sem_wait（）减少信号量。函数sem_trywait（）和函数pthread_ mutex_trylock（）起同样的作用，它是函数sem_wait（）的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数，它们都在头文件/usr/include/semaphore.h中定义。<br><br>　　信号量的数据类型为结构sem_t，它本质上是一个长整型的数。函数sem_init（）用来初始化一个信号量。它的原型为：<br><br>　　extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));<br><br>　　sem为指向信号量结构的一个指针；pshared不为０时此信号量在进程间共享，否则只能为当前进程的所有线程共享；value给出了信号量的初始值。<br><br>　　函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时，调用这个函数会使其中的一个线程不在阻塞，选择机制同样是由线程的调度策略决定的。<br><br>　　函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0，解除阻塞后将sem的值减一，表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait（）的非阻塞版本，它直接将信号量sem的值减一。<br><br>　　函数sem_destroy(sem_t *sem)用来释放信号量sem。<br><br>　　下面我们来看一个使用信号量的例子。在这个例子中，一共有4个线程，其中两个线程负责从文件读取数据到公共的缓冲区，另两个线程从缓冲区读取数据作不同的处理（加和乘运算）。<br><br>
<table align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>/* File sem.c */<br>#include &lt;stdio.h&gt;<br>#include &lt;pthread.h&gt;<br>#include &lt;semaphore.h&gt;<br>#define MAXSTACK 100<br>int stack[MAXSTACK][2];<br>int size=0;<br>sem_t sem;<br>/* 从文件1.dat读取数据，每读一次，信号量加一*/<br>void ReadData1(void){<br>　FILE *fp=fopen("1.dat","r");<br>　while(!feof(fp)){<br>　　fscanf(fp,"%d %d",&amp;stack[size][0],&amp;stack[size][1]);<br>　　sem_post(&amp;sem);<br>　　++size;<br>　}<br>　fclose(fp);<br>}<br>/*从文件2.dat读取数据*/<br>void ReadData2(void){<br>　FILE *fp=fopen("2.dat","r");<br>　while(!feof(fp)){<br>　　fscanf(fp,"%d %d",&amp;stack[size][0],&amp;stack[size][1]);<br>　　sem_post(&amp;sem);<br>　　++size;<br>　}<br>　fclose(fp);<br>}<br>/*阻塞等待缓冲区有数据，读取数据后，释放空间，继续等待*/<br>void HandleData1(void){<br>　while(1){<br>　　sem_wait(&amp;sem);<br>　　printf("Plus:%d+%d=%dn",stack[size][0],stack[size][1],<br>　　stack[size][0]+stack[size][1]);<br>　　--size;<br>　}<br>}<br><br>void HandleData2(void){<br>　while(1){<br>　　sem_wait(&amp;sem);<br>　　printf("Multiply:%d*%d=%dn",stack[size][0],stack[size][1],<br>　　stack[size][0]*stack[size][1]);<br>　　--size;<br>　}<br>}<br>int main(void){<br>　pthread_t t1,t2,t3,t4;<br>　sem_init(&amp;sem,0,0);<br>　pthread_create(&amp;t1,NULL,(void *)HandleData1,NULL);<br>　pthread_create(&amp;t2,NULL,(void *)HandleData2,NULL);<br>　pthread_create(&amp;t3,NULL,(void *)ReadData1,NULL);<br>　pthread_create(&amp;t4,NULL,(void *)ReadData2,NULL);<br>　/* 防止程序过早退出，让它在此无限期等待*/<br>　pthread_join(t1,NULL);<br>}</td>
        </tr>
    </tbody>
</table>
<br>　　在Linux下，我们用命令gcc -lpthread sem.c -o sem生成可执行文件sem。 我们事先编辑好数据文件1.dat和2.dat，假设它们的内容分别为1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ，我们运行sem，得到如下的结果：<br><br>Multiply:-1*-2=2<br>Plus:-1+-2=-3<br>Multiply:9*10=90<br>Plus:-9+-10=-19<br>Multiply:-7*-8=56<br>Plus:-5+-6=-11<br>Multiply:-3*-4=12<br>Plus:9+10=19<br>Plus:7+8=15<br>Plus:5+6=11<br><br>　　从中我们可以看出各个线程间的竞争关系。而数值并未按我们原先的顺序显示出来这是由于size这个数值被各个线程任意修改的缘故。这也往往是多线程编程要注意的问题。<br>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87518.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-12 19:47 <a href="http://www.cppblog.com/ivy-jie/articles/87518.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux进程通信</title><link>http://www.cppblog.com/ivy-jie/articles/87516.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 12 Jun 2009 11:45:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87516.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87516.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87516.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87516.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87516.html</trackback:ping><description><![CDATA[<p>1、管道(pipe)</p>
<p>用语具有亲缘关系进程间的通信</p>
<p>匿名一次性使用的，半双工。一个进程往输出端写管道，另一个进程从输入端读管道。<br>&nbsp; #include&lt;unistd.h&gt;<br>&nbsp; int pipe(int fd[2]);<br>&nbsp; fd[0]:表示读端<br>&nbsp; fd[1]:表示写端</p>
<p>&nbsp;</p>
<p>2、有名管道(named pipe)</p>
<p>允许无亲缘关系进程间的通信</p>
<p>有名管道，作为特别文件存储于文件系统中。有名管道一旦建立就存在于文件系统中，除非显示的unlink<br>&nbsp; #include&lt;sys/tpes.h&gt;<br>&nbsp; #include&lt;sys/stat.h&gt;<br>&nbsp; int mknod(const char *path,mode_t mod,dev_t dev);<br>&nbsp; int mkfifo(const char *path,mode_t mode);<br>&nbsp; path:创建有名管道的全路径名<br>&nbsp; mod:创建有名管道的模式，指存取权限<br>&nbsp; dev:设备值，该值取决于文件创建的种类，它只在创建设备文件时才会用到<br>&nbsp; 注意:有名管道创建后就可以使用了，有名管道和管道的使用方法基本是相同的。只是使用有名管道的时候必须先调用open()将其打开<br>&nbsp; 因为有名管道是一个存在于硬盘上的文件，而管道是存在于内存中的特殊文件</p>
<p>&nbsp; 下面的程序一个读管道，另一个写管道，这两个函数用的是非阻塞读写管道</p>
<p>&nbsp;</p>
<p>#include &lt;sys/types.h&gt;<br>#include &lt;sys/stat.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;fcntl.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#define FIFO "/tmp/myfifo"</p>
<p>main(int argc, char** argv)<br>{<br>&nbsp;&nbsp;&nbsp; char buf_r[100];<br>&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp; int nread;<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; if((mkfifo(FIFO,O_CREAT|O_EXCL)&lt;0)&amp;&amp;(errno!=EEXIST))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("cannot create fifoserver\n");<br>&nbsp;&nbsp;&nbsp; printf("Preparing for reading bytes...\n");<br>&nbsp;&nbsp;&nbsp; memset(buf_r,0,sizeof(buf_r));</p>
<p>&nbsp;&nbsp;&nbsp; fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);<br>&nbsp;&nbsp;&nbsp; if(fd==-1)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("open");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; while(1)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(buf_r,0,sizeof(buf_r));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((nread=read(fd,buf_r,100))==-1){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno==EAGAIN)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("no data yet\n");<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("read %s from FIFO\n",buf_r);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; pause();<br>&nbsp;&nbsp;&nbsp; unlink(FIFO);<br>}</p>
<p>&nbsp;</p>
<p><br>#include &lt;sys/types.h&gt;<br>#include &lt;sys/stat.h&gt;<br>#include &lt;errno.h&gt;<br>#include &lt;fcntl.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;<br>#define FIFO "/tmp/myfifo"</p>
<p>main(int argc,char** argv)<br>{<br>&nbsp;&nbsp;&nbsp; int fd;<br>&nbsp;&nbsp;&nbsp; char w_buf[100];<br>&nbsp;&nbsp;&nbsp; int nwrite;<br>&nbsp;&nbsp;&nbsp; if(fd==-1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno==ENXIO)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("open error; no reading process\n");<br>&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);<br>&nbsp;&nbsp;&nbsp; if(argc==1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Please send something\n");<br>&nbsp;&nbsp;&nbsp; strcpy(w_buf,argv[1]);</p>
<p>&nbsp;&nbsp;&nbsp; if((nwrite=write(fd,w_buf,100))==-1)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno==EAGAIN)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("The FIFO has not been read yet.Please try later\n");<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("write %s to the FIFO\n",w_buf);<br>}</p>
<p><br>3、信号量</p>
<p>主要用于进程间及同一进程不同线程之间的同步手段</p>
<p><br>4、消息队列</p>
<p>克服信号量有限，可写可读</p>
<p><br>5、信号（Signal）</p>
<p>比较复杂，用于通知接受进程有某事发生</p>
<p><br>6、共享内存</p>
<p>最有用的进程间通信方式，使得多个进程可访问同以内存空间，但需要依靠某种同步机制</p>
<p>第一步：创建共享内存</p>
<p>第二步：映射共享内存</p>
<p><br>7、套接字</p>
<p>不同机器之间通信</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87516.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-12 19:45 <a href="http://www.cppblog.com/ivy-jie/articles/87516.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>