系统章节-----基础IO

目录

1.复习下C的文件接口

fopen:打开文件    FILE *fopen(const char *path,const char *mode)

fwrite:向文件当中写

fread:从文件当中读

fseek:移动文件流指针的位置

int fclose(FILE*fp);关闭文件

2.系统调用的文件接口

open:int open(const char*pathname,int flags);

write:写

read:读

lseek:移动文件流指针

close:

3.文件描述符

(1)文件描述符:文件描述符就是一个小正数(没有负数)

(2)打印文件描述符的值

(3)文件描述符的分配规则:最小未使用

4.内核理解文件描述符

从task_struct的角度理解文件描述符是什么?通过查看源码可知:

面试题:一个进程最多打开多少个文件描述符 ?

5.理解文件描述符和文件流指针的区别

(1)从源码的角度理解,文件流指针(struct  _IO_FILE)是什么?

 (2)将文件描述符和文件流指针结合起来

6.重定向

(1)重定向的符号

(2)重定向的接口

(3)重定向代码

 7.静态库&动态库

(1)动态库

(2)静态库

8.软硬链接

9.文件系统

ext2文件系统:


1.复习下C的文件接口

fopen:打开文件
    FILE *fopen(const char *path,const char *mode)

path:带有路径的文件名称(如果说不带有路径,打开的文件可以是在当前路径下)

mode:打开文件的方式

下表为一些常见的mode

文件使用方式 含义如果指定文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件出错
“w” (只写)
为了输出数据,打开一个文本文件 (输出的时候会将之前的内容清空)建立一个新的文件
“a”(追加)向文本文件尾添加数据 出错
“rb”(只读)为了输入数据,打开一个二进制文件 出错
“wb”(只写)为了输出数据,打开一个二进制文件 建立一个新的文件
ab”(追加)向一个二进制文件尾添加数据 出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件 建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

fwrite:向文件当中写

size_t fwrite(const void*ptr,size_t size,size_t nmemb,FILE *stream);

参数:ptr:要往文件中写的内容

           size:定义往文件中写的时候,一个块是多大,单位字节;(通常定义为一个字节)

           nmemb:期望写多少块

           stream:文件流指针

返回值:返回成功写入到文件的块数

通常情况下,代码调用时,这样设置:

size:1(1个块的大小是1字节)

nmemb:块的个数(期望写多少字节)

返回值:成功写入文件当中块的个数(成功写入文件中的字节数量)


fread:从文件当中读

size_t fread(void *ptr,size_t size,size_t nmemb,FILE*stream)

参数:ptr:将文件中的内容保存到ptr指向的内存空间当中(这个内存空间一定要是合法的(程序员自己定义的))

           size:定义从文件中读的时候,一个块是多大,单位字节;(通常定义为一个字节)

           nmemb:期望读多少块

           stream:文件流指针

返回值:返回成功读入的文件块的个数


fseek:移动文件流指针的位置

参数:stream:文件流指针

           offset:偏移量

           whence:将文件流指针偏移到什么位置

                SEEK_SET:文件头部

                SEEK_CUR:当前文件流指针的位置

                SEEK_END:文件末尾

返回值:成功:0

              失败:-1


int fclose(FILE*fp);关闭文件

fp:文件流指针

作用:关闭文件流(关闭文件)

注意:打开的文件使用完成之后一定要记得关闭文件,否则

当文件句柄持续泄露的时候,最终进程就不能打开新的文件了;内存泄漏,导致进程占用的内存资源过多,如果持续泄漏,操作系统是没有办法满足进程申请内存资源的请求,操作系统就会强杀进程


2.系统调用的文件接口

open:
int open(const char*pathname,int flags);

int open(const char*pathname,int flags,mode_t mdoe);

参数:pathname:要打开或创建的文件目录

           flags:打开文件时可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。

                O_RDONLY:只读打开

                O_WRONLY:只写打开

                O_RDWR:读写打开

        这三个常量,必须且只能指定一个

                O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明文件的访问权限

                O_APPEND:追加写

