⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dup-dup2用法_一点记忆.htm

📁 DUP2用法
💻 HTM
📖 第 1 页 / 共 2 页
字号:
href="http://hi.baidu.com/realxfliu">一点记忆</A></DIV>
<DIV class=desc></DIV>
<DIV id=tabline></DIV>
<DIV id=tab><A href="http://hi.baidu.com/realxfliu">主页</A><A class=on 
href="http://hi.baidu.com/realxfliu/blog">博客</A><A 
href="http://hi.baidu.com/realxfliu/album">相册</A><SPAN>|</SPAN><A 
href="http://hi.baidu.com/realxfliu/profile">个人档案</A> </DIV></DIV>
<DIV class=stage>
<DIV class=stagepad>
<DIV style="WIDTH: 100%">
<TABLE class=modth cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD class=modtl width=7>&nbsp;</TD>
    <TD class=modtc noWrap>
      <DIV class=modhead><SPAN class=modtit>查看文章</SPAN></DIV></TD>
    <TD class=modtc noWrap align=right></TD>
    <TD class=modtr width=7>&nbsp;</TD></TR></TBODY></TABLE>
<DIV class=modbox id=m_blog>
<DIV class=tit>dup/dup2用法</DIV>
<DIV class=date>2007-03-07 15:15</DIV>
<TABLE style="TABLE-LAYOUT: fixed">
  <TBODY>
  <TR>
    <TD>
      <DIV class=cnt>
      <P>相信大部分在Unix/Linux下编程的程序员手头上都有《Unix环境高级编程》(APUE)这本超级经典巨著。作者在该书中讲解dup/dup2之前曾经讲过“文件共享”,这对理解dup/dup2还是很有帮助的。这里做简单摘录以备在后面的分析中使用:<BR>Stevens 
      said:<BR>(1) 
      每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可将视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:<BR>&nbsp;&nbsp; 
      (a) 文件描述符标志。<BR>&nbsp;&nbsp; (b) 指向一个文件表项的指针。<BR>(2) 
      内核为所有打开文件维持一张文件表。每个文件表项包含:<BR>&nbsp;&nbsp; (a) 
      文件状态标志(读、写、增写、同步、非阻塞等)。<BR>&nbsp;&nbsp; (b) 当前文件位移量。<BR>&nbsp;&nbsp; (c) 
      指向该文件v节点表项的指针。<BR>图示:<BR>&nbsp;&nbsp; 文件描述符表<BR>&nbsp;&nbsp; 
      ------------<BR>fd0&nbsp; 0&nbsp;&nbsp; | p0&nbsp; -------------&gt; 文件表0 
      ---------&gt; vnode0<BR>&nbsp;&nbsp; ------------<BR>fd1&nbsp; 
      1&nbsp;&nbsp; | p1&nbsp; -------------&gt; 文件表1 ---------&gt; 
      vnode1<BR>&nbsp;&nbsp; ------------<BR>fd2&nbsp; 2&nbsp;&nbsp; | p2&nbsp; 
      <BR>&nbsp;&nbsp; ------------<BR>fd3&nbsp; 3&nbsp;&nbsp; | p3&nbsp; 
      <BR>&nbsp;&nbsp; ------------<BR>... ...<BR>... ...<BR>&nbsp;&nbsp; 
      ------------</P>
      <P>一、单个进程内的dup和dup2<BR>假设进程A拥有一个已打开的文件描述符fd3,它的状态如下:<BR>&nbsp; 
      进程A的文件描述符表(before dup2)<BR>&nbsp;&nbsp; ------------<BR>fd0&nbsp; 
      0&nbsp;&nbsp; | p0&nbsp; <BR>&nbsp;&nbsp; ------------<BR>fd1&nbsp; 
      1&nbsp;&nbsp; | p1&nbsp; -------------&gt; 文件表1 ---------&gt; 
      vnode1<BR>&nbsp;&nbsp; ------------<BR>fd2&nbsp; 2&nbsp;&nbsp; | p2&nbsp; 
      <BR>&nbsp;&nbsp; ------------<BR>fd3&nbsp; 3&nbsp;&nbsp; | p3&nbsp; 
      -------------&gt; 文件表2 ---------&gt; vnode2<BR>&nbsp;&nbsp; 
      ------------<BR>... ...<BR>... ...<BR>&nbsp;&nbsp; ------------</P>
      <P>经下面调用:<BR>n_fd = dup2(fd3, STDOUT_FILENO);后进程状态如下:</P>
      <P>进程A的文件描述符表(after dup2)<BR>&nbsp;&nbsp; ------------<BR>fd0&nbsp; 
      0&nbsp;&nbsp; | p0&nbsp; <BR>&nbsp;&nbsp; ------------<BR>n_fd 
      1&nbsp;&nbsp; | p1&nbsp; ------------<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      \<BR>fd2&nbsp; 2&nbsp;&nbsp; | 
      p2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      _\|<BR>fd3&nbsp; 3&nbsp;&nbsp; | p3&nbsp; -------------&gt; 文件表2 
      ---------&gt; vnode2<BR>&nbsp;&nbsp; ------------<BR>... ...<BR>... 
      ...<BR>&nbsp;&nbsp; ------------<BR>解释如下:<BR>n_fd = dup2(fd3, 
      STDOUT_FILENO)表示n_fd与fd3共享一个文件表项(它们的文件表指针指向同一个文件表项),n_fd在文件描述符表中的位置为 
      STDOUT_FILENO的位置,而原先的STDOUT_FILENO所指向的文件表项被关闭,我觉得上图应该很清晰的反映出这点。按照上面的解释我们 
      就可以解释CU中提出的一些问题:<BR>(1) "dup2的第一个参数是不是必须为已打开的合法filedes?" -- 答案:必须。<BR>(2) 
      "dup2的第二个参数可以是任意合法范围的filedes值么?" -- 答案:可以,在Unix其取值区间为[0,255]。</P>
      <P>另外感觉理解dup2的一个好方法就是把fd看成一个结构体类型,就如上面图形中画的那样,我们不妨把之定义为:<BR>struct fd_t 
      {<BR>&nbsp;int index;<BR>&nbsp;filelistitem 
      *ptr;<BR>};<BR>然后dup2匹配index,修改ptr,完成dup2操作。</P>
      <P>在学习dup2时总是碰到“重定向”一词,上图完成的就是一个“从标准输出到文件的重定向”,经过dup2后进程A的任何目标为STDOUT_FILENO的I/O操作如printf等,其数据都将流入fd3所对应的文件中。下面是一个例子程序:<BR>#define 
      TESTSTR "Hello dup2\n"<BR>int main() 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      int&nbsp;&nbsp;&nbsp;&nbsp; fd3;</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd3 = open("testdup2.dat", 
      0666);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd &lt; 0) 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("open 
      error\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      exit(-1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (dup2(fd3, STDOUT_FILENO) 
      &lt; 0) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("err in dup2\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf(TESTSTR);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 
      0;<BR>}<BR>其结果就是你在testdup2.dat中看到"Hello dup2"。</P>
      <P>二、重定向后恢复<BR>CU上有这样一个帖子,就是如何在重定向后再恢复原来的状态?首先大家都能想到要保存重定向前的文件描述符。那么如何来保存呢,象下面这样行么?<BR>int 
      s_fd = STDOUT_FILENO;<BR>int n_fd = dup2(fd3, 
      STDOUT_FILENO);<BR>还是这样可以呢?<BR>int s_fd = dup(STDOUT_FILENO);<BR>int n_fd 
      = dup2(fd3, STDOUT_FILENO);<BR>这 
      两种方法的区别到底在哪呢?答案是第二种方案才是正确的,分析如下:按照第一种方法,我们仅仅在"表面上"保存了相当于fd_t(按照我前面说的理解方 
      法)中的index,而在调用dup2之后,ptr所指向的文件表项由于计数值已为零而被关闭了,我们如果再调用dup2(s_fd, 
      fd3)就会出错(出错原因上面有解释)。而第二种方法我们首先做一下复制,复制后的状态如下图所示:<BR>进程A的文件描述符表(after 
      dup)<BR>&nbsp;&nbsp; ------------<BR>fd0&nbsp; 0&nbsp;&nbsp; | p0&nbsp; 
      <BR>&nbsp;&nbsp; ------------<BR>fd1&nbsp; 1&nbsp;&nbsp; | p1&nbsp; 
      -------------&gt; 文件表1 ---------&gt; vnode1<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      /|<BR>fd2&nbsp; 2&nbsp;&nbsp; | 
      p2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      /<BR>fd3&nbsp; 3&nbsp;&nbsp; | p3&nbsp; -------------&gt; 文件表2 
      ---------&gt; vnode2<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      /<BR>s_fd 4&nbsp;&nbsp; | p4&nbsp; ------/&nbsp; <BR>&nbsp;&nbsp; 
      ------------<BR>... ...<BR>... ...<BR>&nbsp;&nbsp; ------------</P>
      <P>调用dup2后状态为:<BR>进程A的文件描述符表(after dup2)<BR>&nbsp;&nbsp; 
      ------------<BR>fd0&nbsp; 0&nbsp;&nbsp; | p0&nbsp; <BR>&nbsp;&nbsp; 
      ------------<BR>n_fd 1&nbsp;&nbsp; | p1&nbsp; ------------<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      \<BR>fd2&nbsp; 2&nbsp;&nbsp; | 
      p2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      _\|<BR>fd3&nbsp; 3&nbsp;&nbsp; | p3&nbsp; -------------&gt; 文件表2 
      ---------&gt; vnode2<BR>&nbsp;&nbsp; ------------<BR>s_fd 4&nbsp;&nbsp; | 
      p4&nbsp; -------------&gt;文件表1 ---------&gt; vnode1 <BR>&nbsp;&nbsp; 
      ------------<BR>... ...<BR>... ...<BR>&nbsp;&nbsp; 
      ------------<BR>dup(fd)的语意是返回的新的文件描述符与fd共享一个文件表项。就如after 
      dup图中的s_fd和fd1共享文件表1一样。</P>
      <P>确定第二个方案后重定向后的恢复就很容易了,只需调用dup2(s_fd, n_fd);即可。下面是一个完整的例子程序:<BR>#define 
      TESTSTR "Hello dup2\n"<BR>#define SIZEOFTESTSTR 11</P>
      <P>int main() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      int&nbsp;&nbsp;&nbsp;&nbsp; 
      fd3;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      int&nbsp;&nbsp;&nbsp;&nbsp; 
      s_fd;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      int&nbsp;&nbsp;&nbsp;&nbsp; n_fd;</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd3 = open("testdup2.dat", 
      0666);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd3 &lt; 0) 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("open 
      error\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      exit(-1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 复制标准输出描述符 
      */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s_fd = 
      dup(STDOUT_FILENO);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (s_fd 
      &lt; 0) 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("err in dup\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
}</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 重定向标准输出到文件 
      */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n_fd = dup2(fd3, 
      STDOUT_FILENO);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n_fd 
      &lt; 0) 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("err in dup2\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(STDOUT_FILENO, 
      TESTSTR, SIZEOFTESTSTR);&nbsp;&nbsp; /* 写入testdup2.dat中 */</P>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 重定向恢复标准输出 
      */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (dup2(s_fd, n_fd) &lt; 
      0) 
      {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      printf("err in dup2\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; write(STDOUT_FILENO, 
      TESTSTR, SIZEOFTESTSTR);&nbsp;/* 输出到屏幕上 
      */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<BR>}<BR>注 
      意这里我在输出数据的时候我是用了不带缓冲的write库函数,如果使用带缓冲区的printf,则最终结果为屏幕上输出两行"Hello 
      dup2",而文件testdup2.dat中为空,原因就是缓冲区作怪,由于最终的目标是屏幕,所以程序最后将缓冲区的内容都输出到屏幕。</P>
      <P><BR>三、父子进程间的dup/dup2<BR>由fork调用得到的子进程和父进程的相同文件描述符共享同一文件表项,如下图所示:<BR>父进程A的文件描述符表<BR>&nbsp;&nbsp; 
      ------------<BR>fd0&nbsp; 0&nbsp;&nbsp; | p0&nbsp; <BR>&nbsp;&nbsp; 
      ------------<BR>fd1&nbsp; 1&nbsp;&nbsp; | p1&nbsp; -------------&gt; 文件表1 
      ---------&gt; vnode1<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      /|\<BR>fd2&nbsp; 2&nbsp;&nbsp; | 
      p2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      &nbsp;|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>子进程B的文件描述符表&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      |<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      |<BR>fd0&nbsp; 0&nbsp;&nbsp; | 
      p0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>&nbsp;&nbsp; 
      ------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      |<BR>fd1&nbsp; 1&nbsp;&nbsp; | p1&nbsp; 
      ---------------------|<BR>&nbsp;&nbsp; ------------<BR>fd2&nbsp; 
      2&nbsp;&nbsp; | p2&nbsp; <BR>&nbsp;&nbsp; 
      ------------<BR>所以恰当的利用dup2和dup可以在父子进程之间建立一条“沟通的桥梁”。这里不详述。</P>
      <P>四、小结<BR>灵活的利用dup/dup2可以给你带来很多强大的功能,花了一些时间总结出上面那么多,不知道自己理解的是否透彻,只能在以后的实践中慢慢探索了。</P>
      <P>参考资料:<BR>1、《Unix环境高级编程》</P></DIV></TD></TR></TBODY></TABLE><BR>
<DIV class=opt><A title=查看该分类中所有文章 
href="http://hi.baidu.com/realxfliu/blog/category/&Iuml;&micro;&Iacute;&sup3;±à&sup3;&Igrave;">类别:系统编程</A> | <A 
title=将此文章添加到百度搜藏 
onclick="window.open('http://cang.baidu.com/do/add?it='+encodeURIComponent('dup/dup2用法'+'_百度空间')+'&amp;iu='+encodeURIComponent(location.href)+'&amp;fr=sp#nw=1','_s','scrollbars=no,width=600,height=450,right=75,top=20,status=no,resizable=yes'); return false;" 
href="http://cang.baidu.com/do/add" target=_blank>添加到搜藏</A> | 浏览(<SPAN 
id=result></SPAN>) </DIV>
<DIV class=line></DIV>
<SCRIPT language=JavaScript>
allkey=allkey+"da6592d65c918b2f06088bb9_6a31bcec24c1952763d09fa6_";
</SCRIPT>

<DIV id=in_comment><A name=comment></A>
<DIV class=tit>网友评论:</DIV>
<SCRIPT>
function writecmt(type,id,cmtname,cmturl,cmttime){
	var html1="";
	if(type==1){
		if(cmturl==""){
			html1="<a name='"+id+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
		}else{
			html1="<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
		}
	}else{
		if(cmtname=="匿名网友"){
			if(cmturl==""){
				html1="<a name='"+id+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span> ";
			}else{
				html1="<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class='date'>"+cmttime+"</span>";
			}
		}else{
			if(cmturl==""){
				html1="<div class='f14' style='display:inline'>网友:<a name='"+id+"'>"+cmtname+"</a> - <span class=\"date\">"+cmttime+"</span></div>";
			}else{
				html1="<div class='f14' style='display:inline'>网友:<a name='"+id+"' href='"+cmturl+"' target='_blank' title='"+cmturl+"'>"+cmtname+"</a> - <span class=\"date\">"+cmttime+"</span></div>";
			}
		}
	}
	document.write(html1);
}

</SCRIPT>

<DIV id=page></DIV></DIV>
<DIV id=in_send>
<FORM id=popFormSubmit name=form1 onsubmit="return checkcmtform()" 
action=/realxfliu/commit method=post><INPUT type=hidden value=8 name=ct> <INPUT 
type=hidden value=1 name=cm> <INPUT type=hidden value=6a31bcec24c1952763d09fa6 
name=spBlogID>
<SCRIPT language=JavaScript>
	document.write("<input type='hidden' name='spRefURL' value='"+window.location.href+"'>");
</SCRIPT>
 
<DIV class=tit>发表评论:</DIV>
<TABLE cellSpacing=5 cellPadding=0 width=620 border=0>
  <TBODY>
  <TR>
    <TD class=f14>姓 名:</TD>
    <TD><INPUT id=spBlogCmtor style="WIDTH: 220px" onfocus=hidErr(1); 
      maxLength=49 onchange="checkname('spBlogCmtor')" name=spBlogCmtor>
      <DIV id=nmerror style="DISPLAY: none">*姓名最长为50字节</DIV></TD></TR>
  <TR id=1_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=1_err_con></DIV></TD></TR>
  <TR>
    <TD class=f14>网址或邮箱:</TD>
    <TD><INPUT id=spBlogCmtURL style="WIDTH: 360px" onfocus=hidErr(2); 
      maxLength=128 onchange="checkeandu('spBlogCmtURL')" name=spBlogCmtURL> 
    (选填)</TD>
    <SCRIPT>
G("spBlogCmtor").value="";
G("spBlogCmtURL").value="";
</SCRIPT>
  </TR>
  <TR id=2_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=2_err_con></DIV></TD></TR>
  <TR>
    <TD class=f14 vAlign=top>内 容:</TD>
    <TD><TEXTAREA id=spBlogCmtText style="WIDTH: 520px; HEIGHT: 155px" onfocus=showverkey();hidErr(3); name=spBlogCmtText></TEXTAREA>
      <SCRIPT>
G("spBlogCmtor").value=G("spBlogCmtor").defaultValue;
G("spBlogCmtText").value="";
</SCRIPT>
       </TD></TR>
  <TR id=3_err style="DISPLAY: none">
    <TD>&nbsp;</TD>
    <TD>
      <DIV class=error id=3_err_con></DIV></TD></TR>
  <TR id=vercode>
    <TD class=f14 vAlign=top>验证码:</TD>
    <TD vAlign=top><INPUT type=hidden 
      value=36138FE969ACB7CC8F89FBB5EC40FB9614F33AD1B5EFE0AFE1352D7F696707E73B28CAA6DB54A6DF8FC20F3D2E74F5BD5E0DC29E1EBBC14350BD6885B51872FA 
      name=spVcode> <INPUT maxLength=4 size=6 name=spVerifyKey 
      autocomplete="off">请输入下图中的四位验证码,字母不区分大小写。<BR>
      <SCRIPT language=JavaScript>

			var imgsrc="http://post.baidu.com/cgi-bin/genimg?36138FE969ACB7CC8F89FBB5EC40FB9614F33AD1B5EFE0AFE1352D7F696707E73B28CAA6DB54A6DF8FC20F3D2E74F5BD5E0DC29E1EBBC14350BD6885B51872FA";
			document.write("<img id='verifypic' src='"+imgsrc+"' width='120' height='40'>");

			function newverifypic(){
				document.getElementById("verifypic").src = imgsrc +"&t="+ Math.random();
			}
		</SCRIPT>
      <A title=看不清左边的字符 href="javascript:newverifypic();">看不清?</A> </TD></TR>
  <TR>
    <TD class=f14 vAlign=top>&nbsp;</TD>
    <TD class=f14 
  vAlign=top><INPUT type=submit value=发表评论 name=btn_ok></TD></TR></TBODY></TABLE></FORM></DIV><BR></DIV>
<TABLE height=8 cellSpacing=0 cellPadding=0 width="100%" border=0>
  <TBODY>
  <TR>
    <TD class=modbl width=7>&nbsp;</TD>
    <TD class=modbc>&nbsp;</TD>
    <TD class=modbr width=7>&nbsp;</TD></TR></TBODY></TABLE></DIV></DIV></DIV></DIV>
<SCRIPT language=javascript>
<!--
var hstr="/realxfliu/brwstat?key1=1";
document.write("<script src='"+hstr+"&key2="+allkey+"'><\/script>");
//-->
</SCRIPT>
<BR>
<CENTER>
<DIV id=ft>&copy;2007 Baidu</DIV></CENTER>
<SCRIPT>
if(document.getElementById("m_blog"))
{
	var imgarray = document.getElementById("m_blog").getElementsByTagName('img');
	var imgw = document.getElementById("m_blog").offsetWidth;
	imgw =imgw-40;
	for(var i=0; i<imgarray.length; i++){
	if(imgarray[i].className=="blogimg" && imgarray[i].width>=imgw) imgarray[i].width=imgw;
	}
}
</SCRIPT>
</CENTER><IMG style="DISPLAY: none" src=""> </BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -