博主原创,转载请加上http://www.cnblogs.com/jikexianfeng/p/5769357.html
一.课程概要 1.文件描述符 2.文件操作和内核数据结构 3.文件原子操作
二.正文 (一).文件描述符 1.文件描述符简介 1).对于linux内核而言,所有的文件打开都是有文件描述符引用的. 2).文件描述符是一个非负整数(返回-1为标准出错),并且与文件是相对应的. 3).当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符. 4).当读写文件时,需要(用open或creat返回值)将文件描述符当做参数传递给相应的函数(read,write). 5).代码实践 gcc -o bin/fd_test src/fd_test.c 遇到问题:当(./bin/fd_test)执行代码时,打开文件的失败(返回值为-1),进入bin目录执行fd_test时成功(返回值为3)? 6).返回值为什么为3? 当程序运行时,系统会打开3个文件,标准输入0(STDIN_FILENO),标准输出1(STDOUT_FILENO),标准出错2(STDERR_FILENO),这些都定义在头文件<unistd.h>中.所以打开的文件是以3开始计数的. 7).文件描述符的范围时0~OPEN_MAX.这个宏值.很多系统时63,linux是1024. (二),实现shell命令 1.自定义头文件 1).编写一个具有的cp功能的自定义头文件 io.h(传入的为文件描述符) 2) 路径 include/io.h src/io.c 3).编译生成二进制文件 gcc -c -Iinclude src/io.c 2.编写mcp上层文件 1).路径 src/mcp.c 2).编译 gcc -Iinclude src/io.c src/mcp.c -o bin/mcp 3.编写mcat上层文件 1).路径 src/mcat.c 2).编译 gcc -Iinclude src/io.c src/mcp.c -o bin/mcp
(三).文件操作和内核数据结构 1.操作文件I/O的五个函数 1).open 2).read 3).wirte 4).lseek 5).close 2.操作文件描述符号的函数 1).dup 2).dup2 3.改变文件状态函数 1).fcntl 4.文件在内核中的数据结构(有3种数据结构表示) 1).文件描述符表 2).文件表项 3).V节点 5.文件原子操作
(四).函数详解 1.open函数(打开文件) 1).头文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 2).原型 int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); const char *pathname 为要打开的文件路径 int flags 为读/写标志位 O_RDONLY 自读打开 O_WRONLY 只写打开 or O_RDWR 读写打卡 O_APPEND 每次写时都加到文件尾端 O_CREAT 若此文件不存在则创建它,使用此选择项时,须同说明第三个参数mode,用其说明文件的权限 O_EXCL 如果同时指定了O_CREAT,而文件存在,则出错.这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作 O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截断为0 O_SYNC 每次write都等物理I/O操作完成 O_NONBLOCK 如果pathname指定的是一个FIFO,一块特殊文件或一个字特殊文件,则此选择项为此文件的本次打开操作和后续I/O操作设置非阻塞方式. mode_t mode 为可选参数,只有创建文件时候,才选择使用 返回值:成功返回文件描述符,失败返回-1.
2.creat函数(创建文件,默认为只写方式打开) 1).头文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> 2).原型 int creat(const char *pathname, mode_t mode); const char *pathname 为要打开的文件路径 mode_t mode 文件的权限 返回值:成功返回文件描述符,失败返回-1.
3.close 函数(关闭文件描述符) 1).头文件 #include <unistd.h> 2).原型 int close(int fd); int fd 为文件描述符 返回值:成功返回0,失败返回-1. 当一个进程中制定时,它打开的所有文件都有内核自动关闭.
4.read 函数(从文件中读内容) 1).头文件 #include <unistd.h> 2).原型 ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); ssize_t read(int fildes, void *buf, size_t nbyte); int fildes 文件描述符 void *buf 缓存区 size_t nbyte 需要读数据到的字节数 返回值:成功返回读到的字节数,若已读到文件尾部且文件没有内容,则为0,失败返回-1. 3).有多种情况使读到的字节数少于要求读的字节数 (1).在读普通文件时,在读到要求字节数之前已到达文件尾端. (2).当从终端设备读时,通常最多读一行. (3).当从网络读时,网络中的缓存机构可能造成返回值小于所要求读的字节数. (4).某些面向记录的设备,如磁带,每次最多返回一个记录. (5).进程由于信号造成的中断(正在读的时候,进程终止).
5. write函数(向文件中写内容) 1).头文件 #include <unistd.h> 2).原型 ssize_t pwrite(int fildes, const void *buf, size_t nbyte,off_t offset); ssize_t write(int fildes, const void *buf, size_t nbyte); int fildes 文件描述符 const void *buf, size_t nbyte 缓存区 size_t nbyte 需要向文件写入的字节数 返回值:成功返回已写到的字节数,失败返回-1.
6.lseek 函数(改变读写文件时读写指针位置) 1).头文件 #include <unistd.h> #include <sys/types.h> 2).原型 off_t lseek(int fd, off_t offset, int whence); int fd 文件描述符 off_t offset 文件偏移量 int whence 位置 若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处 offset 个字节. 若whence是SEEK_CUR,则将该文件的位移量设置为当前值加 offset, offset可以为正或负. 若whence是SEEK_END,则将该文件的位移量设置为文件长度加 offset, offset可以为正或负.
8.dup和dup2(文件描述符操作) 1).头文件 #include <unistd.h> 2).原型 int dup(int oldfd); int dup2(int oldfd, int newfd); 用来复制文件描述符,在进程通信的时候,改变进程标准输入和标准输出设备, 返回值:成功返回新的文件描述符,失败返回-1. 3).使用dup2从定向文件标准输入/输出. (1).编写dup2_test.c (1).路径 src/dup2_test.c (2).编译 gcc -o bin/dup2_test src/dup2_test.c
9.fcntl(可以改变已经打开文件的性质.) 1).头文件 #include<sys/types.h> #include<unistd.h> #include<fcntl.h> 2).原型 int fcntl(int fd, int cmd, ... /* arg */ ); int fd 文件描述符 int cmd 文件状态设置 返回值:成功(则依赖于cmd)返回一个大于0的数,失败返回-1. 3).编写mcat上层文件 (1).路径 src/flag_test.c (2).编译 gcc -Iinclude src/io.c src/flag_test.c -o bin/flag_test (3)./bin/flag_test test.txt zhangjianqi!
10.文件在内核中的数据结构(详情见./picture/内核中的数据结构.png) 一个打开的文件在内核中有三种数据结构,第一个是文件描述符表,第二个是文件表项,第三个是v节点,其中当一个进程启动的时候,他会自动打开一个FILE结构体,这个FILE结构体里面就是一些fb文件描述符的字段,那么如何通过文件描述符这个字段来找到这个文件了,咱们来看一个草图,当一个进程启动的时候,它会自动的打开一些文件描述符,有0(标准输入)1(标准输出)2(标准出错),然后通过文件描述符找到内核当中的一个数据结构FDT,第2个是文件表项的指针,文件描述文件表里面第一个内容是文件描述符的标志,第二个是被表象的指针,就是指向文件表项的指针,当它找到FI这个文件表项的时候,文件表项里面又记录了文件的状态标志,也就是只读,只写,或O_APPEND等状态标志,以及当前文件的偏移量,文件表项里面还记录了V节点的表项指针,他还用来指向下一个数据结构V节点. v节点里面又存放了文件的类型文件的创作者或者操作时间,也就是ls -l看到的所以内容,其中V节点里面存放了最重要的内容是i-node(快设备的标号,也就是ls -i看到的所以内容),然后通过i-node来找到这个磁盘上的文件.
11.文件原子操作 1).稳健追加 打开文件时使用 O_APPEN 标志,进程对文件偏移量调整和数据追加成原子操作.内核每次对文件写之前,都将进程的当前偏移量设置为该文件尾端,这样不在需要lseek来调整偏移量. 2).文件创建 对open函数的 O_CREAT 和 O_EXCL的使用,而该文件存在,open将失败,否则创建该文件,并且使得文件是否存在的判定和创建过程为原子操作.
代码下载:https://git.oschina.net/jikexianfeng/LINUX.git
|
请发表评论