注意!以下系统调用的具体使用方法可通过"man 2 open"命令查看使用手册。

1. open()

返回值

如果打开成功,返回一个非负整数的文件描述符。
如果打开失败,返回-1,并且设置错误号给系统定义的全局变量errno,用于标记函数到底出了什么错误。

关于形参flags的O_CREAT、O_EXCL选项。

(1)O_CREAT

指定O_CREAT时,如果:
文件存在:直接打开
文件不存在:创建该文件。

(2)O_EXCL

当O_EXCL与O_CREAT同时被指定,打开文件时,如果文件之前就存在的话,就报错。保证每次open的是一个新的文件,如果文件以前就存在,提醒你open的不是一个新文件。这使得测试和创建两者成为一个原子操作。

(3)O_APPEND

多次open同一文件,实现共享操作时,指定O_APPEND可以防止数据相互覆盖的发生。

open打开文件时,内核做了哪些事情

记录打开文件的信息。

程序运行起来后就是一个进程了,OS会使用系统函数,在内存中开辟一个空间用于存储一个task_struct的结构体(也称为进程表),记录进程运行时的各种信息,比如所打开文件的相关信息。
open将文件成功打开后,在task_struct中又会创建一些结构体,用于记录当前进程目前所开文件的信息,后续所有的文件操作,都需要依赖于这些信息,其中就包括指向打开文件的文件描述符。

2. perror()

perror() 是一个库函数。可将上一个函数发生错误的原因(errno对应的原因)输出到STDERR。故不用刻意去记errno的宏。
工作原理:调用perror函数时,它会自动去一张对照表,将errno中保存的错误号,换成具体的文字信息并打印出来。

3. write/read

数据流动过程

程序缓存(buf数组)<——>open打开文件时开辟的内核缓存<——>驱动程序的缓存<——>块设备上的文件

实际得到的字节数小于要求读取的字节数的情况:

  1. 读普通文件时,在读到要求字节数之前已达到文件尾端。
  2. 从终端设备读时,一次最多读一行。
  3. 从网络读时,网络中的缓冲机制可能造成返回值小于所要求读取的字节数。
  4. 从某些面向记录的设备读时,一次最多返回一个记录。
  5. 当信号造成终端。

read(0, buf, sizeof(buf))实现的是什么功能

实现的是,从键盘读取数据到到缓存buf中。具体的数据中转的过程是:
键盘——>键盘驱动程序的缓存——>open /dev/stdin时开辟的内核缓存——>read应用缓存buf

read会将从键盘读入的任何数据都理解为字符型,即输入100的话,会看做是 ‘1’, ‘0’, ‘0’.
write与read同理,故如果需要用write打印一个整形数字需要:

int a = 65;
write(1, &a, sizeof(a));

scanf()

是对read()的二次封装,主要加入了人性化的格式转换功能。当scanf()不能完全替代read(),因为read()能读取某些驱动设备。

4. 标准输入/输出/错误文件

程序开始运行时,默认会调用open()将标准输入/输出/错误文件打开

1和2有啥区别?

使用这两个文件描述符,都能够把文字信息打印到屏幕。如果仅仅是站在打印显示的角度,其实用哪一个文件描述符都能办到。
用途:
1:专用于打印普通信息
2:专门用于打印各种报错信息

lseek()

(1) od -c [文件名]:以字符形式查看文件内容。
(2) 可以使用lseek制作出空洞文件

5. 文件描述符


其中,进程表项存储在进程空间中;文件表(由open函数创建)存储在内核空间中;v节点表中存储了函数指针,当read、write操作文件时,会根据底层硬件具体情况的不同,调用不同的函数来实现读写。

为什么使用O_APPEND可以解决在文件共享中,写操作相互覆盖的情况?

文件大小信息被大家共享,当文件被写入数据后,文件大小会同时得到更新,当open都指定O_APPEND后,使用不同的文件描述符写数据时,都会先将文件位移量更新为文件的大小,保证每次都是在文件的最末尾写数据,就不会出现相互覆盖的情况。

6. dup()

功能:复制某个已经打开的文件描述符,得到一个新的描述符,这个新的描述符,也指向被复制描述符所指向的文件。
dup2():功能同dup(),只不过在dup2()中,使用者可以指定新文件描述符。如果这个新文件描述符已经被占用,dup2会把它关闭后再使用。

dup、dup2复制的意义

1.实现文件的共享操作。使用dup、dup2复制方式实现文件共享时,不管复制出多少个文件描述符,它们永远对应同一个文件表,故共享同一个文件位移量,无论谁操作后文件位移量都会被更新,因此不会出现覆盖。
2.实现输入输出重定向。将STDIN_FILENO或者STDOUT_FILENO指向需要重定向的文件描述符即可。

7. sync、fsync和fdatasync

  1. sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。
    通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数。这就保证了定期冲洗内核的块缓冲区。命令sync(1)也调用sync函数。
  2. fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。fsync可用于数据库这样的应用程序,这种应用程序需要确保将修改过的块立即写到磁盘上。
  3. fdatasync函数类似于fsync,但它只影响文件的数据部分。而除数据外,fsync还会同步更新文件的属性。

8. fcntl()

主要功能有:

  1. 复制一个已有描述符
  2. get/set fd
  3. get/set 文件状态标志
  4. get/set 异步IO所有权
  5. get/set 记录锁