ImageVerifierCode 换一换
你正在下载:

ioctl.docx

[预览]
格式:DOCX , 页数:16 ,大小:22.18KB ,
资源ID:12064253      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/12064253.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(ioctl.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

ioctl.docx

1、ioctl我这里说的ioctl函数是在驱动程序里的,因为我不知道还有没有别的场合用到了ioctl,所以就规定了我们讨论的范围。为什么要写篇文章呢,是因为我前一阵子被ioctl给搞混了,这几天才弄明白它,于是在这里清理一下头脑。ioctl可以向内核传递参数。 一、 什么是ioctl。 ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下: int ioctl(int fd, ind cmd, ); 其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序

2、对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。 第一个参数fd是一个文件描述符,我们这里是建立的一个套接字描述符第二个参数,是在sockios.h中定义的一个32位描述符,我们也可以在这里添加新的类型,用来扩展实际需求。第三个参数,是一个指针,用来指向某些我们实际应用中的参数。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数控制设备的I/O通道。 二、 ioctl的必要性 如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可以

3、在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。 所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。 三、 ioctl如何实现 这是一个很麻烦的问题,我是能省则省。要说清楚它,没有四五千字是不行的,所以我这里是不可能把它说得非常清楚了,不过如果有读者对用户程序怎么和驱动程序联系起来感兴趣的话,可以看我前一

4、阵子写的write的奥秘。读者只要把write换成ioctl,就知道用户程序的ioctl是怎么和驱动程序中的ioctl实现联系在一起的了。 我这里说一个大概思路,因为我觉得Linux设备驱动程序这本书已经说的非常清楚了,但是得化一些时间来看。 在驱动程序中实现的ioctl函数体内,实际上是有一个switchcase结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。 命令码的组织是有一些讲究的,因为我们一定要做到命令和

5、设备是一一对应的,这样才不会将正确的命令发给错误的设备,或者是把错误的命令发给正确的设备,或者是把错误的命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将是非常困难的事情。 所以在Linux核心中是这样定义一个命令码的: _| 设备类型 | 序列号 | 方向 |数据尺寸|-|-|-|-| 8 bit | 8 bit |2 bit |814 bit|-|-|-|-|这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从

6、命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。这些宏我就不在这里解释了,具体的形式请读者察看Linux核心源代码中的和,文件里给除了这些宏完整的定义。这里我只多说一个地方,那就是幻数。 幻数是一个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一个数字是一样的,只是更加利于记忆和理解。就是这样,再没有更复杂的了。 更多的说了也没有,读者还是看一看源代码吧,推荐各位阅读Linux 设备驱动程序所带源代码中的short一例,因为它比较短小,功能比较简单,可以看明白ioctl的功能和细节。 四、 cmd参数如何得出 这里确实

7、要说一说,cmd参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过switchcase结构进行相应的操作。 要透彻理解,只能是通过阅读源代码,我这篇文章实际上只是一个引子。Cmd参数的组织还是比较复杂的,我认为要搞熟它还是得花不少时间的,但是这是值得的,驱动程序中最难的是对中断的理解。 五、 小结 ioctl其实没有什么很难的东西需要理解,关键是理解cmd命令码是怎么在用户程序里生成并在驱动程序里解析的,程序员最主要的工作量在switch

8、case结构中,因为对设备的I/O控制都是通过这一部分的代码实现的。一般的说,,用户空间的IOCTL系统调用如下所示: ioctl(int fd, int command, (char *) argstruct)因为这个调用拥有与网络相关的代码,所以文件描述符号fd就是socket()系统调用所返回的,而command参数可以是/usr/include/linux/sockios.h头文件中的任何一个,这些个命令根据它可以解决的问题所涉及的方面被分为多种的类型.比如:改变路由表(SIOCADDRT, SIOCDELRT)读取或更新ARP/RARP缓存(SIOCDARP, SIOCSRARP)一

9、般的和网络有关的函数(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程序用

10、这些命令去读取IP地址信息,硬件地址信息,广播地址信息,和与网络接口相关的标志.对于这些ioctl,第三个参数是一个IFREQ结构体,这个结构体被定义在/usr/include/linux/if.h头文件中,在一些情况下,新的ioctl命令可能被需要(除了在那个头文件中被定义的之外),比如 WAVELAN无线网卡保持着无线信号强度的信息,这些信西可能要 对用户程序有用.用户程序是怎么访问到这些信息的呢?我们的第一反应就是定义一个新的命令在sockios.h头文件中,比如SIOCGIFWVLNSS,不幸的是,这个命令在其他的网络接口上是根本没有意义的,另外试图在其他接口上用这个名另而并非是在无线

11、网口上用会出现违规访问,我们需要的是定义新特性接口命令的机理。幸运的是,LINUX操作系统为此目的内置了钩子,如果你再看一下那个头文件sockios.h你会注意到每一个设备都有一个预定义的SIOCDEVPRIVATE命令,实现它的任务就全权交给了写这个设备驱动的程序员了.根据常规约定,一个用户程序调用一个特定的ioctl命令如下: ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr)这里ifr是一个ifreq结构体变量,它用一个和这个设备联系的接口名称填充ifr的ifr NAME域,比如,前述的无线网卡接口名称为eth1。不失一般性,一个用户程序将同样要与内

12、核交换命令参数和操作结果,而这些已经通过一个域ifr.ifr_data的填充而做到了,比如,这个网卡的信号强度信息被返回到这个域当中。LINUX源代码已经包含了两个特殊设备de4x5和ewrk3,他们定义和实现了特殊的ioctl命令.,这些设备的源代码在以下的文件中:de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, 他们两个设备都为在用户空间和驱动间交换数据定义了他们自己的私有结构,在ioctl之前,用户程序填充了需要的数据并且将ifr.ifr_data指向这个结构体.我们在两个驱动中走的更远些从而进入代码前,让我们跟踪一下处理ioctl系统调用的若干步骤,所有接口类型的

13、ioctl请求都导致dev_ioctl()被调用,这个ioctl仅仅是个包装,大部分的真实的操作留给了dev_ifsioc().,这个dev_ioctl()要做的唯一一个事情就是检查调用过程是否拥有合适的许可去核发这个命令,然后dev_ifsioc()首先要做的事情之一就是得到和名字域ifr.ifr_name中所对应的设备结构,这在一个很大的switch语块的代码后实现。SIOCDEVPRIVATE命令和SIOCDEVPRIVATE+15的命令参数全部交给了默认操作,这些都是switch的分支语句.这里发生的是,内核检查是否一个设备特殊的ioctl的回调已经在设备结构中被设置,这个回调是保持在

14、设备结构中的一个函数指针。如果回调已经被设置了.内核就会调用它.所以,为了实现一个特殊的ioctl,需要做的就是写一个特殊ioctl的回调,然后让device结构中的do_ioctl域指向它,对于EWK3设备,这个函数叫做ewrk3_ioctl(),对应的设备结构在ewrk3_init()中被初始化,ewrk3_ioctl()的代码清晰的展示了ifr.ifr_data的作用 ,是为了在用户程序和驱动之间交换信息。注意,内存的这个区域有双方向交换数据的作用,例如,在ewrk3驱动代码中,ifr.ifr_data最初的2个字节被用做向驱动传递预想要的动作。同样第五个字节指向的缓冲区用于交换其他的信

15、息。当你浏览ewrk3_ioctl()代码的时候,记住在一个应用中用户空间的指令是无法访问内核空间的,由于这个原因 ,2个特殊的步骤提供给了驱动编写人员.他们是memcpy_tofs()和memcpy_fromfs()。内核里的做法是用memcpy_tofs() 拷贝内核数据到用户空间,类似的memcpy_fromfs()也是这样的,只是他拷贝用户数据到内核空间.。这些程序步骤是由于调用verify_area()而被执行的,目的是确认数据访问不会违法。同样记住printk()的用法是打印调试信息,这个函数和printf()很相象,但是它不能处理浮点数据,printf()函数在内核中是不能被使用

16、的。由printk()产生的输出被转储到了一个目录./usr/adm/messages。 从源码分析发现两个系统下的ifconfig的up命令都是通过调用ioctl的SIOCSIFFLAGS命令来完成这个工作的。我也写了一个测试ioctl()函数的程序来测试,发现在Linux下通过ioctl()的SIOCSIFFLAGS命令来设置网口的IFF_UP标志后,只能够通过IP地址访问远程主机,并不能通过域名访问远程主机。 linux系统ioctl使用示例These were writed and collected by kf701,you can use and modify them but N

17、O WARRANTY. Contact with me : kf_701程序1:检测接口的 inet_addr,netmask,broad_addr程序2:检查接口的物理连接是否正常程序3:更简单一点测试物理连接程序4:调节音量*程序1*#include #include #include #include #include #include #include #include #include #include #include static void usage() printf(usage : ipconfig interface n); exit(0);int main(int arg

18、c,char *argv) struct sockaddr_in *addr; struct ifreq ifr; char *name,*address; int sockfd;if(argc != 2) usage(); else name = argv1;sockfd = socket(AF_INET,SOCK_DGRAM,0); strncpy(ifr.ifr_name,name,IFNAMSIZ-1);if(ioctl(sockfd,SIOCGIFADDR,&ifr) = -1) perror(ioctl error),exit(1); addr = (struct sockaddr

19、_in *)&(ifr.ifr_addr); address = inet_ntoa(addr-sin_addr); printf(inet addr: %s ,address);if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr) = -1) perror(ioctl error),exit(1); addr = (struct sockaddr_in *)&ifr.ifr_broadaddr; address = inet_ntoa(addr-sin_addr); printf(broad addr: %s ,address);if(ioctl(sockfd,SIOCG

20、IFNETMASK,&ifr) = -1) perror(ioctl error),exit(1); addr = (struct sockaddr_in *)&ifr.ifr_addr; address = inet_ntoa(addr-sin_addr); printf(inet mask: %s ,address);printf(n); exit(0);* 程序2*#include #include #include #include #include #include #include #include #include #include typedef unsigned short

21、u16;typedef unsigned int u32;typedef unsigned char u8;#include #include int detect_mii(int skfd, char *ifname) struct ifreq ifr; u16 *data, mii_val; unsigned phy_id;/* Get the vitals from the interface. */ strncpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(skfd, SIOCGMIIPHY, &ifr) 0) fprintf(stderr,

22、 SIOCGMIIPHY on %s failed: %sn, ifname, strerror(errno); (void) close(skfd); return 2; data = (u16 *)(&ifr.ifr_data); phy_id = data0; data1 = 1;if (ioctl(skfd, SIOCGMIIREG, &ifr) 0) fprintf(stderr, SIOCGMIIREG on %s failed: %sn, ifr.ifr_name, strerror(errno); return 2; mii_val = data3;return(mii_val

23、 & 0x0016) = 0x0004) ? 0 : 1);int detect_ethtool(int skfd, char *ifname) struct ifreq ifr; struct ethtool_value edata;memset(&ifr, 0, sizeof(ifr); edata.cmd = ETHTOOL_GLINK;strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1); ifr.ifr_data = (char *) &edata;if (ioctl(skfd, SIOCETHTOOL, &ifr) = -1)

24、printf(ETHTOOL_GLINK failed: %sn, strerror(errno); return 2; return (edata.data ? 0 : 1);int main(int argc, char *argv) int skfd = -1; char *ifname; int retval;if( argv1 ) ifname = argv1; else ifname = eth0;/* Open a socket. */ if ( skfd = socket( AF_INET, SOCK_DGRAM, 0 ) ) 0 ) printf(socket errorn)

