马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
open 是 Linux 文件操作最底层的系统调用之一:它打开文件、返回一个文件描述符,之后所有读写关闭都围绕这个描述符展开。理解 open,等于理解了“文件描述符”这个贯穿整个系统的概念。本文讲清楚它的函数原型、flags 组合、mode 权限、返回值与错误处理,再给出几个典型用法。
一、函数原型
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
两个版本,三参数的版本多出一个 mode,它在创建新文件时才用得上,可以省略。
二、参数说明
- pathname:要打开的文件路径,相对路径或绝对路径都行。
- flags:文件访问模式和状态标记,决定怎么打开(只读、读写等)以及其他行为。
- mode:文件权限位。当 flags 里含
O_CREAT 时必须指定,用来设置新文件的权限。
三、常见 flags 标记
flags 可以单独用,也可以用按位或组合。<fcntl.h> 里定义了下面这些。
1. 访问模式(三选一)
| 标记 | 含义 |
| O_RDONLY | 只读打开 |
| O_WRONLY | 只写打开 |
| O_RDWR | 读写方式打开 |
2. 文件状态标记(可叠加)
| 标记 | 含义 |
| O_CREAT | 文件不存在则创建 |
| O_EXCL | 与 O_CREAT 合用时,文件已存在则返回错误,避免被意外覆盖 |
| O_TRUNC | 文件存在且以写方式打开时,长度截断为 0 |
| O_APPEND | 追加模式打开,写操作从文件末尾开始 |
| O_NONBLOCK | 非阻塞模式,对设备文件和管道尤其有效 |
| O_SYNC | 每次写操作都等数据真正落盘,保证一致性 |
O_EXCL + O_CREAT 组合是创建“唯一锁文件”的经典写法,比先判断再创建安全得多;O_APPEND 是多进程写同一日志 时的常用保障,因为每次写入都从末尾开始。
四、mode 参数:新文件的权限
mode 通常是一组按位定义的常量,通过按位或与 others 组合:
S_IRUSR / S_IWUSR / S_IXUSR:文件所有者的读、写、执行权限
S_IRGRP / S_IWGRP / S_IXGRP:文件所属组的权限
S_IROTH / S_IWOTH / S_IXOTH:其他用户的权限
要注意实际权限还要受进程 umask 影响:最终权限 = mode & ~umask。写服务程序时如果生成的配置文件权限不对,八成是 umask 在起作用,而不是 mode 写错了。
五、返回值与错误
成功返回一个文件描述符(非负整数),失败返回 -1,并设置全局变量 errno 指示具体错误类型。常见错误码:
| 错误码 | 触发场景 |
| EEXIST | 用了 O_CREAT|O_EXCL 但文件已存在 |
| EACCES | 权限不足,无法打开文件 |
| EISDIR | 路径是目录,但期望的是文件 |
| ENOENT | 文件或目录不存在 |
| EMFILE | 进程已打开的文件描述符数量达到上限 |
| ENOSPC | 设备上没有足够空间创建文件 |
拿到 -1 后,用 perror() 或 strerror(errno) 就能直接把错误原因打印成人话。看 EMFILE 这种,说明是进程级 fd 泄漏,值得回头查代码里哪条路径没 close。
六、示例
1. 打开一个文件做读写
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("example.txt", O_RDWR);
if (fd == -1) {
perror("Error opening file");
return 1;
}
// 读写操作...
close(fd);
return 0;
}
2. 创建新文件并写入
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("newfile.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("Error creating file");
return 1;
}
const char *text = "Hello, world!";
write(fd, text, 13);
close(fd);
return 0;
}
这里 O_WRONLY|O_CREAT|O_TRUNC 是“创建或清空”的常用组合,权限设为用户可读写。注意 write 返回值要检查,写失败(比如磁盘满、管道破裂)时它返回的字节数会小于预期。
七、几个高级用法
1. 非阻塞模式
O_NONBLOCK 在处理设备文件、管道或 FIFO 时很有效。打开命名管道时不加它,进程会一直卡在等另一端打开;加上之后立刻返回:
int fd = open("myfifo", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
perror("Error opening FIFO");
}
2. 追加模式
int fd = open("logfile.txt", O_WRONLY | O_APPEND);
if (fd == -1) {
perror("Error opening file");
}
3. 打开设备文件
open 不只操作普通文件,也能打开 /dev/null、磁盘设备等。设备文件通过 open 和 ioctl 交互,从而实现硬件控制,这是很多底层工具的基础。
八、工程上的提醒
- open 之后一定要 close,异常分支也要走 close,否则文件描述符泄漏,长时间运行会撞上 EMFILE。
- 只看 open 成功与否不够,write/read 的返回值必须检查。
- 需要“原子创建”就用
O_CREAT|O_EXCL,不要用“先 access 再 open”那套时序有问题的写法。
- 多进程追加写日志
用 O_APPEND,比自己维护偏移量可靠。
- 写服务时先确认 umask,再决定 mode 写多大,不然生成的sock、配置、日志
权限总不符合预期。
九、总结
open 是 Linux 处理文件的基础函数,能力靠 flags 和 mode 两个字组合出来:访问模式定方向,状态标记定行为,mode 定新文件权限,返回值则是贯穿后续操作的文件描述符,出错时靠 errno 区分原因。把「先 open、判 -1、再 read/write、最后 close」这条主线写扎实,再结合 O_NONBLOCK、O_APPEND、O_EXCL 这几个高频标记,日常的文件操作代码基本都能写得稳当。
qidao123.com企服评测-应用市场 编辑提示 |