本系列文章节选自本人所著《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 /*
21 sleep(5);
22 // ioctl(fd, CDROM_LOCKDOOR, 1);
23 if (!ioctl(fd, CDROMCLOSETRAY, NULL)) {
24 printf("close cdromsucceed\n");
25 } else {
26 printf("close cdromfailed\n");
27 }
28 */
29 return 0;
30 }
运行结果:
[dennis@localhost chap02]$ sudo./ioctl
eject cdrom succeed
代码分析:
1行包含的头文件linux/cdrom.h中,定义了光驱这种设备所支持的操作的宏
8行调用open打开光驱,得到对应光驱的文件描述符。由于open光驱时,可能光驱中没有光盘,因此需要使用O_NONBLOCK选项,否则open会失败
15行调用ioctl,向光驱发出CDROMEJECT命令(弹出光盘命令)。用户可看到光盘被成功弹出
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 [dennis@localhost 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也将阻塞,而不是返回。