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
来写入剩余的数据。
- 返回实际写入的字节数 (0 <= 返回值 <=
- 出错时:
- 返回 -1,并设置全局变量
errno
来指示具体的错误类型(例如EAGAIN
、EBADF
、EFAULT
、ENOSPC
等)。
- 返回 -1,并设置全局变量
重要提示: 绝对不能仅仅通过检查 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> // errnoint 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;
}
代码解释:
- 定义要写入的字符串
message
和其长度message_len
。 - 使用
open()
以只写模式 (O_WRONLY
) 打开或创建output.txt
文件。如果文件不存在则创建 (O_CREAT
),如果存在则清空 (O_TRUNC
)。权限设置为0644
。 - 调用
write(fd, message, message_len)
尝试将整个消息写入文件。 - 检查
write
的返回值:-1 表示错误;如果返回值不等于message_len
,则表示未完全写入(在此简单场景下不太可能发生,但展示了检查的必要性);否则表示成功写入。 - 最后,使用
close()
关闭文件描述符。
示例 2:向标准错误输出写入错误信息
这个例子展示了如何使用 write
向标准错误 (STDERR_FILENO
) 写入信息,这通常用于输出错误或诊断信息,与标准输出分开。
#include <unistd.h> // write
#include <string.h> // strlenint 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 上有错误信息
}
代码解释:
- 定义错误信息字符串。
- 直接调用
write(STDERR_FILENO, error_msg, strlen(error_msg))
将错误信息写入标准错误输出。STDERR_FILENO
是预定义的常量,值为 2。
理解 write
函数的关键在于记住它只是“尝试”写入指定数量的字节,并且总是需要检查返回值来确认操作结果和实际写入的字节数。