Linux基础IO-2

在上一篇博客之中,我们对C语言中的文件基本操作进行了一个简答的回顾,并通过vim编写代码对库函数进行了简单实现。

那么这一篇博客我们我们将从系统调用接口出发,来对文件IO进行一个介绍。

目录

1.open

1.1参数介绍

1.2返回值

2.write

2.1参数介绍

2.2返回值

3.read

3.1参数介绍

3.2返回值

4.lseek

4.1参数介绍

4.2返回值

5.close

5.1参数介绍

6.实例

7.库函数和系统调用接口

1.open

int open(char *pathname, int flag, int mode);

1.1参数介绍

其中,参数pathname则是需要打开文件的路径名;flag则是对应的文件打开方式,打开方式的选择代表着对文件的不同操作选项;

flag存在3个必选项(只能选择其一,不能同时使用):O_RDONLY、O_WRONLY、O_RDWR,分别对应文件操作中的:只读、只写和可读可写。

值得一提的是:这3个选项的本质是一个宏(底层实现中的定义),对应3个数字,分别为:00、01和02(16进制)。所以这3者只能选择其一,不能同时使用,因为加以“或”来使用,本质上其对应的数字并没有发生改变,还可能会变为未知值(接口无法识别)。

flag还存在可选项:O_CREAT--文件不存在则创建;O_TRUNC--截断文件,即丢弃文件原有数据;O_APPEND--追加写,总是将新加入数据写道文件末尾。

例子:当我们要实现库函数中提到的w+打开方式时,通过系统调用接口open来实现的话,其中打开方式选项的内容即为:O_RDWR | O_CREAT | O_TRUNC。

对于open中的第3个参数mode,当O_CREAT被使用的时候,就一定需要使用mode来设定被创建文件的权限。值得注意的时:当我们通过数字来对创建文件赋权时,一定要注意赋权数字首位的0不能省略,因为在常用权限位之前还存在一些特殊的权限位,不过我们很少加以关注。

1.2返回值

接口执行成功则返回一个文件描述符(非负整数)作为文件的操作句柄;失败则返回-1。

2.write

ssize_t write(int fd, char *buf, size_t len);

2.1参数介绍

fd--open接口打开文件时返回的文件操作句柄;buf--要写入文件数据所在的空间首地址;len--要写入文件数据的长度(以字节为单位)。

2.2返回值

写入数据成功则返回写入的数据长度(写入数据长度为0时,返回0),失败则返回-1。

3.read

ssize_t read(int fd, char *buf, size_t len);

3.1参数介绍

fd--open接口打开文件时返回的文件操作句柄,buf--一块空间的首地址,用于存放读取到的文件内容;len--需要读取的数据长度(len的长度不能大于buf的大小,防止越界)。

3.2返回值

读取数据成功则返回实际读取到的数据长度(字节为单位),如果返回0,则说明光标位置(读写位置)位于文件末尾;失败则返回-1。

4.lseek

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

4.1参数介绍

fd--open接口打开文件时返回的文件操作句柄;offset--光标位置(读写位置)偏移量;wherence--偏移量的起始位置。

wherence参数对应3个关键字:SEEK_SET--文件起始位置;SEEK_CUR--光标(读写)所在位置;SEEK_END--文件末尾位置。

4.2返回值

当前跳转后,读写位置相当于文件起始位置的偏移量,根据这个特性,我们可以使用该接口让读写位置跳转到文件末尾,如此lseek的返回值便为文件的大小。失败则返回-1。

5.close

int close(int fd);

 关闭打开文件,释放资源。

5.1参数介绍

fd--open接口打开文件时返回的文件操作句柄。

6.实例

介绍完系统调用接口对文件的操作之后,还是老方法,让我们通过代码来对上述接口进行一个简单的实践。

Linux基础IO-2_第1张图片

 最后所执行出的结果便是,创建出对应的main.txt文件,也成功将data写入到其中,最后也成功读取到了main.txt中的内容,通过可执行文件test很好的打印了出来。正如下图所示:

Linux基础IO-2_第2张图片

 7.库函数和系统调用接口

在大致了解完库函数和系统调用接口之后,我们来对二者再次进行一个简答的比较和讲述。

老生常谈的概念是,系统调用接口是底层提供给我们来对内核实现管理的一种方式,而库函数则是前人对系统调用接口的进行一部封装,提高了管理方式的功能性。

那么从对文件的操作就可以看出,库函数返回的文件操作句柄是一个FILE*的数据,而系统调用接口中的文件操作句柄是一个int类型的数据。显而易见,FILE结构体就是对文件描述符了一个封装,其内部肯定包含了文件描述符成员。

然后让我们来聊一聊缓冲区,其实从本质上而言,对于系统调用接口而言并不存在缓冲区,所以在之前的博客当中,我们演示通过系统调用接口_exit()来退出程序的时候会发现缓冲区没有被刷新,对应内容没有被打印显示。

其实当我们调用系统调用接口来对内容进行打印的时,会发现系统调用接口的打印内容会直接显示在终端上,而对于printf库函数则是会等进程结束之后才会将内容打印显示到终端上。

当我们查询库函数和系统调用接口的内容时,我们会发现对于系统调用接口而言并不存在缓冲区,所以当有内容需要被打印时,它会即刻打印结果显示在终端。而对于库函数,它们在封装时,前人定义了缓冲区的内容,需要打印的内容会不断的添加到缓冲区当中,直到缓冲区被刷新,其结果才会显示到终端上。

那么,我们为什么需要创建缓冲区,缓冲区的作用是什么。这个问题的答案其实很简单,是为了避免多次IO,提高文件IO效率。

你可能感兴趣的:(从0开始的Linux,linux)