欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第二十八讲 IO 函数

程序员文章站 2024-01-04 13:09:10
...

第二十八讲 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) 试试看其他灯的效果。如果你还想玩,可以改改亮灭函数控制不同灯亮的方法。

上一篇:

下一篇: