1、系统IO:Linux提供给应用程序操作文件(普通文件、设备文件)的接口
与标准IO的区别:
- 1、标准IO是系统IO的包装,在标准IO的实现中,实际上调用了系统IO提供的函数。
- 2、标准IO只处理普通文件,系统IO可以处理所有类型
文件:设备文件,网络文件。
文件描述符:file description ,fd
- 实际上,系统中是以整数形式存储这个文件描述符,要操作某个文件,必须先获得该文件描述符,就类似于学号,具有唯一性。
- fd是操作系统分配的
- 操作系统会为所有程序提供三个文件描述符
- 0 :标准输入
- 1 :标准输出
- 2 :标准错误
2. linux系统IO的API函数
2.1 打开、关闭文件
- (1)open:用来打开或创建一个文件open and possibly create a file or device
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数:
- pathname: 要打开或创建的文件名(带路径)
- flags: 打开标识:包括主标识 + 副标识
主标识:(必须三选一)
- O_RDONLY : read only 只读打开
- O_WRONLY : write only 只写打开
- O_RDWR : read and write 读写打开
- 副标识:根据需求选择,可以多选也可以没有,多选时两个标识之间用|连接
- O_APPEND: 追加方式
- O_CREAT: 创建标志(如果文件不存在则创建),注意最后没有字母E
- O_EXCL: 该标志一般和O_CREAT配合使用,用来测试文件是否存在。如果指定 O_CREAT | O_EXCL ,如果文件存在,则open会失败,并且 errno == EEXIST
perror才能打印出errno来,printf不行。 - O_TRUNC: truncate 截短标记
假如文件存在,并且是一个普通文件,而且打开方式是O_RDWR or O_WRONLY ,则O_TRUNC会清空文件的内容。 - O_TRUNC | O_RDONLY -> O_TRUNC不会起作用!!
mode: 当创建文件O_CREAT时,需要第三个参数表示创建这个文件的权限,如果不是创建文件,则第三个参数会被忽略
- 文件的权限有两种方式指定:
- (1) 宏
- S_IRUSR(用户可读) S_IWUSR(用户可写)S_IXUSR(用户可执行)
- S_IRGRP(组用户可读) S_IWGRP S_IXGRP
- S_IROTH(其它用户可读) S_IWOTH S_IXOTH
- 例如:指定文件权限 110 100 100 0644
S_IRUSR|S_IWUSR | S_IRGRP |S_IROTH
- 例如:指定文件权限 110 100 100 0644
- (2) 八进制的方式
如: 0644、0400、0755
- 返回值: 成功返回打开的那个文件的文件描述符(>=0),该文件描述符用来指定此打开的文件,
- 后续所有对该文件的操作都必须通过它,所以该文件描述符需要保存!
- 失败返回-1, 同时errno被设置。
- (2)close用来关闭一个文件描述符
#include <unistd.h>
int close(int fd);
2.2 读、写文件
- (1)read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- 含义:read用来从fd指定的文件中,读取count个字节到,buf指定的内存空间中去。
参数: - fd: 文件描述符,指定从哪个文件中读取数据
- buf: 指向一段内存空间,表示数据读到哪里去
- count: 数量,表示读多少个字节
返回值:
- 成功返回实际读到的字节数(>0 && <= count)
- 出错返回-1, 同时errno被设置
- 返回值为0,表示读到文件末尾啦!!
(2)write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
- 含义:把buf指向的那段内存空间中前面count个字节的数据,写到fd指定的文件中去。
- 参数:
- fd: 文件描述符,表示要写到哪个文件中去
- buf: 指向一段内存空间,指向要写的数据的首地址
- count:要写多少个字节
返回值 :
- 成功返回实际写入文件的字节数
- 返回0,表示什么也没写
- 失败返回-1,同时errno被设置。
[练习]:利用系统io来实现两个文件之间的拷贝,文件名由命令行参数指定filecopy.c
2.3 文件的定位 lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- 含义:给fd指定的文件设置位置指针,也就是光标或偏移量。
- 参数:
- fd: 文件描述符,要定位的文件;
- offset: 偏移量,具体含义需要结合第三个参数
- whence: 定位方式,有如下三种:
- SEEK_SET: 基于文件开头定位
新光标位置=文件开头+offset(>=0) - SEEK_CUR:基于当前光标位置定位
新光标位置=当前光标位置+offset(可正可负) - SEEK_END:基于文件末尾定位
新光标位置=文件末尾+offset(可正可负)
- SEEK_SET: 基于文件开头定位
- 返回值:
- 成功返回新光标位置相对于文件开头的偏移量(以字节为单位)
- 失败返回-1, 同时errno被设置
- 例子:
- 可以利用lseek来求一个文件的大小
- long size = lseek(fd, 0, SEEK_END);
2.4 文件映射/解映射
- (1)文件映射mmap
- 什么是映射:read,write时候其实是把磁盘的内容复制了一份内存,能不能不复制呢?特别是文件比较大时?
- 文件映射到进程的内存空间后,就好像进程与文件之间建立了连接信号(可以想象成指针),进程在自己空间操作这个映射,会及时反映到文件中
- 假设同一个文件被多个进程映射了,那某一个进程操作了这个映射,其他进程也会实时获得更新,方便进程之间共享
- 适用场景:更新、读写一块固定大小的文件,文件大小不能变化
- 多进程共享
- 设备文件的映射:例如屏幕文件映射到进程空间中个,我们对这块空间赋值(颜色值),到时屏幕就可以看到对应颜色。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- 含义:实现把一个文件的内容映射到一个进程的地址空间中去。
- 参数:
- addr: 指针,映射地址,表示把这个文件的内容映射到进程的哪个地址上去。有人说,我怎么知道是哪个地址呢?
- 对,一般人不知道。所以这个参数一般为NULL,表示让操作系统自行分配一个地址。
- length: 要映射的文件内容的长度。
- 向上取PAGE_SIZE(4096)的整数倍。在内核中,内存是以页PAGE为单位的。
- prot:映射的内在区域的权限(应与打开文件时的权限一致,因为操作此区域内存实际上就是操作文件的内容)
- PROT_EXEC: 可执行
- PROT_READ: 可读的
- PROT_WRITE:可写的
- PROT_NONE:没有访问权限
- flags: 映射标志。决定对映射部分的操作是否对其他进程可见。
- MAP_SHARED: 共享映射,对其他进程可见,内存操作直接应用到文件中去。
- SHARED 在映射时,直接操作的文件!!!
- MAP_PRIVATE: 私有映射,对其他进程不可见,内存操作不应用到文件中去。
- PRIVATE 在映射时,会单独给这个文件这个进程开辟一个私有的空间。
- fd: 文件描述符,表示映射哪个文件.
- offset:偏移量,表示要从文件的哪个位置开始映射。
- offset必须为PAGE_SIZE(4k)整数倍。内核不仅是是以页为单位,而且内存地址必须是页对齐(必须是4k的整数倍)
- 返回值:
- 成功返回映射后那段内存空间的首地址
- 失败返回MAP_FAILED,同时errno被设置。
- 注意!!!:文件原来多大,映射后,向映射空间写数据,写再多也不能超过其原来大小。
(2)文件解映射munmap
int munmap(void *addr, size_t length);
- 参数:
- addr:要解映射的地址,mmap的返回的地址
- length:要解映射的内存空间的长度。
- 返回值:
- 0:正确, -1:错误,同时设置errno。
注意:进程退出会自动munmap, 但是关闭文件,不会自动munmap
[练习1]:
- (1)体验什么是映射:
将有内容的文件,映射到进程空间,通过映射地址修改内容,再通过vi 或 cat来查看文件,看内容是否实时更新了。 mmap1.c
两个文件共享一个文件,看是否实时更新。mmap2.c+mmap1.c
[练习2]:
- 1. 写一个程序,解析一个bmp的文件,打印出给定的bmp文件的信息:
- 是不是一个bmp的文件 ?
- 如果是一个bmp的文件:
- 这个bmp文件的分辨率是多少?宽 x 高
- 这个bmp文件的大小是多少字节?
- 这个bmp文件每个像素点占多少个字节?
- bmp文件名通过命令行参数指定
- “&ff”是数据存储时把短数据变成长数据的常用做法。
lseek, read
- 2、 用两个程序通过mmap实现聊天 homework2_1.c homework2_2.c