Linux文件IO

本文深入讲解了Linux下文件IO的基本概念和技术细节,包括文件描述符、文件操作如open、close、read、write等函数的使用,以及文件属性的获取方法。
Linux文件io

首先要知道文件描述表,通过这张表就可以找到对应的文件。从0开始编码到1023一共1024个。每个进程的前三个默认打开,也就是0,1,2分别指向标准输入、标准输出、标准错误三个文件,可以不用open直接使用。
pcb本质是结构体
一个进程有一个文件描述表:1024
前三个被占用
文件描述符的作用,通过他们找到对应的磁盘位置。

在这里插入图片描述
打开文件

int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);	

参数:

	pathname  文件路径
	flags:
		必选项:O_RDONLY,O_WRONLY,O_RDWR
		可选项:
				创建文件:O_CREAT
					创建文件是检测文件是否存在:O_EXCL
					如果文件存在,返回-1
					必须与O_CREAT一起使用
				追加文件:O_APPEND
				文件截断:O_TRUNC
				设置非阻塞:O_NONBLOCK
	model:
		只有创建新文件的时候才起作用

open函数的flags参数是一个32位整数,每一位对应不同的操作位

在这里插入图片描述

#include <unistd.h>

int close(int fd);//fd为open返回的文件描述符,这个时候文件描述符,就被关闭,不能找到文件位置
//close() returns zero on success.  On error, -1 is returned, and errno is set appropriately.
read 和write
 #include <unistd.h>
 ssize_t read(int fd, void *buf, size_t count);
参数:
	fd:open返回的值
	buf:缓冲区,存储要读取的数据
	count:缓冲区能存储的最大字节数sizeof(buf)
返回值:
	-1:失败
	成功:
		>0:读出的字节数
		=0:文件读完了
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
	fd:open的返回值文件描述符
	buf:要写到文件中的数据
	count:strlen(buf)
返回值:
	-1:失败
	>0:写到文件的字节数

程序示例:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>

int main(){
        //打开文件,
        int fd=open("./read.txt",O_RDWR);//以读写的方式打开文件read.txt文件
        printf("fd=%d\n",fd);
        if(fd==-1){
                perror("open error");  //打印失败信息
        }       
        //打开另一个文件,写操作
        int fd1=open("tmp",O_WRONLY|O_CREAT,0664);//以写的方式打开文件,如果没有创建新的文件,权限为0664
        printf("fd1=%d\n",fd1);
        
        //read

        char buf[4096];
        int len=read(fd,buf,sizeof(buf)); //从fd中读取4076个字节
        
        while(len>0){//这个意思是持续读,直到len=0说明读取的数据为0,也就是没有数据了
                //数据写到文件中
                int ret=write(fd1,buf,len);
                printf("ret=%d\n",ret);
                //read
                len=read(fd,buf,sizeof(buf));
        }
        close(fd);
        close(fd1);
        return 0;
}

在这里插入图片描述
每个进程都有一个文件描述符表,每个文件描述符指向一个文件表项,表项中有current file 和i-node的指针。
所以每一个进程都有一个lseek,比如第一个进程打开文件的时候,是文件指针偏移为0,另一个进程文件指针偏移量也为0,各自维护自己的。

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
//如果whence SEEK_SET参数offset=0 那么返回值为0
//					    offset=10那么返回值为10
//说白了就是相对文件指针的偏移量
a.文件指针移动到头部
lseek(fd,0,SEEK_SET);
b.获取指针当前位置:
	int  len=lseek(fd,0,SEEK_CUR);
c获取文件长度:
	int len=lseek(fd,0,SEEK_END);
文件拓展
文件原大小100K,拓展为1100k
	lseek(fd,1000,SEEK_END);//这时候不会拓展成功
	最后做一次写操作
	write(fd,"a",1);

在这里插入图片描述
这里不是乱码而是‘\0’起到占位的作用。

errno和perror

errno是一个全局变量,不同的值,对应不同的错误信息。
perror是一个函数,把errno对应的信息打印出来。

 #include <stdio.h>
 void perror(const char *s);

perror("发生错误")
//显示信息为:发生错误:error info
阻塞和非阻塞

阻塞和非阻塞是文件的属性,并不是程序的属性
普通文件默认非阻塞
终端设备默认阻塞,管道、套接字默认阻塞

如果一个程序app,冲终端读取10字符,而为人输入15个字符会发生什么现象?

bash默认是前台程序
./app 启动了一个程序,前台程序这个时候变成了app,而bash变成了后台程序。
./app等待用户输入10个字符,而实际输入了15个字符,剩下的还在输入终端缓冲区中。
read函数解除阻塞读取缓冲区数据,write,程序结束。
bash从后台程序变为前台程序,检测到了缓冲区数据,将缓冲区作为shell命令区做了解析。

获取文件属性
struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* inode number */
               mode_t    st_mode;        /* protection */***********常用***********
               nlink_t   st_nlink;       /* number of hard links */
               uid_t     st_uid;         /* user ID of owner */*****常用***********
               gid_t     st_gid;         /* group ID of owner */****常用***********
               dev_t     st_rdev;        /* device ID (if special file) */
               off_t     st_size;        /* total size, in bytes */*常用***********
               blksize_t st_blksize;     /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* time of last access */
               struct timespec st_mtim;  /* time of last modification */***常用*****
               struct timespec st_ctim;  /* time of last status change */

           #define st_atime st_atim.tv_sec      /* Backward compatibility */
           #define st_mtime st_mtim.tv_sec
           #define st_ctime st_ctim.tv_sec
           };

在这里插入图片描述

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
//lstat 读取的是连接文件
//stat读取的是连接文件指向的文件属性
	

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 #include <unistd.h>

 int dup(int oldfd);
 //将oldfd的文件描述符所指的内容复制给新的文件描述符,返回从3到1023最小的且没有被占用的
 int dup2(int oldfd, int newfd);
//如果newfd已经指向了一个文件先close(newfd)然后执行oldfd只向的文件。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<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、付费专栏及课程。

余额充值