文章目录
C语言文件I/O
写文件
没有指定文件的目录则会在当前目录下创建文件。
以"w"模式打开文件,会把原始内容清掉再写。
以"a"模式打开文件,会把在原始内容后面继续追加。
读文件
stdin & stdout & stderr
- C默认会打开三个输入输出流,分别是stdin, stdout, stderr
- 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针
当我们的运行C程序的时候,操作系统就会默认将这三个输入输出流打开,之后我们才能调用类似于scanf和printf之类的函数向键盘和显示器进行相应的输入输出操作。
所有的外设,本质核心操作就是 read 或 write。对于键盘文件,它的读方法就是从键盘读取数据到内存,对于显示器文件,如调用 printf 函数时,操作系统是要往显示器上写入的,其实你输入的命令是你通过键盘输入的,所以系统应该是往键盘读数据。至于用户能看到输入的命令,仅仅是为了方便用户,操作系统把从键盘输入的数据,一方面给了系统读取,一方面给显示器方便用户。
对于键盘
int read(…)
int write(…)
对于网卡
int read(…)
int write(…)
对于显示器
int read(…)
int write(…)
但不同的硬件,对应的读写方式肯定是不一样的
操作系统会为每一个底层硬件创建struct file结构体,此结构体中一定包含了两个函数指针,分别指向这个硬件对应的读方法和写方法
系统文件I/O
操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问。
open
系统接口中使用open函数打开文件
第一个参数
要打开或创建的目标文件
- 给出文件名,若当前目录存在,则打开,否则在当前目录创建;
- 若给出绝对路径,则在该路径下创建;
第二个参数
open函数的第二个参数是flags,表示打开文件的方式。打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
- O_RDONLY: 只读打开
- O_WRONLY: 只写打开
- O_RDWR : 读,写打开
- O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
- O_APPEND: 追加写
第三个参数
mode,表示创建文件的默认权限。
例如,将mode设置为0666,理论上文件创建出来的权限如下-rw-rw-rw-
但是实际上创建出来文件的权限值还会受到umask的影响,实际创建出来文件的权限为:mode&(~umask)。umask的默认值一般为0002,当我们设置mode值为0666时实际创建出来文件的权限为0664。也就是如下-rw-rw-r--
如果想使新创建出来的文件不受umask值的影响,那么仅需在创建该文件前令umask值为0,即
umask(0);
不需要创建文件时,第三个参数可以不写
返回值
- 成功:新打开的文件描述符
- 失败:-1
close
参数 :被关闭文件的文件描述符1。
返回值:关闭成功,返回0;关闭失败,返回-1;
write
参数:将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中。
返回值:写入成功,返回实际写入数据的字节个数;写入失败,返回-1;
read
参数:从文件描述符为fd的文件读取count字节的数据到buf位置当中。
返回值:读出成功,返回实际读取数据的字节个数;读出失败,返回-1;
测试
文件描述符fd
我们发现上述文件描述符是从3开始依次增加1的,那么,
- 0 ,1, 2去哪了?
- 文件描述符的分配规则又是什么呢?
我们在上面提到了三个系统帮我们默认打开的三个流,stdin、stdout、stderr
打开文件一定是进程运行的时候打开的
而任何进程在运行的时候都会默认打开三个输入输出流
即标准输入流、标准输出流以及标准错误流也就是stdin、stdout、stderr
标准输入对应的设备是键盘,
标准输出、标准错误对应的设备是显示器
我们在打开上述4个文件前如果close(0)
会发生什么?
我们发现第一个文件的文件描述符变为了0
从结果中我们可以看出,文件描述符的分配规则是在files_struct数组当中,找到当前没有被使用的
最小的一个下标,作为新的文件描述符
文件描述符的本质
一个进程可以打开一个文件,一个进程也可以打开多个文件,所以在操作系统内可能同时存在大量文件,对于这些大量的文件要被操作系统
先描述、再组织
的管理起来;
要打开一个文件就要为其创建对应的文件对象
struct file
(被打开文件的描述结构体对象),以双链表管理起来这些struct file
那么进程和打开文件的对于关系又如何维护?
那么就有了
struct files_struct
,它里面包含一个数组struct file *fd_array[]
,
进程的task_struct
中有一个指针struct files_struct *files
指向struct files_struct
对象,内核创建文件对象后将其的地址填入该数组struct file *fd_array[]
中
所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件
所以进程在访问对应的文件的时候,其实是先拿着文件描述符找到文件描述符数组中对应下标中的地址,再通过地址找到对应的struct file对象,再通过这个对象访问到磁盘中的文件。