write系统调用及示例

我们来介绍与 read 和 open 紧密配合使用的 write 函数。如果说 read 是从文件描述符“拿”数据,那么 write 就是向文件描述符“放”数据。


1. 函数介绍

write 是一个 Linux 系统调用,用于将数据从程序的缓冲区写入到由文件描述符 fd 指定的文件、管道、套接字或其他输出流中。它是程序向外部(如文件、屏幕、网络)发送数据的基本方式之一。


2. 函数原型

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

3. 功能

尝试将 count 个字节的数据从 buf 指向的缓冲区写入到文件描述符 fd 所关联的文件或资源中。


4. 参数

  • int fd: 这是目标文件描述符。它标识了数据要写入的目标,比如通过 open 打开的文件、标准输出 (STDOUT_FILENO)、标准错误 (STDERR_FILENO) 或一个网络套接字等。
  • const void *buf: 这是一个指向包含待写入数据的缓冲区的指针。由于数据是从这个缓冲区读取并写出去的,所以指针被声明为 const,表明函数不会修改这块内存中的数据。
  • size_t count: 这是要写入的字节数。函数会尝试写入从 buf 开始的 count 个字节。

5. 返回值

write 函数返回实际成功写入的字节数。

  • 成功时:
    • 返回实际写入的字节数 (0 <= 返回值 <= count)。
    • 在阻塞模式下,通常返回值会等于 count。但在某些情况下(如写入管道或网络套接字时缓冲区满),返回值可能小于 count。这时,程序通常需要循环调用 write 来写入剩余的数据。
  • 出错时:
    • 返回 -1,并设置全局变量 errno 来指示具体的错误类型(例如 EAGAINEBADFEFAULTENOSPC 等)。

重要提示绝对不能仅仅通过检查 write 的返回值是否为 -1 来判断写操作是否完全成功。必须检查返回值是否等于请求写入的字节数 count,或者在返回值小于 count 时采取相应措施(如循环写入)。


6. 相似函数,或关联函数

  • pwrite: 类似于 write,但它允许你在一次调用中同时指定要写入的文件描述符、缓冲区、写入字节数以及文件内的偏移量。它不会改变文件的当前偏移量。
  • writev: 允许你将多个不连续缓冲区(一个 iovec 数组)中的数据写入到文件描述符中,这对于需要拼接发送多个数据块的场景非常有用。
  • read: 与 write 相反,用于从文件描述符读取数据。
  • open: 通常在调用 write 之前使用,用来获取要写入文件的文件描述符。

7. 示例代码

示例 1:将数据写入文件

这个例子演示如何创建(或截断)一个文件,并将一些文本数据写入其中。

#include <unistd.h>  // write, close
#include <fcntl.h>   // open, O_WRONLY, O_CREAT, O_TRUNC
#include <stdio.h>   // perror, printf
#include <stdlib.h>  // exit
#include <string.h>  // strlen
#include <errno.h>   // errno

int main() {
    int fd;                    // 文件描述符
    const char *message = "Hello, Linux System Programming!\n";
    size_t message_len = strlen(message); // 获取要写入的字节数
    ssize_t bytes_written;    // 实际写入的字节数

    // 1. 打开(或创建)一个文件用于写入
    // O_WRONLY: 只写模式
    // O_CREAT: 如果文件不存在则创建
    // O_TRUNC: 如果文件存在,则截断(清空)它
    // 0644 是新创建文件的权限 (所有者读写,组和其他用户只读)
    fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("Error opening/creating file");
        exit(EXIT_FAILURE);
    }

    printf("File 'output.txt' opened/created successfully with fd: %d\n", fd);

    // 2. 调用 write 将数据写入文件
    bytes_written = write(fd, message, message_len);

    // 3. 检查 write 的返回值
    if (bytes_written == -1) {
        perror("Error writing to file");
        close(fd); // 出错也要记得关闭文件
        exit(EXIT_FAILURE);
    } else if ((size_t)bytes_written != message_len) {
        // 检查是否所有数据都被写入 (在简单场景下通常如此,但好习惯是检查)
        fprintf(stderr, "Warning: Only %zd of %zu bytes written to file.\n", bytes_written, message_len);
        // 在实际应用中,可能需要循环 write 来处理这种情况
    } else {
         printf("Successfully wrote %zd bytes to 'output.txt'.\n", bytes_written);
    }

    // 4. 关闭文件描述符
    if (close(fd) == -1) {
        perror("Error closing file");
        exit(EXIT_FAILURE);
    }

    printf("File closed. Check 'output.txt' for the content.\n");
    return 0;
}

代码解释:

  1. 定义要写入的字符串 message 和其长度 message_len
  2. 使用 open() 以只写模式 (O_WRONLY) 打开或创建 output.txt 文件。如果文件不存在则创建 (O_CREAT),如果存在则清空 (O_TRUNC)。权限设置为 0644
  3. 调用 write(fd, message, message_len) 尝试将整个消息写入文件。
  4. 检查 write 的返回值:-1 表示错误;如果返回值不等于 message_len,则表示未完全写入(在此简单场景下不太可能发生,但展示了检查的必要性);否则表示成功写入。
  5. 最后,使用 close() 关闭文件描述符。

示例 2:向标准错误输出写入错误信息

这个例子展示了如何使用 write 向标准错误 (STDERR_FILENO) 写入信息,这通常用于输出错误或诊断信息,与标准输出分开。

#include <unistd.h>  // write
#include <string.h>  // strlen

int main() {
    const char *error_msg = "An error occurred in the program.\n";

    // 直接向标准错误 (文件描述符 2) 写入错误信息
    // 注意:这里也没有处理 write 可能部分写入的情况,
    // 对于短消息写入 stderr 通常可以假设一次性成功,
    // 但在严格要求下仍需检查返回值。
    write(STDERR_FILENO, error_msg, strlen(error_msg));

    return 0; // 程序正常退出,但 stderr 上有错误信息
}

代码解释:

  1. 定义错误信息字符串。
  2. 直接调用 write(STDERR_FILENO, error_msg, strlen(error_msg)) 将错误信息写入标准错误输出。STDERR_FILENO 是预定义的常量,值为 2。
此条目发表在未分类分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注