一般的说,,用户空间的IOCTL系统调用如下所示: ioctl(int fd, int command, (char *) argstruct)因为这个调用拥有与网络相关的代码,所以文件描述符号fd就是socket()系统调用所返回的,而command参数可以是/usr/include/linux/sockios.h头文件中的任何一个,这些个命令根据它可以解决的问题所涉及的方面被分为多种的类型.
|
比如:
改变路由表(SIOCADDRT, SIOCDELRT)
读取或更新ARP/RARP缓存(SIOCDARP, SIOCSRARP)
一般的和网络有关的函数(SIOCGIFNAME, SIOCSIFADDR等等)
Goodies目录中包含了很多展示ioctl用法的示例程序,看这些程序的时候,注意根据ioctl的命令类型来学习具体的调用参数结构,比如:和路由表相关的IOCTL用RTENTRY结构, rtentry结构是被定义在/usr/include/linux/route.h文件中的,再一个和ARP相关的ioctl调用用到的arpreq结构被定义在/usr/include/linux/if_arp.h文件之中.网络接口相关的ioctl命令最具有代表性的特征为都是以S或G开头,其实就是设置或得到数据, getifinfo.c程序用这些命令去读取IP地址信息,硬件地址信息,广播地址信息,和与网络接口相关的标志.对于这些ioctl,第三个参数是一个IFREQ结构体,这个结构体被定义在/usr/include/linux/if.h头文件中,在一些情况下,新的ioctl命令可能被需要(除了在那个头文件中被定义的之外),比如 WAVELAN无线网卡保持着无线信号强度的信息,这些信西可能要 对用户程序有用.用户程序是怎么访问到这些信息的呢?我们的第一反应就是定义一个新的命令在sockios.h头文件中,比如SIOCGIFWVLNSS,不幸的是,这个命令在其他的网络接口上是根本没有意义的,另外试图在其他接口上用这个名另而并非是在无线网口上用会出现违规访问,我们需要的是定义新特性接口命令的机理。幸运的是,LINUX操作系统为此目的内置了钩子,如果你再看一下那个头文件sockios.h你会注意到每一个设备都有一个预定义的SIOCDEVPRIVATE命令,实现它的任务就全权交给了写这个设备驱动的程序员了.根据常规约定,一个用户程序调用一个特定的ioctl命令如下: ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr)这里ifr是一个ifreq结构体变量,它用一个和这个设备联系的接口名称填充ifr的ifr NAME域,比如,前述的无线网卡接口名称为eth1。
不失一般性,一个用户程序将同样要与内核交换命令参数和操作结果,而这些已经通过一个域ifr.ifr_data的填充而做到了,比如,这个网卡的信号强度信息被返回到这个域当中。LINUX源代码已经包含了两个特殊设备de4x5和ewrk3,他们定义和实现了特殊的ioctl命令.,这些设备的源代码在以下的文件中:de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, 他们两个设备都为在用户空间和驱动间交换数据定义了他们自己的私有结构,在ioctl之前,用户程序填充了需要的数据并且将ifr.ifr_data指向这个结构体.
我们在两个驱动中走的更远些从而进入代码前,让我们跟踪一下处理ioctl系统调用的若干步骤,,所有接口类型的ioctl请求都导致dev_ioctl()被调用,这个ioctl仅仅是个包装,大部分的真实的操作留给了dev_ifsioc().,这个dev_ioctl()要做的唯一一个事情就是检查调用过程是否拥有合适的许可去核发这个命令,然后dev_ifsioc()首先要做的事情之一就是得到和名字域ifr.ifr_name中所对应的设备结构,这在一个很大的switch语块的代码后实现。
SIOCDEVPRIVATE命令和SIOCDEVPRIVATE+15的命令参数全部交给了默认操作,这些都是switch的分支语句.这里发生的是,内核检查是否一个设备特殊的ioctl的回调已经在设备结构中被设置,这个回调是保持在设备结构中的一个函数指针。如果回调已经被设置了.内核就会调用它.
所以,为了实现一个特殊的ioctl,需要做的就是写一个特殊ioctl的回调,然后让device结构中的do_ioctl域指向它,对于EWK3设备,这个函数叫做ewrk3_ioctl(),对应的设备结构在ewrk3_init()中被初始化,ewrk3_ioctl()的代码清晰的展示了ifr.ifr_data的作用 ,是为了在用户程序和驱动之间交换信息。注意,内存的这个区域有双方向交换数据的作用,例如,在ewrk3驱动代码中,ifr.ifr_data最初的2个字节被用做向驱动传递预想要的动作。同样第五个字节指向的缓冲区用于交换其他的信息。
当你浏览ewrk3_ioctl()代码的时候,记住在一个应用中用户空间的指令是无法访问内核空间的,由于这个原因 ,2个特殊的步骤提供给了驱动编写人员.他们是memcpy_tofs()和memcpy_fromfs()。内核里的做法是用memcpy_tofs() 拷贝内核数据到用户空间,类似的memcpy_fromfs()也是这样的,只是他拷贝用户数据到内核空间.。这些程序步骤是由于调用verify_area()而被执行的,目的是确认数据访问不会违法。同样记住printk()的用法是打印调试信息,这个函数和printf()很相象,但是它不能处理浮点数据,printf()函数在内核中是不能被使用的。由printk()产生的输出被转储到了一个目录./usr/adm/messages。