sock数据包接收——阻塞方式

内核版本:4.1.15

开发板:正点原子IMX6ULL

参考资料:深入理解Linux网络——张彦飞

目录

1 等待接收消息

2. 软中断模块


        在同步阻塞IO模型中,先是用户进程发起创建socket的指令,然后切换到内核态完成了内核态的初始化,接下来,Linux在数据包的接收上,是硬中断和ksoftirqd线程在进程处理。当ksoftirq线程处理完以后,在通知相关的用户进程。

1 等待接收消息

        用户态使用recv函数接受消息,当用户态执行recv函数后,进入系统调用后,用户态进入内核态,执行一些列的内核协议层函数,然后到socket对象的接收队列中查看是否有数据,没有的话就把自己添加到socket对应的等待队列里,然后让出cpu使用权,操作系统会选择下一个就绪的进程来执行。

// net/socket.c
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
		unsigned int, flags, struct sockaddr __user *, addr,
		int __user *, addr_len)
{
	/* 定义sock,表示网络套接字,后续通过文件描述符fd查找到对应的套接字后,将其赋值给sock */
	struct socket *sock;
	/* 用于描述一段内存区域,这里用于表示接收数据的缓冲区信息 */
	struct iovec iov;
	/* msghdr结构体是消息头结构体,用于存储与消息相关的各种信息,如消息的
	控制信息,数据缓冲区 目的地址等 */
	struct msghdr msg;
	/* sockaddr_storage结构体是一个通用的套接字地址存储结构体,可以容纳
	不同类型的套接字地址结构,用于存储发送方的地址信息 */
	struct sockaddr_storage address;
	/* 存储函数执行过程中的错误码 */
	int err, err2;
	/* 用于表示是否需要减少文件引用计数 */
	int fput_needed;

	/* 将用空间缓冲区ubuf导入到内核空间的iov中 */
	err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter);
	if (unlikely(err))
		return err;

	/* 根据传入的fd找到socket对象,找到之后赋值给sock */
	sock = sockfd_lookup_light(fd, &err, &fput_needed);
	if (!sock)
		goto out;

	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	/* Save some cycles and don't copy the address if not needed */
	/* 将address的地址赋值给msg.msg_name,以便在接收数据时获取发送方的地址
	信息 */
	msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
	/* We assume all kernel code knows the size of sockaddr_storage */
	msg.msg_namelen = 0;
	/* 检查套接字文件的标志f_flags是否设置了O_NONBLOCK,非阻塞标志。
	如果设置了该标志,则将flags标志位或上的MSG_DONTWAIT,表示此次接收
	操作将以非阻塞方式进行 */
	if (sock->file->f_flags & O_NONBLOCK)
		flags |= MSG_DONTWAIT;

	/* 通过sock_recvmsg函数从套接字sock接收数据和消息,并将其存储到msg结构体
	中,该函数接受套接字sock、消息头结构体指针msg,数据缓存区的迭代计数以及
	flag作为参数  */
	err = sock_recvmsg(sock, &msg, iov_iter_count(&msg.msg_iter), flags);

	if (err >= 0 && addr != NULL) 
	{
		/* 如果数据接收成功,即err>=0,表示需要将发送方的地址信息复制到
		用户空间, */
		err2 = move_addr_to_user(&address,
					 msg.msg_namelen, addr, addr_len);
		if (err2 < 0)
			err = err2;
	}
	/* 用来决定是否减少套接字相关文件的引用计数 */
	fput_light(sock->file, fput_needed);
out:
	return err;
}

        接下来sock_recvmsg会调用sock_recvmsg_nosec函数,其中sock->ops->recvmsg指针是在inet_creat函数中注册的。


// net/socket.c
static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
				     size_t size, int flags)
{
    // inet_recvmsg
	return sock->ops->recvmsg(sock, msg, size, flags);
}

// net/ipv4/af_inet.c
int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
		 int flags)
{
	struct sock *sk = sock->sk;
	int addr_len = 0;
	int err;

	sock_rps_record_flow(sk);

	err = sk->sk_prot->recvmsg(sk, msg, size, flags & MSG_DONTWAIT,
				   flags & ~MSG_DONTWAIT, &addr_len);
	if (err >= 0)
		msg->msg_namelen = addr_len;
	return err;
}

        可以看的sock->ops指向的是inetsw_array数组中的inet_stream_ops指针,而sk->sk_port指针指向的是tcp_port指针。

// inetsw_array是传输层协议
// static struct inet_protosw inetsw_array[] =
// {
// 	{
/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值