25、; exit(-1); retval = detect_ethtool(skfd, ifname);if (retval = 2) retval = detect_mii(skfd, ifname);close(skfd);if (retval = 2) printf(Could not determine statusn);if (retval = 1) printf(Link downn);if (retval = 0) printf(Link upn);return retval;*程序3*#include #include #include #include #include #inc

26、lude #include #define LINKTEST_GLINK 0x0000000astruct linktest_value unsigned int cmd; unsigned int data;staticvoidusage(const char * pname) fprintf(stderr, usage: %s n, pname); fprintf(stderr, returns: n); fprintf(stderr, t 0: link detectedn); fprintf(stderr, t%d: %sn, ENODEV, strerror(ENODEV); fpr

27、intf(stderr, t%d: %sn, ENONET, strerror(ENONET); fprintf(stderr, t%d: %sn, EOPNOTSUPP, strerror(EOPNOTSUPP); exit(EXIT_FAILURE);staticintlinktest(const char * devname) struct ifreq ifr; struct linktest_value edata; int fd;/* setup our control structures. */ memset(&ifr, 0, sizeof(ifr); strcpy(ifr.if

28、r_name, devname);/* open control socket. */ fd=socket(AF_INET, SOCK_DGRAM, 0); if(fd 0 ) return -ECOMM; errno=0; edata.cmd = LINKTEST_GLINK; ifr.ifr_data = (caddr_t)&edata;if(!ioctl(fd, SIOCETHTOOL, &ifr) if(edata.data) fprintf(stdout, link detected on %sn, devname); return 0; else errno=ENONET; perror(linktest); return errno;intmain(int argc, char *argv) if(argc != 2) usage(argv0); return linktest(argv1);*

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1