举一个例子:O_RDWR | O_CREAT:以可读可写的方式打开文件,若文件不存在则创建它。

             mode:当创建一个新文件的时候指定新创建文件的权限;传递一个8进制的数字(就是权限,例如0664)

返回值:成功:新打开的文件描述符

               失败:-1


write:写

ssize_t write(int fd,const void *buf,size_t count);

参数:fd:文件描述符

           buf:将buf指向的内容写到文件当中去

           count:期望写多少字节

返回值:返回写入的字节数量


read:读

ssize_t write(int fd,const void*buf,size_t count);

参数:fd:文件描述符

           buf:将文件当中读到的内容写到buf指向的空间当中去

           count:期望读多少字节

返回值:返回读到的字节数量


lseek:移动文件流指针

off_t lseek(int fd,off_t offset,int whence);

参数:fd:文件描述符

           offset:偏移量,字节单位

           whence:偏移的位置

                SEEK_SET:文件头部

                SEEK_CUR:当前文件流指针的位置

                SEEK_END:文件末尾

返回值:  成功:返回偏移的位置,单位字节

                失败:返回-1


close:

int close(int fd);

关闭文件描述符


3.文件描述符

(1)文件描述符:文件描述符就是一个小正数(没有负数)

(2)打印文件描述符的值

(3)文件描述符的分配规则:最小未使用

文件描述符不是固定进行分配,而是按照最小未使用原则进行分配的

由上图可知:当我们关闭0号文件描述符的时候,系统给我们fd分配的就是0

4.内核理解文件描述符

从task_struct的角度理解文件描述符是什么?
通过查看源码可知:

当我们创建一个进程的时候就会产生一个task_struct结构体,task_struct结构体中含有一个files_struct结构体指针,指向一个files_struct结构体,在这个files_struct结构体中有一个fd_array数组,文件描述符就是fd_array这个数组的下标。

面试题:一个进程最多打开多少个文件描述符 ?

这个问题的场景并不是在基础IO(面试官和你探讨文件操作的时候问道的),而大多数场景都是和面试官在讨论网络编程的时候,会被问到的。

  1. 系统当中针对一个进程打开的文件描述符的数量是有限制的。
  2. 通过ulimit -a可以查看到“open files”的大小,而“open files”的大小就是一个进程可以打开的文件描述符的数量。
  3. 我的机器中看到的“open files”的大小是1024/100001,也就是意味着,我的机器创建出来的进程最大打开的文件数量就是1024/100001
  4. “open files”的大小并不是没有办法改变的,是可以通过ulimit -n[count]进行改变。

5.理解文件描述符和文件流指针的区别

(1)从源码的角度理解,文件流指针(struct  _IO_FILE)是什么?

文件流指针FILE*及struct _IO_FILE是一个由C标准库定义的结构体,这个结构体中的一个int _filen元素保存文件描述符的数值。

这个结构体数组中含有我们在exit函数执行的时候清理缓冲区时提到的那个缓冲区

现在就知道为什么_exit函数不清理缓冲区吗?

因为缓冲区是C标准库定义的,而_exit函数是系统调用函数,当然不会清理缓冲区了。

 (2)将文件描述符和文件流指针结合起来

文件流指针对应的结构体 struct _IO_FILE这个结构体内部的成员变量 int _fileno保存了对应的文件描述符数值。

总结:文件流指针是标准库操作文件句柄,文件描述符是系统调用接口。

           文件流指针中包含了一个成员变量,这个成员变量中保存的就是文件描述符。

           库函数内部封装的就是系统调用函数。

           缓冲区是由C标准库定义的,是用户态的缓冲区,存在于文件流指针的结构体中。

6.重定向

(1)重定向的符号

        >:清空重定向

        >>:追加重定向

