本系列文章節選自本人所著《Linux下C語言應用程式設計》。
本系列文章,所需代碼請從以下位址下載下傳:
http://download.csdn.net/download/scyangzhu/5129027
1.1.1 ioctl
ioctl 函數是I / O操作的雜物箱。不能用本章中其他函數表示的I / O操作通常都能用ioctl表示。終端I / O是ioctl 的最大使用方面,主要用于裝置的I / O控制。例如:序列槽線上傳送的資料通過read、write來操作,而序列槽的波特率、校驗位、停止位可以通過ioctl來設定。再例如,使用ioctl來控制光驅的彈出操作等。
#include <sys/ioctl.h>
int ioctl(int fd, int cmd, . . . ) ;
功能:要求裝置完成某種操作
傳回:若出錯則為- 1,若成功則為其他值。
參數:
fd:要操控的裝置的檔案描述符
cmd:要求裝置完成的操作。一般會是針對該裝置的頭檔案中定義的宏
第3個參數:針對cmd操作的參數
使用範例(使用ioctl控制CDROM):
ioctl.c
1 #include <linux/cdrom.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <fcntl.h>
5
6 int main(void)
7 {
8 int fd =open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
14 // ioctl(fd, CDROM_LOCKDOOR, 0);
15 if (!ioctl(fd, CDROMEJECT, NULL)) {
16 printf("eject cdromsucceed\n");
17 } else {
18 printf("eject cdromfailed\n");
19 }
20
29 return 0;
30 }
運作結果:
[[email protected] chap02]$ sudo./ioctl
eject cdrom succeed
代碼分析:
1行包含的頭檔案linux/cdrom.h中,定義了光驅這種裝置所支援的操作的宏
8行調用open打開光驅,得到對應光驅的檔案描述符。由于open光驅時,可能光驅中沒有CD光牒,是以需要使用O_NONBLOCK選項,否則open會失敗
15行調用ioctl,向光驅發出CDROMEJECT指令(彈出CD光牒指令)。使用者可看到CD光牒被成功彈出
1.1.2 select
read函數可以監控1個檔案描述符(例如:鍵盤)是否有輸入,當鍵盤沒有輸入時,read将阻塞,直到使用者從鍵盤輸入資料,read成功傳回。用相同的方法可以監控滑鼠是否有輸入。但要同時監控滑鼠和鍵盤是否有輸入,這個方法就無能為力了。如下面程式所示:
1 fd = open(“/dev/input/mice”,O_RDONLY); // /dev/input/mice是滑鼠的裝置檔案
2 read(0, buf, 100);
3 read(fd, buf, 100);
這是因為,當read 鍵盤時若無輸入,則程式阻塞在第2行,此時即使滑鼠有輸入,程式也沒有機會執行第3行去獲得滑鼠的輸入。
這種情況下,需要使用select同時監控多個檔案描述符
#include <sys/select.h>
intselect(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, conststruct timeval *timeout)
功能:同時監控多個檔案描述符上是否有輸入、輸出、錯誤
參數:
maxfd:表示要檢測的描述符個數,是以其值應為最大描述符+1
readset:被監控是否有輸入的檔案描述符集。不監控時,設為NULL
writeset:被監控是否可以輸出的檔案描述符集。不監控時,設為NULL
exceptset:被監控是否有錯誤産生的檔案描述符集。不監控時,設為NULL
timeval:監控逾時時間。設定為NULL,表示一直阻塞到有檔案描述符被監控到有指定變化
傳回值:失敗,傳回-1;成功,傳回readset 、writeset、exceptset集中所有有指定變化的檔案描述符的數目(若是因逾時而傳回,傳回值為0)。
注:readset 、writeset 、exceptset3 個描述符集指針均是值- 結果參數。調用時,被監控描述字相應位需置1;傳回時未就緒描述字相應位會被清0,而就緒描述字相應位會被置1
以下幾個系統定義的宏,會與select配套使用
FD_ZERO(&rset):清0檔案描述符集rset所有位
FD_SET(4,&rset):設定檔案描述符集rset的bit4
FD_CLR(fileno(stdin),&rset):清0檔案描述符集rset的bit0
FD_ISSET(socketfd,&rset):若檔案描述符集rset中對應socketfd的位置1,傳回真;反之,傳回假
使用範例(同時監控鍵盤和滑鼠是否有輸入):
select.c
1 #include <stdio.h>
3 #include <sys/select.h>
4 #include <fcntl.h>
5 #include<unistd.h>
6
7 #define MAXNUM 100
8
9 int main(void)
10 {
11 fd_set rfds;
12 struct timeval tv;
13 int retval, fd;
14 char buf[MAXNUM];
15
16 fd = open("/dev/input/mice", O_RDONLY);
21 while (1) {
22 FD_ZERO(&rfds);
23 FD_SET(0, &rfds);
24 FD_SET(fd,&rfds);
25 tv.tv_sec = 5;
26 tv.tv_usec = 0;
27
28 retval = select(fd + 1,&rfds, NULL, NULL, &tv);
29 if (retval < 0)
30 printf("error\n");
31 if (retval == 0)
32 printf("No datawithin 5 seconds\n");
33 if (retval > 0) {
34 if (FD_ISSET(0,&rfds)) {
35 printf("Data is available from keyboard now\n");
36 read(0, buf,MAXNUM);
37 }
38 if (FD_ISSET(fd,&rfds)) {
39 printf("Data is available from mouse now\n");
40 read(fd, buf,MAXNUM);
41 }
42 }
43 }
44 return 0;
45 }
執行結果:
1 [[email protected] chap02]$ sudo ./select
2 a (此處,從鍵盤輸入a和回車)
3 Data is available from keyboard now
4 Data is available from mouse now (此處單擊滑鼠左鍵)
5 Data is available from mouse now
6 No data within 5 seconds (此處一直沒有任何操作)
結果分析:
16行打開滑鼠對應的檔案描述符
22行清空檔案描述符集rfds
23-24行将rfds中對應鍵盤和滑鼠的位置1
25-26行設定逾時時間
28行調用select同時監控鍵盤和滑鼠是否可讀,并設定逾時時間為5秒
當使用者從鍵盤輸入時,34行結果為真,執行35行列印結果(見執行結果第3行)
當使用者單擊滑鼠時,38行結果為真,執行39行列印結果(見執行結果第4、5行)
當使用者在5秒内沒有任何操作,将導緻28行傳回0,進而執行32行列印結果(見執行結果第6行)
特别說明:select以相同方式對待以非阻塞方式和以阻塞方式打開的檔案描述符。即:被select監控的檔案描述符即使是以非阻塞方式打開的,如果沒有實際資料可讀,select也将阻塞,而不是傳回。