1:OPEN_MAX常量
1> 文件描述符的范围:0 ~~OPEN_MAX
2> 早期UNIX版本规定OPEN_MAX=19,即一个进程可以打开20个文件
3> 现在UNIX扩展了,一般OPEN_MAX=63,即一个进程可以打开64个文件了
2:open函数
原型 : int open(const char *pathname, into flag, …/*, mode_t mode */) ;
头文件 : #include <sys/types.h>
#include <fcntl.h> // 定义了O_RDWR等常量
#include <sys/stat.h>
作用 : 打开或创建一个文件
参数 :
Oflag
1> 下面三个常量只能选择一个:O_RDONLY, O_WRONLY, O_RDWR
2> 下面的几个常量可以选择一个或者多个进行组合:
1> O_APPEND : 追加
2> O_CREAT : 如果文件不存在时,则创建此文件。使用此选项时,
同时说明第三个参数mode,用其说明该新文件的
存取许可权位
3> O_EXCL : 这个标志可以用来测试文件是否存在,用法:
同时设置O_EXCL和O_CREAT两个标志,如果
文件存在,则调用open函数会出现错误
4> O_TRUNC : 如果此文件存在,而且设置为只读或者只写模式,则
将其长度截短至0
5> O_NONBLOCK: 如果pathname指向的是一个FIFO,一个块设备或者
字符设备文件,则此选项为此文件打开和后续的I/O
操作设置非阻塞方式
6> O_SYNC : 最严格的同并方式,wirte操作在数据和属性都写在
物理磁盘后才返回
3:creat函数
1> 原型 : int creat(const char *pathname, O_RDONLY | O_CREAT | O_TRUNC)
2> 头文件 : #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
3> 作用 : 新建一个文件,现在可以不用这个函数,直接用open这个函数就可以了
4:creat函数的缺点
1> 只能以写的方式来打开一个文件
2> 在早期时,如果要创建一个临时文件,则应该先creat,然后close,然后再open
3> 现在直接就可以利用这种方式了:
Open (pathname, O_RDONLY | O_CREAT | O_TRUNC, mode_t) ;
5:close函数
1> 原型 : int close
2> 头文件 : <unistd.h>
3> 作用 : 关闭一个文件
6:关于close文件的两个注意点
1> 文件关闭之后,那么在此文件上的所有锁将自动消失
2> 当程序结束后,文件也自动会被关闭,很多程序利用这一特性
7:lseek函数
1> 原型 : off_t lseek(int fd, off_t offset, int whence)
2> 头文件 : #include <sys/types>
#include <unistd.h>
3> 作用 : 动态定位文件指针的位置,使得可以随机访问
4> 参数 :
int whence :
1> SEEK_SET :以文件开头为参照
2> SEKK_CUR :以文件当前指针为参照
3> SEEK_END :以文件末尾为参照
Off_t offset
可以正,也可以复
8:关于lseek的返回值
1> 对于普通文件,如果调用lseek成功,则返回值是非负值
2> 对于一些设备文件,如果调用lseek成功,则返回值可能是负值
―――――――――――――――――――
所以,在测试lseek是否成功被调用时,应该检验它的返回值是不是-1,而不是检验它是不是< 0
9:lseek的延长文件
1> lseek只在内核中记录文件的位置
2> 可以让lseek的结果大于文件的长度,当下次写时,那么就会延长文件,中间的部分用0来填充
10:read函数
1> 原型 : int read(int fd, void *buf, size_t size) ;
2> 头文件 :
#include <unistd.h>
3> 参数 :
Void *buf : 缓冲区
Size_t size : 要求读的字节数目
4> 返回值 :
Ø >0 :实际读到的字节数目
Ø -1 :读时发生错误
Ø 0 :说明达到文件末尾
11:write函数
1> 原型 : int write(int fd, void *bf, size_t size) ;
2> 头文件 :
#include <unistd.h>
3> 参数 :
Void *buf :缓冲区
Size_t size :要求写入的字节数目
4> 返回值 :
Ø >0 :实际写入的字节数目
Ø -1 :表示写操作失败
―――――――――――――――――――
12:一般write操作失败的原因
1> 磁盘已经写满
2> 超过了一个给定进程的文件长度限制
13:BUFSIZE的大小对读写时间的影响
1> 随着bufsize的增大,时间逐渐缩短
2> 当缩短到一定值时,则不会再缩短
14:内核使用三种数据结构来表示文件共享的关系
1> 一个进程对应的各个fd的记录项目
2> 一个fd对应一个文件表
3> 一个文件表还对应一个v结构
15:三种数据结构的组织结构如下所示
1> 每一个进程都对应一个表----进程表项,其中的记录以fd为索引
2> 进程表象中的每一条记录的后部分有一个指针,指向文件表相
3> 文件表相的最后有一个v节点指针,指向V数据结构
―――――――――――――
注意:
1> 内核为所有打开的文件维护一张文件表
2> 文件表的内容:
1> 文件状态标志(读,写,增写,同步,非阻塞)
2> 当前文件位移量
3> V节点指针
3> V节点
1> 包含文件类型
2> 和对此文件操作的函数指针的信息
16:多个文件描述符项指向同一文件表项的情况:
1> 这是允许的
2> 当使用dup函数时,则就会出现不同文件描述符指向相同文件表的情况
3> 父进程和子进程之间也是这种情况
17注意:
当两个进程或者一个进程打开一个文件两次时,则会建立两个文件表项,这样才能保证每个进程都具有
自己的一些属性(例如:读写位置等)
18:两个进程打开同一文件时的图示
1> 文件表项可以有多个
2> V节点只有一个
19:注意
任何一个包含两个或两个以上的函数操作都不可能为原子操作,因为在两个函数中间,内核可能会置换
进程
20:注意
当两个以上的进程同时写同一个文件时,可能会出现问题
前提:两个进程写同一份文件时的数据结构如上图所示
――――――――――――――详细表述―――――――――――――――
进程A和进程B它们的任务相同,都是定位到1500字节处,然后往里面写入东西
1> 进程A打开文件file,并利用lseek定位到1500字节处
2> 进程B打开文件file,并利用lseek定位到1500字节处
3> 内核调度进程B执行,则进程B执行写操作,假设写了100个字节,则V节点里面的信息会更新
到1600字节处
4> 接着,内核调度进程A执行,则进程A执行写操作,但是因为进程A的文件表项里面的文件读写
指针是1500字节,所以这个时候进程A会从1500字节处来写数据
从上面可以看处,已经出现问题了
21: dup函数的两种
1> int dup(int fd)
2> int dup(int fd1, int fd2)
22: int dup(int fd1, int fd2) 说明
1> 参数 :
Fd1 : 已知的
Fd2 : 想申请的fd数值
2> 情况:
1> 如果fd2已经打开,先将其关闭
2> 如果fd2 = fd1,则返回fd2,不关闭它
3> 如果fd2没有打开,则直接复制
23:注意
程序运行时,设置O_SYNC会增加时间
24:fcntl函数的5种功能
1> 复制一个现存的文件描述符
2> 获得/设置文件描述符标记
3> 获得/设置文件状态标志
4> 获得/设置异步I/O有权
5> 获得/设置记录锁