linux网络协议栈分析——ioctl的调用流程

首先从系统调用开始,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;
}

你可能感兴趣的:(linux,socket,struct,cmd,File,网络协议)