linux 文件IO

Linux文件IO

在这里插入图片描述

C语言

在这里插入图片描述

PCB

在这里插入图片描述

task_struct结构体
/usr/src/linux-headers/include/linux/sched.h
files_struct结构体

文件描述符
一个进程默认打开3个文件描述符
STDIN_FILENO 0
STDOUT_FILENO 1
STDERR_FILENO 2

open/close
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限,则文件权限为:mode&(~umask)
返回值
打开成功,返回文件描述符;
打开失败,返回-1
在这里插入图片描述
在这里插入图片描述
int close(int fd); 同理
read/write
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0
参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读
写位置向后移。这个读写
位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置
ssize_t write(int fd, const void *buf, size_t count);
返回值:成功返回写入的字节数,出错返回-1并设置errno
写常规文件时,write的返回值通常等于请求写的字节数count,而向终端设备或网络写
则不一定。

阻塞和非阻塞

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端
设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻
塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是
不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而
向终端设备或网络写则不一定。
现在明确一下阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被
置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比
如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡
眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情
况:
正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程
的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正
在读写该进程的地址空间。
就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另
一个进程,所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进
程,那么该调度谁执行呢?内核的调度算法是基于优先级和时间片的,而且会根据每个进程
的运行情况动态调整它的优先级和时间片,让每个进程都能比较公平地得到机会执行,同时
要兼顾用户体验,不能让和用户交互的进程响应太慢。

lseek
每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通
常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以O_APPEND方
式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek
和标准I/O库的fseek函数类似,可以移动当前读写位置

fcntl
fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这
些标志称为File Status Flag),而不必重新open文件
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock)
用fcntl改变File Status Flag

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
int main(void)
{
char buf[10];
int n;
int flags;
flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
perror("fcntl");
exit(1);
}
tryagain:
n = read(STDIN_FILENO, buf, 10);
if (n < 0) {
if (errno == EAGAIN) {
sleep(1);
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
goto tryagain;
}
perror("read stdin");
exit(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}
<think>好的,用户想了解Linux文件IO操作或者解决相关问题,需要教程或常见问题解决方案。首先,我需要整理一下相关的知识点,确保覆盖文件IO的基本操作、常见问题以及解决方法。根据用户提供的引用内容,尤其是引用4和引用5,里面提到了文件共享、打开方式、重定位等,这些都需要详细解释。 首先,应该从基础的文件操作开始,比如打开、读写、关闭文件。用户可能对系统调用如open、read、write、close不太熟悉,需要解释这些函的使用方法,包括参和返回值。同时,要提到文件描述符的概念,这是Linux文件IO的核心。 接下来,关于文件共享的三种方式,引用4中已经提到,我需要详细展开,说明不同进程如何共享文件,以及dup和dup2的作用。这部分可能涉及到文件描述符的复制,以及重定向的原理,如引用2中的重定位符号>的实现,这里可以结合例子说明。 然后,常见问题方面,用户可能会遇到权限问题、文件锁定、大文件处理等。比如,打开文件时权限不足,或者多个进程同时写入导致据混乱。这时候需要讲解如何正确设置文件权限,使用flock或者fcntl进行文件锁定,以及处理大文件时使用O_LARGEFILE标志。 另外,用户提供的引用2和引用3提到了重定位和IO操作的条件,这部分可以解释输入输出重定向的原理,比如使用dup2系统调用实现,以及非阻塞IO的相关问题。可能用户在实际操作中遇到重定向失败或者IO阻塞的情况,需要给出解决方案。 还要考虑不同模式下的文件操作,如阻塞与非阻塞模式的区别,如何设置非阻塞模式,以及这些模式对读写操作的影响。引用3提到发起IO操作的条件,如果无法立即完成则报错,这可能与非阻塞模式有关,需要说明清楚。 在代码示例方面,用户可能需要实际的代码片段来理解如何操作。比如,使用open打开文件,并处理错误;使用read和write进行文件读写,并检查返回值;使用dup2实现重定向等。同时,需要强调错误处理的重要性,比如检查系统调用的返回值,避免程序崩溃。 关于文件位置指针的问题,用户可能在追加写入或随机访问时遇到问题,需要解释lseek函的使用,以及O_APPEND标志的作用,确保写入位置正确。 最后,生成相关问题时要围绕用户可能进一步询问的方向,比如文件锁的具体使用、非阻塞IO的应用场景、文件描述符的复制方法等,这样可以帮助用户深入理解。 现在需要将以上思考整理成一个结构清晰的回答,分点讲解,结合引用中的内容,并附上代码示例和问题解决方案。注意使用正确的LaTeX格式,行内公式用$,独立公式用$$,同时确保引用标识正确添加在对应的位置。</think>### Linux 文件 IO 操作教程 #### 一、基础操作 1. **文件打开与关闭** 使用系统调用 `open()` 打开文件,返回文件描述符(fd)。需指定路径和模式(如只读、只写、追加等)。示例: ```c int fd = open("test.txt", O_RDWR | O_CREAT, 0644); // 创建或打开文件,权限644 if (fd == -1) { perror("open failed"); exit(1); } close(fd); // 关闭文件 ``` 模式参如 `O_RDONLY`(只读)、`O_WRONLY`(只写)可通过位或组合[^4]。 2. **读写操作** - `read(fd, buf, size)`:从文件描述符读取据到缓冲区。 - `write(fd, buf, size)`:将缓冲区据写入文件。 示例: ```c char buf[1024]; ssize_t bytes_read = read(fd, buf, sizeof(buf)); // 返回实际读取字节 if (bytes_read == -1) { /* 错误处理 */ } ``` #### 二、文件共享与描述符复制 1. **共享方式** - 同一进程多次 `open()` 同一文件。 - 不同进程分别 `open()` 同一文件。 - 使用 `dup()` 或 `dup2()` 复制描述符。 示例通过 `dup2` 实现输出重定向到文件: ```c int new_fd = open("output.txt", O_WRONLY | O_CREAT, 0644); dup2(new_fd, STDOUT_FILENO); // 将标准输出重定向到文件 ``` 2. **重定位原理** Shell 中的 `>` 符号通过 `open()` + `close()` + `dup()` 实现,将文件关联到标准输出描述符[^2]。 #### 三、常见问题与解决方案 1. **权限不足** - 检查文件权限:`ls -l` 查看权限,使用 `chmod` 修改。 - 确保 `open()` 调用时正确设置模式参(如 `O_CREAT` 需指定权限)。 2. **并发写入冲突** - 使用文件锁(`flock()` 或 `fcntl()`): ```c struct flock lock = {F_WRLCK, SEEK_SET, 0, 0, getpid()}; fcntl(fd, F_SETLKW, &lock); // 阻塞式加锁 ``` 3. **大文件处理** - 编译时添加 `-D_FILE_OFFSET_BITS=64`,或使用 `O_LARGEFILE` 标志。 #### 四、进阶操作 1. **非阻塞模式** 设置 `O_NONBLOCK` 标志: ```c int fd = open("/dev/tty", O_RDONLY | O_NONBLOCK); if (fd == -1) { /* 处理错误 */ } ``` 若当前无法立即完成 IO,操作会返回 `EAGAIN` 错误[^3]。 2. **文件位置指针** 使用 `lseek(fd, offset, whence)` 调整读写位置。例如追加写入: ```c lseek(fd, 0, SEEK_END); // 移动到文件末尾 write(fd, "new data", 8); ``` 或直接通过 `O_APPEND` 标志实现原子追加[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值