1. 程式人生 > >六、文件IO——fcntl 函數 和 ioctl 函數

六、文件IO——fcntl 函數 和 ioctl 函數

read event 情況 har 屬性 並且 enc 名稱 lock

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 }

  

六、文件IO——fcntl 函數 和 ioctl 函數