六、文件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 }