六、文件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\n", 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\n", lseek(fdin, 0L, SEEK_CUR));
26 
27         if(write(fdout, buff, size) != size) {
28             fprintf(stderr, "write error: %s\n", strerror(errno));
29             exit(1);
30         }
31     }
32     if(size < 0) {
33         fprintf(stderr, "read error:%s\n", 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\n", 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\n");
28         exit(1);
29     }
30 
31     printf("The device says its name is %s\n", name);
32 
33     //读写打开的设备文件
34     while(1) {
35         ret = read(fd, &event, sizeof(event));
36         if(ret < 0) {
37             perror("read event error\n");
38         }
39 
40         if(EV_KEY == event.type) {
41             //如果事件是一个按键码
42             printf("key code is %d\n", event.code);
43 
44             if(event.code == 16) {
45                 //按 q 退出此应用程序
46                 break;
47             }
48         }
49     }
50 
51     close(fd);
52 
53     return 0;
54 }

  

猜你喜欢

转载自www.cnblogs.com/kele-dad/p/9033766.html