当前位置: 首页 > news >正文

write`系统调用

我们来介绍与 readopen 紧密配合使用的 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>   // 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;
}

代码解释:

  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>  // 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 上有错误信息
}

代码解释:

  1. 定义错误信息字符串。
  2. 直接调用 write(STDERR_FILENO, error_msg, strlen(error_msg)) 将错误信息写入标准错误输出。STDERR_FILENO 是预定义的常量,值为 2。

理解 write 函数的关键在于记住它只是“尝试”写入指定数量的字节,并且总是需要检查返回值来确认操作结果和实际写入的字节数。

http://www.lryc.cn/news/600809.html

相关文章:

  • 宝塔面板如何升级OpenSSL
  • 哈尔滨←→南昌的铁路要道
  • IC测试之pogo pin学习与总结-20250726
  • 鲲鹏服务器部署Kafka2.8.1
  • 微服务springcloud http客户端feign
  • 【资讯】2025年软件行业发展趋势:AI驱动变革,云原生与安全成核心
  • 【Spring Cloud】微服务学习
  • LeetCode——1717. 删除子字符串的最大得分
  • 秋招Day20 - 微服务 - 概念
  • 【机器学习深度学习】模型微调:多久才算微调完成?——如何判断微调收敛,何时终止训练
  • 二维数组相关学习
  • 大模型蒸馏(distillation)---从DeepseekR1-1.5B到Qwen-2.5-1.5B蒸馏
  • UniappDay03
  • 【Canvas与旗帜】条纹版大明三辰旗
  • AI是否会终结IT职业?深度剖析IT行业的“涌现”与重构
  • 慧星云新增大模型服务:多款大模型轻松调用
  • C++:STL中vector的使用和模拟实现
  • MySQL的底层原理--InnoDB数据页结构
  • 人大金仓 kingbase 连接数太多, 清理数据库连接数
  • 基于匿名管道的多进程任务池实现与FD泄漏解决方案
  • VUE2 学习笔记7 v-model、过滤器
  • 6.数组和字符串
  • ChatIm项目文件上传与获取
  • 拉普拉斯方程的径向解法
  • opencv学习(图像金字塔)
  • DriverManager在rt.jar里,凭什么能加载到classpath下的驱动?
  • Vue当中背景图无法占满屏幕的解决方法
  • 记一次腾讯云临时密钥接管存储桶
  • 零基础 “入坑” Java--- 十四、【练习】图书小系统
  • mrpc框架项目的AI总结