青海正平路桥公司人员:ioctl( )函数详解
来源:百度文库 编辑:偶看新闻 时间:2024/04/28 22:13:50
(一) ioctl( )系统调用的实现
ioctl( )系统调用提供了一个通用命令接口,原型为 int ioctl(int fd, unsigned long com, caddr_t data) 。其中fd是一个设备描述符或者是一个网络连接(即套接字描述sockfd)。本文针对的是套接字描述符. 首先用户进程通过socket(AF_xxx, SOCK_xxx, flag)函数创建一个TCP或者UDP套接字sockfd, 然后再调用ioctl( )函数发送io命令.而从src/sys/kern/sockio.c中我们发现基于socket的ioctl( )函数实际上是调用了bsd_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD cmd, CYG_ADDRWORD data):
// Table entrys
NSTAB_ENTRY( bsd_nste, 0,
"bsd_tcpip",
"",
0,
bsd_init,
bsd_socket);
struct cyg_sock_ops bsd_sockops =
{
bsd_bind,
bsd_connect,
bsd_accept,
bsd_listen,
bsd_getname,
bsd_shutdown,
bsd_getsockopt,
bsd_setsockopt,
bsd_sendmsg,
bsd_recvmsg
};
cyg_fileops bsd_sock_fileops =
{
bsd_read,
bsd_write,
bsd_lseek,
bsd_ioctl,
bsd_select,
bsd_fsync,
bsd_close,
bsd_fstat,
bsd_getinfo,
bsd_setinfo
};
下面是bsd_ioctl( )函数的具体实现:
static int
bsd_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD cmd, CYG_ADDRWORD data)
{
struct socket *so = (struct socket *)fp->f_data;
void *p = 0;
switch (cmd) {
case FIONBIO:
if (*(int *)data)
so->so_state |= SS_NBIO;
else
so->so_state &= ~SS_NBIO;
return (0);
case FIOASYNC:
if (*(int *)data) {
so->so_state |= SS_ASYNC;
so->so_rcv.sb_flags |= SB_ASYNC;
so->so_snd.sb_flags |= SB_ASYNC;
} else {
so->so_state &= ~SS_ASYNC;
so->so_rcv.sb_flags &= ~SB_ASYNC;
so->so_snd.sb_flags &= ~SB_ASYNC;
}
return (0);
case FIONREAD:
*(int *)data = so->so_rcv.sb_cc;
return (0);
case SIOCATMARK:
*(int *)data = (so->so_state&SS_RCVATMARK) != 0;
return (0);
}
/*
* Interface/routing/protocol specific ioctls:
* interface and routing ioctls should have a
* different entry since a socket's unnecessary
*/
if (IOCGROUP(cmd) == 'i')
return (ifioctl(so, (u_long)cmd, (caddr_t)data, p));
if (IOCGROUP(cmd) == 'r')
return (rtioctl((u_long)cmd, (caddr_t)data, p));
return ((*so->so_proto->pr_usrreqs->pru_control)(so, cmd, (caddr_t)data, 0, 0));
}
从以上标注过的语句里我们发现:1)如果cmd属于'i'组(与接口相关的io控制命令),则调用ifioctl( )函数;2)如果cmd属于'r'组(与路由相关的io控制命令),则调用rtioctl( )函数;3)如果cmd属于's'组(属于无接口的ioctl,与进程相关的文件描述符的命令,如SIOCSPGRP,SIOCGPGRP,FIONBIO,FIOASYNC等),则调用(*so->so_proto->pr_usrreqs->pru_control)(so, cmd, (caddr_t)data, 0, 0)函数完成相关操作/*该函数放在后面说明*/.
--------------------------------------------------------------------------------
(二)ioctl命令的层次关系
与ip地址无关的io命令(针对ifnet{}, ifaddr{}结构) 与ip地址结构相关的io命令(针对in_ifaddr{}结构)
|_________________________________|
|
涉及到底层硬件设备(如以太网接口,slip接口等)的io命令
由此,我们可以把ioctl命令分为一下4类:
1.与ip地址无关,针对ifnet{}, ifaddr{}结构,且不需要底层硬件设备支持的操作(即不涉及对改底层硬件设备的查询或设置)。
SIOCCGIFCONF,
SIOCGIFFLAGS, SIOCGIFMETRIC, SIOCGIFMTU, SIOCGIFPHYS
SIOCSIFMETRIC, SIOCSIFMTU, SIOCSIFPHYS
注:针对这类命令,直接在ifioctl( )函数中进行处理.
2.与ip地址相关,针对in_ifaddr{}结构,且不需要底层硬件设备支持的操作(不涉及对底层硬件设备的查询或设置)。
SIOCGIFADDR, SIOCGIFBRDADDR, SIOCGIFNETMASK, SIOCGIFDSTADDR,
SIOCSIFBRDADDR, SIOCSIFNETMASK,
注:针对这类命令,ifioctl( )调用了(*so->so_proto->pr_usrreqs->pru_control)(so, cmd, (caddr_t)data, ifp, p),这类命令是属于有接口的ioctl,将所有相关的数据传给与请求指定的接口相关联的协议的用户请求函数的控制函数,通过src/sys/netinet/tcp_usrreq.c和src/sys/netinet/udp_usrreq.c里面的代码,我们发现,对于TCP和UDP插口,该函数实际上调用了in_control( ):
struct pr_usrreqs tcp_usrreqs = {
tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind,
tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach,
tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd,
tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown,
in_setsockaddr, sosend, soreceive, sopoll
};
struct pr_usrreqs udp_usrreqs = {
udp_abort, pru_accept_notsupp, udp_attach, udp_bind, udp_connect,
pru_connect2_notsupp, in_control, udp_detach, udp_disconnect,
pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
pru_rcvoob_notsupp, udp_send, pru_sense_null, udp_shutdown,
in_setsockaddr, sosend, soreceive, sopoll
};
第2类的ioctl命令由于并不涉及到对底层硬件设备的配置或查询,故对它们的处理直接放在in_control( )中完成.
3.与ip地址无关,针对ifnet{}, ifaddr{}结构,且需要底层硬件设备支持的操作(需要修改底层硬件设备的配置)。
SIOCGIFFLAGS, SIOCADDMULTI, SIOCDELMULTI
注:对于这类ioctl命令,由于涉及到了底层硬件设备,ifioctl( )在完成对通用接口的配置后,通过调用ifp->if_ioctl( )函数对底层硬件设备进行配置.对于以太网接口,ifp->if_ioctl = eth_drv_ioctl/*由于ecos针对所有的以太网络设备做了一层抽象,我们称之为通用io层,所以此处调用的是eth_drv_ioctl()(注:由eth_drv_ioctl()去调用 at91sam9260_eth_ioctl() ), 而不是直接去调用at91sam9260_eth_ioctl()*/;对于slip接口,ifp->if_ioctl = slioctl等等.
4.与ip地址相关,针对in_ifaddr{}结构,且需要底层硬件设备的支持的操作(需要修改底层硬件设备的配置)
SIOCSIFADDR, SIOCAIFADDR , SIOCDIFADDR
SIOCSIFDSTADDR
注:针对这类的ioctl命令,当in_control()完成了对结构in_ifaddr的更新后,同时要调用ifp->if_ioctl( )函数对底层硬件设备进行配置.
5.直接针对底层硬件设备的操作(查询,设置等)
SIOCGIFHWADDR, SIOCGIFSTATS
SIOCSIFHWADDR, SIOCGIFSTATSUD
注:由于此类命令只涉及对底层硬件设备的配置,不改变该接口通用地址结构的成员,故ifioctl( )直接调用了ifp->if_ioctl()函数进行处理.
--------------------------------------------------------------------------------
(三)几个ioctl命令的处理
1.SIOCCGIFCONF //查询所有接口当前的配置信息
ifioctl()并不在对该命令进行直接处理,而是通过调用函数ifconf(int cmd,caddr_t data)来获取当前所有接口的配置信息.
2.SIOCSIFADDR
我们观察到对SIOCSIFADDR的具体处理并没有放在in_control()函数中,而是通过调用in_ifinit( )函数来实现.函数in_ifinit(struct ifnet * ifp, struct in_ifaddr *ia, struct sockaddr_in *sin, int scurb)完成了一下几个操作:
(a)将用户进程通过ifreq结构传递下来的ip地址(由指针sin指向)复制到指定的in_ifaddr{}接口结构(由struct in_ifaddr * ia指向)中,并调用ifp->if_ioctl()通知硬件,其中eth_drv_ioctl()又调用了if_ethersubr.c中的ether_ioctl()函数来完成ifp->if_init()和arp_ifinit()的操作;
(b)如果是以太网接口,则必须为通用地址结构选择链路层路由函数.如果最后一个参数scrub==1,则调用in_ifscrub()找到并废除任何基于老地址的路由;
(c)设置该接口的ia_netmask,ia_subnetmask及ia_sockmask字段;
(d)复制ifp所指向的接口度量值到通用地址结构,并根据ifp所指向的接口的类型,设置ia所指向的通用结构中的接口标志ia_flag.
(e)调用rtinit()函数为该接口安装路由
(f)如果ifp指向的接口有多播能力,调用in_addrmulti()函数将该接口加入多播组。
3.SIOCSIFADDR, SIOCSIFNETMASK, SIOCSIFDSTADDR
这三个命令均须经由in_control()函数.并且要求所调用的插口是由一个超级用户进程创建的(通过检查sockfd->so_state中的SS_PRIV标志).
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cycuest/archive/2007/04/27/1587366.aspx