http://blog.csdn.net/cbmsft/article/details/7214639
首先从系统调用开始,ioctl的系统调用在fs/ioctl.c中:
- SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
- {
- ……
- error = do_vfs_ioctl(filp, fd, cmd, arg);
- ……
- }
继续:
- /*
- * When you add any new common ioctls to the switches above and below
- * please update compat_sys_ioctl() too.
- *
- * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d.
- * It's just a simple helper for sys_ioctl and compat_sys_ioctl.
- */
- int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
- unsigned long arg)
- {
- ……
- default:
- if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))//正规文件则调用文件系统的接口
- error = file_ioctl(filp, cmd, arg);
- else
- error = vfs_ioctl(filp, cmd, arg);//非正规文件的调用接口
- break;
- }
- return error;
- }
继续:
- static long vfs_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
- {
- ……
- if (filp->f_op->unlocked_ioctl) {
- error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
- if (error == -ENOIOCTLCMD)
- error = -EINVAL;
- goto out;
- } else if (filp->f_op->ioctl) {
- lock_kernel();
- error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,
- filp, cmd, arg);
- unlock_kernel();
- }
- ……
- }
那么网络文件系统的f_op是如何赋值的?再看一遍socket的创建过程:
- SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
- {
- ……
- retval = sock_create(family, type, protocol, &sock);
- retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
- ……
- }
顺着sock_map_fd继续,调用了sock_attach_fd,进而调用了init_file,传递的参数为:socket_file_ops,该参数赋值给file结构:
- file->f_op = fop;
另外,sock_attach_fd还做了另外的一个操作:
- file->private_data = sock;
socket_file_ops的内容:
- static const struct file_operations socket_file_ops = {
- ……
- .unlocked_ioctl = sock_ioctl,
- ……
- };
到此尚未结束,sock_ioctl并没有完成想要的操作,而是:
- static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
- {
- struct socket *sock;
- struct sock *sk;
- void __user *argp = (void __user *)arg;
- int pid, err;
- struct net *net;
- sock = file->private_data;
- sk = sock->sk;
- net = sock_net(sk);
- ……
- default:
- err = sock->ops->ioctl(sock, cmd, arg);
- /*
- * If this ioctl is unknown try to hand it down
- * to the NIC driver.
- */
- if (err == -ENOIOCTLCMD)
- err = dev_ioctl(net, cmd, argp);
- break;
- ……
- }
那么ops是从哪里获得的呢:
是从socket在创建inet_create函数中,遍历inetsw链表,获得协议结构,保存在:sock->ops = answer->ops;
真正ops是协议初始化inet_init函数调用inet_register_protosw,把全局数组inetsw_array初始化到inetsw链表中的:
fs_initcall(inet_init);
且看inetsw_array全局数组:
- static struct inet_protosw inetsw_array[] =
- {
- {
- .type = SOCK_STREAM,
- .protocol = IPPROTO_TCP,
- .prot = &tcp_prot,
- .ops = &inet_stream_ops,
- .capability = -1,
- .no_check = 0,
- .flags = INET_PROTOSW_PERMANENT |
- INET_PROTOSW_ICSK,
- },
- {
- .type = SOCK_DGRAM,
- .protocol = IPPROTO_UDP,
- .prot = &udp_prot,
- .ops = &inet_dgram_ops,
- .capability = -1,
- .no_check = UDP_CSUM_DEFAULT,
- .flags = INET_PROTOSW_PERMANENT,
- },
- {
- .type = SOCK_RAW,
- .protocol = IPPROTO_IP, /* wild card */
- .prot = &raw_prot,
- .ops = &inet_sockraw_ops,
- .capability = CAP_NET_RAW,
- .no_check = UDP_CSUM_DEFAULT,
- .flags = INET_PROTOSW_REUSE,
- }
- };
这就到了具体协议的ioctl中了:
- const struct proto_ops inet_stream_ops = {
- ……
- .ioctl = inet_ioctl,
- ……
- };
哇,这个函数才是我们关注的重点:
- int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
- {
- struct sock *sk = sock->sk;
- int err = 0;
- struct net *net = sock_net(sk);
- switch (cmd) {
- case SIOCGSTAMP:
- err = sock_get_timestamp(sk, (struct timeval __user *)arg);
- break;
- case SIOCGSTAMPNS:
- err = sock_get_timestampns(sk, (struct timespec __user *)arg);
- break;
- case SIOCADDRT:
- case SIOCDELRT:
- case SIOCRTMSG:
- err = ip_rt_ioctl(net, cmd, (void __user *)arg);
- break;
- case SIOCDARP:
- case SIOCGARP:
- case SIOCSARP:
- err = arp_ioctl(net, cmd, (void __user *)arg);
- break;
- case SIOCGIFADDR:
- case SIOCSIFADDR:
- case SIOCGIFBRDADDR:
- case SIOCSIFBRDADDR:
- case SIOCGIFNETMASK:
- case SIOCSIFNETMASK:
- case SIOCGIFDSTADDR:
- case SIOCSIFDSTADDR:
- case SIOCSIFPFLAGS:
- case SIOCGIFPFLAGS:
- case SIOCSIFFLAGS:
- err = devinet_ioctl(net, cmd, (void __user *)arg);
- break;
- default:
- if (sk->sk_prot->ioctl)
- err = sk->sk_prot->ioctl(sk, cmd, arg);
- else
- err = -ENOIOCTLCMD;
- break;
- }
- return err;
- }