• 六、文件IO——fcntl 函数 和 ioctl 函数


    6.1 fcntl 函数

    6.1.1 函数介绍

    1 #include <unistd.h>
    2 #include <fcntl.h>
    3 int fcntl(int fd, int cmd);
    4 int fcntl(int fd, int cmd, long arg);
    5 int fcntl(int fd, int cmd, struct flock * lock);
    • 函数说明:fcntl()用来操作文件描述词的一些特性。
    • 函数功能:可以改变已经打开文件的性质
    • 参数说明
      • @fd:代表欲设置的文件描述符
      • @cmd:代表欲操作的指令。有以下几种情况:
        • F_DUPFD:用来查找大于或等于参数 arg 的最小且仍未使用的文件描述符,并且复制参数 fd 的文件描述符。执行成功则返回新复制的文件描述符。请参考dup2()。复制文件描述符,新的文件描述符作为函数返回值返回
        • F_GETFD:获取文件描述符,通过第三个参数设置
        • F_SETFD:设置文件描述符,通过第三个参数设置
        • F_GETFL/F_SETFL:
          • 取得/设置文件状态标志,通过第三个参数设置
          • 可以更改的几个标志是:O_APPEND、O_NONBLOCK、SYNC、O_ASYNC(O_RDONLY、O_WRONLY和O_RDWR不适用)
        • F_GETLK 取得文件锁定的状态。
        • F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。
        • F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。
      • @参数lock指针为flock 结构指针,定义在下面。
    • 返回值:
      • 成功则返回0,若有错误则返回-1,错误原因存于errno。
    • 常见的功能:
      • 复制一个现存的描述符,新文件描述符作为函数返回值(cmd = F_DUPFD)
      • 获得/设置文件描述符标志(cmd = F_GETFD 或 F_SETFD)
      • 获得/设置文件状态标志(cmd = F_GETFL 或 F_SETFL)
      • 获得/设置文件锁(cmd = F_SETLK、cmd= F_GETLK、F_SETLKW)
        • 第三个参数为 struct flock 结构体,定义如下  
    1 struct flcok
    2 {
    3     short int l_type; /* 锁定的状态*/
    4     short int l_whence;/*决定l_start位置*/
    5     off_t l_start; /*锁定区域的开头位置*/
    6     off_t l_len; /*锁定区域的大小*/
    7     pid_t l_pid; /*锁定动作的进程*/
    8 };
    • l_type 有三种状态:
      • F_RDLCK 建立一个供读取用的锁定
      • F_WRLCK 建立一个供写入用的锁定
      • F_UNLCK 删除之前建立的锁定
    • l_whence 也有三种方式:
      • SEEK_SET 以文件开头为锁定的起始位置。
      • SEEK_CUR 以目前文件读写位置为锁定的起始位置
      • SEEK_END 以文件结尾为锁定的起始位置。    

    6.1.2 例子 

      文件状态标志设置

      io.h

    1 #ifndef __IO_H__
    2 #define __IO_H__
    3 
    4 extern void copy(int fdin, int fdout);
    5 
    6 extern void set_fl(int fd, int flag);
    7 extern void clr_fl(int fd, int flag);
    8 #endif

      io.c

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include "io.h"
     6 #include <string.h>
     7 #include <errno.h>
     8 #include <stdlib.h>
     9 #include <stdio.h>
    10 #include <fcntl.h>
    11 
    12 
    13 #define BUFFER_LEN 1024
    14 
    15 /* 文件的读写拷贝 */
    16 void copy(int fdin, int fdout)
    17 {
    18     char buff[BUFFER_LEN];
    19     ssize_t size;
    20 
    21 //    printf("file length: %ld
    ", lseek(fdin, 0L, SEEK_END));//将文件定位到文件尾部,偏移量为0L
    22 //    lseek(fdin, 0L, SEEK_SET);// 定位到文件开头
    23 
    24     while((size = read(fdin, buff, BUFFER_LEN)) > 0) { //从 fdin 中读取 BUFFER_LEN 个字节存放入  buff 中
    25 //        printf("current: %ld
    ", lseek(fdin, 0L, SEEK_CUR));
    26 
    27         if(write(fdout, buff, size) != size) {
    28             fprintf(stderr, "write error: %s
    ", strerror(errno));
    29             exit(1);
    30         }
    31     }
    32     if(size < 0) {
    33         fprintf(stderr, "read error:%s
    ", strerror(errno));
    34         exit(1); // 相当于 return 1;
    35     }
    36 }
    37 
    38 
    39 void set_fl(int fd, int flag)
    40 {
    41     int val;
    42 
    43     //获得原来的文件状态标志
    44     val = fcntl(fd, F_GETFL);
    45     if(val < 0) {
    46         perror("fcntl error");
    47     }
    48 
    49     //增加新的文件状态标志
    50     val |= flag;
    51 
    52     //重新设置文件状态标志(val 为新的文件状态标志)
    53     if(fcntl(fd, F_SETFL, val) < 0) {
    54         perror("fcntl error");
    55     }
    56 }
    57 
    58 void clr_fl(int fd, int flag)
    59 {
    60     int val;
    61 
    62     val = fcntl(fd, F_GETFL);
    63     if(val < 0) {
    64         perror("fcntl error");
    65     }
    66     //清除指定的文件状态标志(设置为0)
    67     val &= ~flag;
    68     if(fcntl(fd, F_SETFL, val) < 0) {
    69         perror("fcntl error");
    70     }
    71 }

      file_append.c

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <fcntl.h>
    10 #include "io.h"
    11 
    12 int main(int argc, char *argv[])
    13 {
    14     if(argc < 3) {
    15         fprintf(stderr, "usage: %s content destfile
    ", argv[0]);
    16         exit(1);
    17     }
    18 
    19     int fd;
    20     int ret;
    21     size_t size;
    22 
    23     fd = open(argv[2], O_WRONLY);
    24 
    25     //设置追加的文件状态标志
    26     set_fl(fd, O_APPEND);
    27 
    28     //清除追加文件状态标志
    29     //clr_fl(fd, O_APPEND);
    30     /*
    31     fd = open(argv[2], O_WRONLY | O_APPEND);
    32     if(fd < 0){
    33         perror("open error");
    34         exit(1);
    35     }
    36 
    37     */
    38 /*
    39     //定位到文件尾部
    40     ret = lseek(fd, 0L, SEEK_END);
    41     if(ret == -1) {
    42         perror("lseek error");
    43         close(fd);
    44         exit(1);
    45     }
    46 */    
    47     sleep(10); //睡眠 10s
    48 
    49     //往文件中追加内容
    50     size = strlen(argv[1]) * sizeof(char);
    51     if(write(fd, argv[1], size) != size) {
    52         perror("write error");
    53         close(fd);
    54         exit(1);
    55     }
    56 
    57     return 0;
    58 }

      编译调试与 file_append 例子中相同

    6.2 ioctl函数---io设备控制函数

    1 #include <unistd.h>
    2 #include <sys/ioctl.h>
    3 int ioctl(int fd, int cmd, …)
    • 函数说明:
      • ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。
      • ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。
      • 此函数可以说是 I/O操作的杂物箱。不能用前面说的函数表示的 I/O 操作通常都能用 ioctl 表示。
      • 终端 I/O 是 ioctl 的最大使用方面,主要用于设备 I/O 控制
    • 函数功能:
      • 控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。
      • 用于向设备发控制和配置命令 ,有些命令需要控制参数,这些数据是不能用read / write 读写的,称为Out-of-band数据。
      • 也就是说,read / write 读写的数据是in-band数据,是I/O操作的主体,而ioctl 命令传送的是控制信息,其中的数据是辅助的数据。
    • 参数说明:
      • @fd :就是用户程序打开设备时使用open函数返回的文件标示符,
      • @cmd :就是用户程序对设备的控制命令,
      • @.... : 省略号,那是一些补充参数,即可变参数列表,一般最多一个,有或没有是和cmd的意义相关的。
    • 返回值:成功为0,出错为-1

    6.2.2 例子

      从键盘 IO 设备中,读取键盘的数据

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <unistd.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <fcntl.h>
    10 #include <sys/ioctl.h>
    11 #include <linux/input.h>
    12 
    13 int main(int argc, const char *argv[])
    14 {
    15     int fd = -1;
    16     char name[256] = "Unknown";
    17     struct input_event event;//事件源
    18     int ret = 0;
    19 
    20     if((fd = open("/dev/input/event1", O_RDONLY)) < 0) {
    21         perror("open error");
    22         exit(1);
    23     }
    24 
    25     //EVIOCGNAME 宏的作用是获得 IO 设备的名称
    26     if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
    27         perror("evdev ioctl error
    ");
    28         exit(1);
    29     }
    30 
    31     printf("The device says its name is %s
    ", name);
    32 
    33     //读写打开的设备文件
    34     while(1) {
    35         ret = read(fd, &event, sizeof(event));
    36         if(ret < 0) {
    37             perror("read event error
    ");
    38         }
    39 
    40         if(EV_KEY == event.type) {
    41             //如果事件是一个按键码
    42             printf("key code is %d
    ", event.code);
    43 
    44             if(event.code == 16) {
    45                 //按 q 退出此应用程序
    46                 break;
    47             }
    48         }
    49     }
    50 
    51     close(fd);
    52 
    53     return 0;
    54 }

      

  • 相关阅读:
    [TJOI2018]教科书般的亵渎
    luogu P4781 【模板】拉格朗日插值
    [SDOI2010]捉迷藏
    [CQOI2016]K远点对
    BZOJ4066 简单题
    [国家集训队]JZPFAR
    Understanding User and Kernel Mode
    Linux下如何查看系统启动时间和运行时间以及安装时间
    CentOS Linux搭建独立SVN Server全套流程(修改svn仓库地址、服务启动等)
    linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合:
  • 原文地址:https://www.cnblogs.com/kele-dad/p/9033766.html
Copyright © 2020-2023  润新知