<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Linux系统编程(3)文件读写操作]]></title><description><![CDATA[<h1>Linux系统编程(4)文件读写操作</h1>
<h3>通过read()读文件</h3>
<pre><code class="language-C">#include &lt;unistd.h&gt;

ssize_t read(int fd, void *buf, size_t count);
</code></pre>
<h2>每个参数分别是什么</h2>
<h3>参数 1：<code>int fd</code></h3>
<ul>
<li>
<p dir="auto"><code>fd</code> 是<strong>文件描述符</strong>，一般来自：</p>
<ul>
<li>
<p dir="auto"><code>open()</code> 打开文件</p>
</li>
<li>
<p dir="auto">或者标准输入 <code>0</code>（<code>STDIN_FILENO</code>）</p>
</li>
</ul>
</li>
<li>
<p dir="auto">内核用 <code>fd</code> 找到你打开的“文件对象 + 当前偏移量”等状态。</p>
</li>
</ul>
<h3>参数 2：<code>void *buf</code></h3>
<ul>
<li>
<p dir="auto">你提供的<strong>缓冲区地址</strong>，用来装读出来的数据。</p>
</li>
<li>
<p dir="auto">必须保证这块内存<strong>可写</strong>并且至少有 <code>count</code> 个字节容量。</p>
</li>
<li>
<p dir="auto">例如：</p>
</li>
</ul>
<pre><code class="language-C++">char buf[4096];
read(fd,buf,sizeof(buf));
</code></pre>
<h3>参数 3：<code>size_t count</code></h3>
<ul>
<li>
<p dir="auto">你希望这次最多读取多少字节。</p>
</li>
<li>
<p dir="auto">注意：这是“最多”，不是“必须读满”。</p>
</li>
</ul>
<hr />
<p dir="auto">每次调用read()函数，会从fd指向的文件的当前偏移开始读取len字节到buf所指向的内存中。执行成功时，返回写入buf中的字节数；出错时，返回-1，并设置errno值。fd的文件位置指针会向前移动，移动的长度由读取到的字节数决定。如果fd所指向的对象不支持seek操作（比如字符设备文件），则读操作总是从“当前”位置开始。<br />
基本用法很简单。下面这个例子就是从文件描述符fd所指向的文件中读取数据并保存到word中。读取的字节数即unsigned long类型的大小，在Linux的32位系统上是4字节，在64位系统上是8字节。成功时，返回读取的字节数；出错时，返回-1：**</p>
<pre><code class="language-C">usigned long world;
ssize_t nr
nr = read(fd,&amp;world,sizeof(world));
if(nr==-1)......
</code></pre>
<p dir="auto">返回值三种情况（必须背熟）</p>
<h3>情况 A：返回值 <code>&gt; 0</code></h3>
<ul>
<li>
<p dir="auto">表示<strong>实际读取到的字节数</strong>，比如返回 1000，就说明 <code>buf[0..999]</code> 有效。</p>
</li>
<li>
<p dir="auto"><strong>不保证等于 <code>count</code></strong>：读文件也可能因为系统缓冲、信号等原因“读不满”。</p>
</li>
</ul>
<h3>情况 B：返回值 <code>== 0</code></h3>
<ul>
<li>
<p dir="auto">表示 <strong>EOF（文件结束）</strong>：已经读到文件末尾，再读就没有数据了。</p>
</li>
<li>
<p dir="auto">对普通文件，这是“读完”的标志。</p>
</li>
</ul>
<h3>情况 C：返回值 <code>== -1</code></h3>
<ul>
<li>
<p dir="auto">表示出错，具体原因在全局变量 <code>errno</code> 里。</p>
</li>
<li>
<p dir="auto">最常见的你需要认识两个：</p>
<ul>
<li>
<p dir="auto"><code>errno == EINTR</code>：被信号打断，通常<strong>重试</strong>即可</p>
</li>
<li>
<p dir="auto"><code>errno == EAGAIN / EWOULDBLOCK</code>：非阻塞读时没数据（普通文件很少遇到）</p>
</li>
</ul>
</li>
</ul>
<hr />
<h3>读入所有字节</h3>
<p dir="auto"><strong>诚如前面所描述的，由于调用read()会有很多不同情况，如果希望处理所有错误并且真正每次读入len个字节（至少读到EOF），那么之前简单“粗暴”的read()调用并不合理。要实现这一点，需要有个循环和一些条件语句，如下：</strong></p>
<pre><code class="language-C++">ssize_t ret;//声明返回值
size_t length;//声明需要读取的字节长度
while(len&gt;0 &amp;&amp; ret=read(fd,buf,length) ){
  if(ret==-1){
   if(error==EINTR) continue;
  perror("read"); 
  break;
}
  len-=(size_t)ret;
  buf+=ret;
}
</code></pre>
<p dir="auto"><code>read()</code> 读出来的是<strong>原始字节</strong>，它只返回“实际读了多少字节”，<strong>不会自动在末尾补 <code>'\0'</code></strong>。</p>
<p dir="auto">所以：</p>
<ul>
<li>如果把 <code>read</code> 的结果当作 <strong>C 字符串</strong>来用，必须自己加终止符，并且要预留一个字节空间。<br />
实例：</li>
</ul>
<pre><code class="language-C">char buf[1024];
ssize_t n = read(fd, buf, sizeof(buf) - 1); // 预留 1 字节给 '\0'
if (n &gt; 0) {
    buf[n] = '\0';  // 手动补 '\0'
    printf("%s", buf);
}
</code></pre>
<p dir="auto">补充两点常见坑：</p>
<ul>
<li>
<p dir="auto"><code>read</code> 读到的内容里<strong>可能本来就包含 <code>'\0'</code></strong>（比如二进制文件），这时即使你手动补了终止符，<code>printf("%s")</code> 也会在中间的 <code>'\0'</code> 提前截断。</p>
</li>
<li>
<p dir="auto">如果 <code>n == sizeof(buf)-1</code>，上面这种写法仍然安全，因为你预留了 1 字节。</p>
</li>
</ul>
<hr />
<h3>阻塞IO（先了解即可）</h3>
<p dir="auto"><strong>有时，开发人员不希望read()调用在没有数据可读时阻塞在那里。相反地，他们希望调用立即返回，表示没有数据可读。这种方式称为非阻塞I/O，它支持应用以非阻塞模式执行I/O操作，因而如果是读取多个文件，以防错过其他文件中的可用数据。<br />
因此，需要额外检查errno值是否为EAGAIN。正如前面所讨论的，如果文件描述符以非阻塞模式打开（即open()调用中指定参数为O_NONBLOCK，open()调用的参数”），并且没有数据可读，read()调用会返回-1，并设置errno值为EAGAIN，而不是阻塞模式。当以非阻塞模式读文件时，必须检查EAGAIN，否则可能因为丢失数据导致严重错误。你可能会用到如下代码：</strong></p>
<pre><code class="language-C++"> char buf[MAX_SIZE];
 usigned int len;
 start:
 ssize_t ret = read(fd,buf,len);
 if (ret==-1){
	 if(error==ENTER) goto satrt;
	else if(error==EAGAIN)....;
	else...;
 }
</code></pre>
<h3>怎么打开非阻塞设置</h3>
<p dir="auto">方法 A：<code>open</code> 时带 <code>O_NONBLOCK</code></p>
<pre><code class="language-C">int fd = open("a.txt",O_RDONLY | O_NONBLOCK); 
</code></pre>
<p dir="auto">方法 B：对已有 fd 用 <code>fcntl</code> 打开非阻塞</p>
<pre><code class="language-C">#include &lt;fcntl.h&gt;

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
</code></pre>
<h2>非阻塞读的正确姿势：配合 select/poll/epoll</h2>
<p dir="auto">非阻塞不是让你疯狂 <code>while(read...)</code> 空转（那会 CPU 100%），而是：</p>
<p dir="auto">1）先等“可读事件”（有数据了再读）<br />
2）再 <code>read()</code>，并且通常要<strong>一直读到 EAGAIN</strong> 才停（把缓冲区读空）</p>
<pre><code class="language-C">#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
#include &lt;stdio.h&gt;
#include &lt;poll.h&gt;

int main() {
    int fd = STDIN_FILENO;

    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);

    char buf[1024];

    while (1) {
        struct pollfd pfd = { .fd = fd, .events = POLLIN };
        int r = poll(&amp;pfd, 1, 5000); // 等 5 秒
        if (r == 0) { puts("timeout..."); continue; }
        if (r &lt; 0) { perror("poll"); break; }

        // 可读了：把当前能读的都读出来（直到 EAGAIN）
        while (1) {
            ssize_t n = read(fd, buf, sizeof(buf));
            if (n &gt; 0) {
                write(STDOUT_FILENO, buf, (size_t)n);
                continue;
            }
            if (n == 0) return 0; // EOF
            if (errno == EINTR) continue;
            if (errno == EAGAIN || errno == EWOULDBLOCK) break;
            perror("read");
            return 1;
        }
    }
    return 0;
}
</code></pre>
]]></description><link>http://forum.d2learn.org/topic/158/linux系统编程-3-文件读写操作</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Mar 2026 23:14:34 GMT</lastBuildDate><atom:link href="http://forum.d2learn.org/topic/158.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 22 Jan 2026 08:41:06 GMT</pubDate><ttl>60</ttl></channel></rss>