学习内容:
1)open函数的flag
2)linux系统如何管理文件
3)lseek详解
4)dup和dup2函数介绍
5)标准IO库介绍
如何查man手册:man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查库函数
1、open函数的flag
大家有没有发现open函数有两个如下,
int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
多了一个mode,查看man手册可以发现作用为O_CREAT创建了新文件时对文件的权限设置,如下图:
常用的flag:
1)读写权限:O_RDONLY(只读), O_WRONLY(只写), O_RDWR(可读可写).
2)打开存在并有内容的文件时:O_APPEND(追加)、O_TRUNC(覆盖)
3)打开不存在的文件时:O_CREAT(覆盖创建)、O_EXCL(确保文件创建) O_EXCL标志和O_CREAT标志来结合使用,没有文件时创建文件,有这个文件时会报错提醒我们。
4)阻塞与非阻塞:O_NONBLOCK(非阻塞)我们打开一个文件默认就是阻塞式的,如果你希望以非阻塞的方式打开文件,则flag中要加O_NONBLOCK标志,只用于设备文件,而不用于普通文件。
5)write非阻塞等待:O_SYNC 无O_SYNC时write只是将内容写入底层缓冲区即可返回,在合适的时候会将buf中的内容一次性的同步到硬盘中。这种设计是为了提升硬件操作的性能和销量,提升硬件寿命;但是有时候我们希望硬件不好等待,直接将我们的内容写入硬盘中,这时候就可以用O_SYNC标志。
2、linux系统如何管理文件
1)硬盘中的静态文件和inode(i节点)
硬盘中可以分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域。操作系统访问硬盘时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。而这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表(我们叫inode,i节点,其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
实用:大家格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
2)内存中被打开的文件和vnode(v节点)
一个程序的运行就是一个进程,我们在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode。
3、lseek详解
off_t lseek(int fd, off_t offset, int whence);
whence就是代表一个参考位置(用来表示文件开始处到文件当前位置的字节数)offset代表一个偏移量
参数 offset 的含义取决于参数 whence:
SEEK_SET,则返回的文件偏移量将被设置为 offset。
SEEK_CUR,则返回的文件偏移量将被设置为 cfo 加上 offset,
SEEK_END,则返回的文件偏移量将被设置为文件长度加上 offset,
1)文件指针:当我们要对一个文件进行读写时,一定需要先打开这个文件,所以我们读写的所有文件都是动态文件。动态文件在内存中的形态就是文件流的形式。
2)在动态文件中,我们会通过文件指针来表征这个正在操作的位置。所谓文件指针,就是我们文件管理表这个结构体里面的一个指针如上图。所以文件指针其实是vnode中的一个元素。这个指针表示当前我们正在操作文件流的哪个位置。这个指针不能被直接访问,linux系统用lseek函数来访问这个文件指针。
3)read和write函数都是从当前文件指针处开始操作的,所以当我们用lseek显式的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的。
4)用lseek计算文件长度
eg:return=lseek(fd,0,SEEK_END);//表示从文件末尾
5)用lseek构建空洞文件,这个文件中有一段是空的
6)使用实列:
首先我们看到a.txt里面全都是f,其实我们用O_APPEND就可以在后面追加内容,而我们用lseek来随意位置追加内容
使用SEEK_END
lseek(fd,0,SEEK_END);//在write1前使用一次和O_APPEND同等效果,在write之后再使用一次即可的读取出当前文件的长度
4、dup和dup2函数介绍
int dup(int oldfd); int dup2(int oldfd, int newfd);//返回值即为newfd
1) 什么是文件共享:同一个文件(同一个文件指的是同一个inode,同一个pathname)被多个独立的读写体(几乎可以理解为多个文件描述符)去同时(一个打开尚未关闭的同时另一个去操作)操作。
2) 进行文件描述符复制:dup并不能指定分配的新的文件描述符的数字,dup2系统调用修复了这个缺陷。dup返回的fd和原来的oldfd都指向oldfd打开的那个【动态文件】,操作这两个fd实际操作的都是oldfd打开的那个文件。实际上构成了文件共享
3) fd小于3的分别对应于,stdin(0)、stdout(1)、stderr(3),也就是标准输入、标准输出、标准错误。
4)实现标准输出的重定位实例
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #define FILEPATH "a.txt" int main(int argc,char *argv[]) { int fd=-1; int i=-1; //实际向文件中写入的字节数 int j=-1;//实际向文件中写入的字节数 int k;//用dup复制后的文件描述符 char b[100]= "bbbbbadfghgdfh"; //打开一个文件操作 fd=open(FILEPATH, O_RDWR ); if(-1==fd) { printf("文件打开失败!\n"); perror("open:"); return 0; } else { printf("文件打开成功!fd=%d\n",fd); } close(1); //关闭标准输出文件描述符1 k=dup(fd); //实现重定位 j=write(k,&b,20); if(-1==j) { printf("写入文件失败\n"); perror("写入"); return 0; } printf("write succeed!\n"); close(fd); return 0; }
5、标准IO库介绍
1)标准IO和文件IO有什么区别:标准IO是C库函数(也是通过调用API来完成操作的),而文件IO是linux系统的API(API类似于一种接口,是由操作系统提供的)
2)常用标准IO函数介绍:fopen、fclose、fwrite、fread、ffulsh(刷新标准库函数的缓存,直接写进操作系统的缓冲区中)、fseek
3)一个简单的标准IO读写文件实例
#include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<unistd.h> #define FILEPATH "a.txt" int main(int argc,char *argv[]) { FILE *fd = NULL; //定义一个文件指针 int ret = 0; char Wrtebuff[10]={"sdfgdjdytr"}; fd=fopen(FILEPATH, "r+"); if(NULL == fd) { printf("文件打开失败!\n"); perror("open"); return 0; } ret = fwrite(Wrtebuff,1,sizeof(Wrtebuff),fd); if(ret<0) { perror("write"); return 0; } printf("文件写入成功!\n"); return 0; }
原文地址:https://www.cnblogs.com/ncne/p/10987690.html