Linux 基础IO 2
读取与写入
read与fread
在基础IO 1中我们学会了open和fopen的函数这两个函数是用于为进程打开文件也可以理解为为进程和文件建立了一个链接使其可以交互。那我们建立号链接之后肯定还是需要对文件进行操作,现在我们先来了解读取操作。
read:
这是一个系统调用函数,从文件中读取数据。
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
fd :文件描述符,代表了需要读取的文件或设备。文件描述符可以通过调用 open 或其他文件操作函数获取。
buf :一个指向用户分配的缓冲区的指针,read 将把读取到的数据写入该缓冲区。
count :需要读取的字节数,表示最多读取 count 字节数据。
返回值:
若成功,read 返回读取的字节数(0表示已到达文件末尾)。
若失败,返回 -1,并设置 errno 来指示具体错误。
在读取的时候我们需要先定义一个缓冲区,这里我们定义了一个字符数组buff来接收读取到的数据。read是默认阻塞读取的函数,若是读取数据的请求还没完成会一直阻塞等待。我们可以在open函数中设置非阻塞模式O_NONBLOCK。
错误:
EINTR
:调用被信号中断。
EIO
:发生I/O错误(如硬件故障)。
EINVAL
:参数不合法,比如文件描述符不是合法的读取对象。
EBADF
:文件描述符无效,可能因为文件未打开或者以不适合的方式打开(如只写模式下无法读取)。
EFAULT
:缓冲区地址不合法
fread:
fread是一个C标准库函数
头文件:
#include <stdio.h>
函数原型:
size_t fread(void*buff , size_t size, size_t count , FILE* stream);
参数:
buff:指向存储读取数据的缓冲区。
size:每个数据项的字节数。
count:要读取的数据项的个数。
stream:指向FILE对象的指针,表示要读取的文件。
与read一样需要先定义一个接收缓冲区来接收读取的数据。与read不同的是这里的读取是按照一定格式读取的,第二个参数就是一个数据类型的大小如我们若是整形的话这里就是4字节若是自定义类型就计算出类型的大小填入,第三个则是读取多少个这样的数据若第二个参数为4则这里就是读取10个4字节的数据。与read不同的还有一点,fread有一个用户级的缓冲区。
write与fwrite
若我们的程序产生了一些数据需要存储那么我们不能把它一直放在内存中而是需要写入到文件中去这需要使用write和fwrite函数这两个都是输出函数也就是执行写入操作的函数。
write:
这是个系统调用,会将数据从缓冲区写入到指定的文件中。
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd :文件描述符,代表了需要读取的文件或设备。文件描述符可以通过调用 open 或其他文件操作函数获取。
buf :一个指向用户分配的缓冲区的指针,read 将把读取到的数据写入该缓冲区。
count :需要读取的字节数,表示最多读取 count 字节数据。
返回值:
若成功,write 返回读取的字节数(0表示已到达文件末尾)。
若失败,返回 -1,并设置 errno 来指示具体错误。
这里我们定义了一个缓冲区buf里面存放了”i like linux\n“,write函数就会将buf中的数据写入带fd所代表的文件中。
错误:
EBADF
: 参数fd不是有效的文件描述符或者不支持写操作。
EIO
: I/O错误发生。
ENOSPC
: 设备上没有剩余空间。
fwrite:
是一个C标准库函数功能与write相似,与fread一样会有一个用户级的缓冲区。
头文件:
#include <stdio.h>
函数原型:
size_t fread(void* buff,size_t size,size_t count,FILE* stream);
参数:
buff:指向存储写入数据的缓冲区。
size:每个数据项的字节数。
count:要写入的数据项的个数。
stream:指向FILE对象的指针,表示要写入的文件。
小结
其实无论是输入输出系统调用函数还是C标准库函数做的工作本质都是数据的拷贝,输入是将系统缓冲的数据拷贝进进程的空间中,输出就是将进程缓冲区数据拷贝到系统缓冲区中,然后通过刷新到屏幕上显示出来。只是输入和输出拷贝的方向不同。而C标准库的输入输出函数还会有一个用户级的缓冲区来存放数据这个可以根据实际需要自定义大小,系统调用的函数也有缓冲区,这个缓冲区是设置好的属于系统内核的,缓冲区的作用主要是增加系统的效率。
缓冲区刷新
缓冲区是为了可以提高数据传输的效率而设置的,因为向文件写入读取数据可能不是连续而是断续的,而每次读取写入数据都需要转换到内核中并找到文件所在位置和当前文件位置就是载入相关属性数据也会消耗cpu资源所以有了缓冲区可以一次性读取写入一段较为完整的数据。
刷新方式:
一般数据刷新有三种方式
无缓冲:数据立刻刷新没有缓冲区机制
行刷新:一般系统内核用的就是这种,完成一行的数据就刷新一行,若是一行不满就会等待其满,这里的满不一定是缓冲区写满,类似换行也会刷新,所以当我们使用printf,cout的时候若后面没有加上\n那么可能这个数据不会显示出来或者是跟着其他数据一起显示出来,就是因为这些数据在缓冲区中并没有冲刷。
全缓冲:缓冲区全满的时候一次性刷新,我们在磁盘上拷贝数据就是使用的这种。所以我们平时拷贝数据的时候会一段时间很慢,然后一段时间速度突然就快起来了然后又变慢了就是因为缓冲区机制。
其他刷新机制:
fflush函数:参数是文件指针这是一个C语言的库函数对应着fread,fwrite使用。给他一个文件指针会将文件对应的缓冲区数据冲刷出去。
进程结束:当一个进程结束时操作系统会冲刷掉它的所有缓冲区
系统缓冲区强制刷新在linux上可以使用sync指令,在进程内就是 system(”sync“);system函数的头文件是 stdlib.h ,这个指令还有其他选项有兴趣可以区了解下。