系统IO

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
    • (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(可正可负)
  • 返回值:
    • 成功返回新光标位置相对于文件开头的偏移量(以字节为单位)
    • 失败返回-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

猜你喜欢

转载自blog.csdn.net/qq_36337149/article/details/81064396