#include <unistd.h> //System V
#include <sys/ioctl.h> //BSD and Linux
int ioctl(int d, int request, ...);
- 附加链接:http://www.baike.com/wiki/ioctl
- 作用:操作特殊文件的底层设备参数。特别是,字符特殊文件(如终端)的许多操作字符特性可能由ioctl()请求控制
- ioctl函数时IO操作的杂物箱,通常可以做到其他IO函数不能达到的功能。终端IO是使用ioctl最多的地方
- 注意事项:为了使用这个调用,需要一个打开的文件描述符。open(2)调用通常有一些不想要的副作用,在Linux下可以通过使用O_NONBLOCK标志来避免这些副作用
-
参数:
- 参数1:必须是一个打开的文件描述符
- 参数2:是一个依赖于设备的请求代码。
- ...参数:是指向内存的无类型指针。它在本质上是char *argp(在void *有效之前的日子),并将因此而命名
- 无论参数是in参数还是out参数,以及argu‐ment argp的大小(以字节为单位),ioctl()请求都在其中编码。宏和用于指定ioctl()请求的定义位于文件中
-
返回值:
- 成功:返回0
- 一些ioctl()请求使用返回值作为输出参数,并在成功时返回一个非负的值
- 错误时:返回-1,并适当地设置errno,值如下:
-
EBADF:d不是一个有效的描述符
-
EFAULT:argp引用一个不可访问的内存区域
-
EINVAL:请求或argp无效
-
ENOTTY:d与字符专用设备无关
-
ENOTTY:指定的请求不适用于描述符d引用的对象类型
-
- 在上面的函数原型中,我们只给出了ioctl函数本身所要求的头文件。通常,还要求另外的设备专用头文件。例如,除POSIX.1所说明的基本操作之外,终端IO的ioctl命令都需要头文件<termios.h>
- 每个设备驱动程序可以定义它自己专用的一组ioctl命令,系统则为不同种类的设备提供通用的ioctl命令
四、用ioctl设置终端窗口的大小下图总结了FreeBSD支持的通用的ioctl命令的一些类别:
- 磁带操作使我们可以在磁带上写一个文件结束标志、倒带、越过指定个数的文件或记录等,这些都是read、write、lseek等很难做到的。所以,对这些设备进行操作最容易的方法就是使用ioctl
- 终端窗口大小:大多数UNIX系统都提供了一种跟踪当前终端窗口大小的方法,在窗口大小发生变化时,使内核通知前台进程组
winsize结构体:
- 内核为每个终端和伪终端都维护了一个winsize结构体
struct winsize { unsigned short ws_row; //rows,in characters unsigned short ws_col; //columns,in characters unsigned short ws_xpixel; //horizontal size,pixels(unused) unsigned short ws_ypixel; //vertical size,pixels(unused) };
ioctl对窗口大小的操作
- 用ioctl的TIOCGWINSZ命令可以获取winsize结构体的当前值
- 用ioctl的TIOCSWINSZ命令可以将winsize结构体的新值存储到内核中。如果此新值与存储在内核中的当前值不同,则前台进程组会受到SIGWINCH信号(该信号的系统默认动作为忽略)
- 除了存储winsize结构体的当前值以及在此值改变时产生一个信号之外,内核对winsize结构体不进行任何其他操作。对winsize结构体中的值进行解析完全是应用程序的工作
- 提供这种操作的功能目的是:当窗口大小发生变化时应用程序能够得到通知(如vi编辑器)。应用程序接收此信号后,可以获取窗口大小的新值,然后重绘屏幕
五、ioctl与struct ifreq、 struct ifconf结构体的使用演示案例:
- 下面程序打印当前窗口大小,然后休眠。每个窗口大小改变时,程序捕获到SIGGWINCH信号,然后打印新的窗口大小(我们必须用一个信号终止此程序)
#ifndef TIOCGWINSZ #include <sys/ioctl.h> #endif //打印窗口大小信息 static void pr_winsize(int fd) { struct winsize size; if(ioctl(fd,TIOCGWINSZ,(char *)&size)<0) { perror("TIOCGWINSZ error"); exit(EXIT_FAILURE); } printf("%d rows,%d columns\n",size.ws_row,size.ws_col); } static void sig_winch(int signo) { printf("SIGWINCH received\n"); pr_winsize(STDIN_FILENO); } int main () { if(isatty(STDIN_FILENO)==0) exit(1); //捕获信号,当每次窗口大小改变时,打印窗口大小信息 if(signal(SIGWINCH,sig_winch)==SIG_ERR) { perror("signal error"); exit(EXIT_FAILURE); } pr_winsize(STDIN_FILENO); //循环休眠,等待接受信号 for(;;) pause(); return 0; }
演示结果
- 在一个带窗口终端的系统上运行此程序,当每次改变窗口大小时,就会打印信息。可以按中断键终止程序