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只向的文件。