(2)重定向的接口

        int dup2(int oldfd,int newfd);

        作用:将newfd的值重定向为oldfd,既newfd拷贝oldfd

        参数:oldfd/newfd均是文件描述符

        成功:1.关闭newfd

                   2.让newfd指向oldfd对应的struct file*结构体

        失败:1.如果oldfd是一个非法/无效的文件描述符,则重定向失败;newfd没有变化

                   2.如果oldfd和newfd的值相等,则什么事都没干

(3)重定向代码

重定向标准输出到1.txt文件当中

 7.静态库&动态库

什么是库:静态库和动态库都是程序代码的集合。一般是为了方便将程序提供给第三方使用,就是将程序编写成二进制库文件提供给第三方(用户)使用调用着不必关心内部实现,只需要关心如何使用(如何调用)即可

(1)动态库

特征:win:没有前缀,后缀为dll

           linux:前缀为lib,后缀为.so

生成:使用gcc/g++编译器,增加两个命令行参数

                -fPIC

                -shared

生成动态库的代码中不需要包含main函数

使用:编译可执行程序的时候,依赖动态库

        -L [path] :指定动态库所在的路径

        -l[动态库的名称(去掉前缀和后缀之后的名称)]:指定编译可执行程序时,依赖的动态库。

都有哪些方式让程序可以找到动态库:

        1.将动态库放到可执行程序的路径下(不推荐)

        2.配置LD_LIBBARY_PATH

                动态库的环境变量:LD_LIBBARY_PATH(推荐的方式,公式中在客户机器部署的时候,常用的手段)

        3.放到系统库的路径下:/lib64

(2)静态库

        特征:win没有前缀,后缀为.lib

                   linux:前缀为lib,后缀为.a

        生成:第一阶段:使用gcc/g++将源代码编译成为目标程序(.o)

                   第二阶段:使用ar -rc 命令编译目标程序成为静态库

                        ar -rc [静态库文件名称] [目标文件]

        注意:如果直接使用源码时不行的

8.软硬链接

        软连接:目标文件的快捷方式

    

生成:ln -s 源文件 软连接文件

注意事项:

  1. 修改软连接文件,源文件也会被修改
  2. 源文件如果被删除,软连接文件还在的,修改软连接文件,会重新建立源文件,重新建立链接关系(这种要慎重)一定要在删除源文件的时候,将软链接文件也删除掉

 硬链接:目标文件的替身

     生成:ln 源文件 硬链接文件

9.文件系统

我们使用 ls -l 的时候看到的除了看到文件名,还看到了文件元数据

stat可以看到更多信息

ext2文件系统:

 Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被 划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs-b选项可以设 block大小为102420484096字节。而上图中启动块(Boot Block)的大小是确定的。

  • Block Groupext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
  • 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck inode的总量,未使用的blockinode的数量,一个blockinode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。SuperBlock的信息被破坏,可以说整个文件系统结构就被破坏了
  • GDTGroup Descriptor Table:块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下
    块位图( Block Bitmap ): Block Bitmap 中记录着 Data Block 中哪个数据块已经被占用,哪个数据块没
    有被占用
  • inode 位图( inode Bitmap ):每个 bit 表示一个 inode 是否空闲可用。
  • i 节点表 : 存放文件属性 如 文件大小,所有者,最近修改时间等
  • 数据区:存放文件内容
将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?我们通过 touch 一个新文件来看看如何工作。
[root@localhost linux]# touch abc
[root@localhost linux]# ls -i abc
263466 abc
为了说明问题,我们将上图简化:
创建一个新文件主要有一下 4 个操作:
1. 存储属性
内核先找到一个空闲的 i 节点(这里是 263466 )。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块: 300,500 800 。将内核缓冲区的第一块数据复制到 300 ,下一块复制到 500 ,以此类推。
3. 记录分配情况
文件内容按顺序 300,500,800 存放。内核在 inode 上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录
新的文件名 abc linux 如何在当前的目录中记录这个文件?内核将入口( 263466 abc )添加到目录文件。文件名和 inode 之间的对应关系将文件名和文件的内容及属性连接起来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值