第二十八讲 IO 函数
第二十八讲 IO 函数
文章目录
本讲是联合几讲的内容,因为前面每一期内容都很少,所以结合起来了
本章会有一些扩展的知识,可能会有写偏差,但是会更详细一些,是参考了网上大佬们的理解。很感谢大佬们。
一、 文件描述符和打开模式
1、常见文件操作函数
函数 | 原型 | 参数及其说明 | 返回值 | 头文件 |
---|---|---|---|---|
open (打开文件) |
int open(const char *pathname, int flags); int open(const char *pathname, int flags,mode_t mode); |
pathname:表示要打开的文件路径。 flags:用于指示打开文件的选项,常用的有 O_RDONLY、O_WRONLY 和 O_RDWR。这三个选项必须有且只能有一个被指定。 mode:只在创建文件时需要,用于指定所创建文件的权限位(还要受到 umask 环境变量的影响)。 |
成功:文件描述符 失败:-1 |
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> |
close (关闭文件) |
int close(int fd) | fd:文件描述符 | 成功:0 失败:-1 |
#include <unistd.h> |
read (读取文件内容) |
ssize_t read(int fd,void *buff,size_t count) | fd:使用 open 打开的文件的文件描述符buff:读取的文件内容 count:需要读取的长度 |
成功: count:成功读取全部字节 0~count: 剩余文件长度小于count 读取期间被异步信号打断 失败: -1,读取错误 |
#include <unistd.h> |
write (写入文件) |
ssize_t write(int fd,void *buff,size_t count) | fd:使用 open 打开的文件的文件描述符buff:写入的文件内容 count:需要写入文件的长度 |
成功: count:成功写入全部字节 0~count: 写入期间被异步信号打断 失败: -1,读取错误 |
#include <unistd.h> |
lseek (设置文件读写位置) |
off_t lseek(int fd,off_t offset,int where) | fd:使用 open 打开的文件的文件描述符offset:偏移量 where:偏移量基准点 |
成功:文件偏移位置值 失败:-1 |
#include <unistd.h> |
sync (页缓存和回写) |
void sync(void); | 无 | 无 | #include <unistd.h> |
2、文件打开模式
选项 | 说明 |
---|---|
O_APPEND | 每次进行写操作时,内核都会先定位到文件尾,再执行写操作。 |
O_ASYNC | 使用异步 I/O 模式。 |
O_CLOEXEC | 在打开文件的时候,就为文件描述符设置 FD_CLOEXEC 标志。这是一个新的选项,用于解决在多线程下 fork 与用 fcntl 设置 FD_CLOEXEC 的竞争问题。某些应用使用 fork 来执行第三方的业务,为了避免泄露已打开文件的内容, 那些文件会设置 FD_CLOEXEC 标志。但是 fork 与 fcntl 是两次调用,在多线程下, 可能会在 fcntl 调用前,就已经 fork 出子进程了,从而导致该文件句柄暴露给子进程。关于 O_CLOEXEC 的用途 |
O_CREAT | 当文件不存在时,就创建文件 |
O_DIRECT | 对该文件进行直接 I/O,不使用 VFS Cache。 |
O_DIRECTORY | 要求打开的路径必须是目录 |
O_EXCL | 该标志用于确保是此次调用创建的文件,需要与 O_CREAT 同时使用; 当文件已经存在时,open 函数会返回失败。 |
O_LARGEFILE | 表明文件为大文件 |
O_NOATIME | 读取文件时,不更新文件最后的访问时间 |
O_NONBLOCK<br> O_NDELAY | 将该文件描述符设置为非阻塞的(默认都是阻塞的) |
O_SYNC | 设置为 I/O 同步模式,每次进行写操作时都会将数据同步到磁盘,然后write 才能返回 |
O_TRUNC | 在打开文件的时候,将文件长度截断为0,需要与O_RDWR或O_WRONLY同时使用。在写文件时,如果是作为新文件重新写入,一定要使用O_TRUNC标志,否则可能会造成旧内容依然存在于文件中的错误,如生成配置文件、pid文件等 |
3、lseek where 参数说明
参数 | 说明 |
---|---|
SEEK_SET | 基准点为文件开头 |
SEEK_CUR | 基准点为当前位置 |
SEEK_END | 基准点为文件末尾 |
二、 标准 io 函数
c 标准库实现了一个 IO缓存区。通过调用标准io函数讲数据写入io缓存区,当数据达到一定数量或者调用fflush
函数才会将数据写入内核空间的页缓存区。同理,当页缓存区满了或者调用sync
函数才能讲数据写入磁盘。他们的调用关系如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AOIgj9cX-1632844288613)(C:\Users\Dragon\Desktop\野火linux学习笔记\第一期\第二十八讲.assets\image-20210924232307432.png)]
1、 常见的标准io函数
函数 | 说明 |
---|---|
fopen | 打开文件 |
fclose | 关闭文件 |
fread | 读取文件 |
fwrite | 写入文件 |
fseek | 设置文件读写位置 |
fflush | 同步文件进入页缓冲区 |
2、文件io五大模式
模式 | 说明 |
---|---|
非阻塞模式 | 当需要读写文件时,即使文件没法正常读写,也必须立马返回,不等待文件就绪 |
阻塞模式 | 当需要读写文件时,文件没法正常读写时,等待文件就绪,然后进行正常读写 |
io多路复用 | IO多路复用主要有select、poll、epoll三种模式,select/poll相差不大,主要是通过轮询来不断的检测是否有描述符已就绪,select默认情况下支持最多监控1024个描述符,poll则没有这个限制(底层通过链表实现,可动态增加);epoll不是通过轮询,而是通过回调(callback)方式主动通知已有描述符已就绪,相比较select/poll效率有明显提升。 |
异步io | 读写文件的时候,只需要将数据发送过去,系统会将数据进行读写,然后发送信号告诉执行的读写状态 |
信号驱动io | 等待文件读写就绪状态,当文件就绪时,系统发送信号量通知用户可以读写文件,然后用户再进行读写 |
三、控制 led 设备
1、驱动程序
本质
为硬件设备创建相应的设备节点文件;创建设备文件时,规定好设备文件的使用方式。
根据驱动程序规定的设备文件的使用方式去控制硬件。
步骤
1、找出硬件设备所对应的设备节点文件(/dev 或者 /sys 目录下)
-
led 设备节点文件目录
/sys/class/leds
2、找出驱动程序规定的设备文件的使用方式
- 往 brightness 文件写入数值
四、实验代码(驱动红色led小灯)
/*
* @LastEditors: 夜雨
* @Date: 2021-09-26 22:23:56
* @LastEditTime: 2021-09-28 23:10:02
* @FilePath: /001led/operations_led.c
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#define LEDS_ROOT_PATH "/sys/class/leds/"
#define LEDS_OPTION_FILE "/brightness"
#define LEDS_CUR_PATH(rootpath, color, operationfile) ""rootpath""color""operationfile""
#define LEDS_PATH(color) LEDS_CUR_PATH(LEDS_ROOT_PATH, color, LEDS_OPTION_FILE)
int main(char* argc, char**argv)
{
int redFd = open(LEDS_PATH("red"), O_WRONLY);
{
if(redFd == -1)
{
printf("open %s is failed",LEDS_PATH("red") );
close(redFd);
while(1);
}
}
while(1)
{
write(redFd, "255", 3) ;
printf("write 255\r\n" );
sleep(1);
write(redFd, "0", 1) ;
printf("write 0\r\n" );
sleep(1);
}
return 0;
}
五、 其他
本章讲到的所有函数并没有都将例子举出来,如果感兴趣,可以自己去实践下。当然,如果您仅仅只看文章,不去敲代码,其实是学不到什么东西的。并且这种要经常用才能加深自己的印象。
在实验代码中,我只做了红灯实验,感兴趣的可以将 open(LEDS_PATH("red"), O_WRONLY)
改成 open(LEDS_PATH("green"), O_WRONLY)
或者 open(LEDS_PATH("blue), O_WRONLY)
试试看其他灯的效果。如果你还想玩,可以改改亮灭函数控制不同灯亮的方法。