linux socket-recvfrom系统调用

recvfrom库函数

应用程序调用recv或recvfrom库函数接收数据。recv和recvfrom主要区别是是否设置socket地址。

__socketcall ssize_t recvfrom(int, void*, size_t, int, const struct sockaddr*, socklen_t*);
ssize_t recv(int, void*, size_t, int);

ssize_t recv(int socket, void *buf, size_t len, int flags) {
  return recvfrom(socket, buf, len, flags, NULL, 0); //对于recv,指定后两个参数为空
}

armv8系统调用:
ENTRY(recvfrom)
    mov     x8, __NR_recvfrom
    svc     #0

    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno_internal

    ret
END(recvfrom)

recvfrom系统调用

先看数据结构

struct msghdr

struct msghdr为socket层和协议层进行通信的消息形式。
struct iovec存储用户空间传递的buf地址和长度

struct iovec
{
	void __user *iov_base;	/* BSD uses caddr_t (1003.1g requires void *) */
	__kernel_size_t iov_len; /* Must be size_t (1003.1g) */
};

/*
 *	As we do 4.4BSD message passing we use a 4.4BSD message passing
 *	system, not 4.3. Thus msg_accrights(len) are now missing. They
 *	belong in an obscure libc emulation or the bin.
 */
 
struct msghdr {
	void	*	msg_name;	/* Socket name			*/ //包括socket地址等
	int		msg_namelen;	/* Length of name		*/
	struct iovec *	msg_iov;	/* Data blocks			*/  //用户空间buf block,可以是数组
	__kernel_size_t	msg_iovlen;	/* Number of blocks		*/ //用户空间buf block个数,数组长度
	void 	*	msg_control;	/* Per protocol magic (eg BSD file descriptor passing) */ //控制消息
	__kernel_size_t	msg_controllen;	/* Length of cmsg list */
	unsigned int	msg_flags;
};

struct kiocb

每个IO请求都对应一个kiocb结构体,ki_ctx指示是否为同步IO还是异步IO,默认NULL为同步IO
ki_users指示引用数量。tsk存储当前进程。

struct kiocb {
	atomic_t		ki_users;

	struct file		*ki_filp;
	struct kioctx		*ki_ctx;	/* NULL for sync ops */
	kiocb_cancel_fn		*ki_cancel;
	void			(*ki_dtor)(struct kiocb *);

	union {
		void __user		*user;
		struct task_struct	*tsk;
	} ki_obj;

	__u64			ki_user_data;	/* user's data for completion */
	loff_t			ki_pos;

	void			*private;
	/* State that we remember to be able to restart/retry  */
	unsigned short		ki_opcode;
	size_t			ki_nbytes; 	/* copy of iocb->aio_nbytes */
	char 			__user *ki_buf;	/* remaining iocb->aio_buf */
	size_t			ki_left; 	/* remaining bytes */
	struct iovec		ki_inline_vec;	/* inline vector */
 	struct iovec		*ki_iovec;
 	unsigned long		ki_nr_segs;
 	unsigned long		ki_cur_seg;

	struct list_head	ki_list;	/* the aio core uses this
						 * for cancellation */

	/*
	 * If the aio_resfd field of the userspace iocb is not zero,
	 * this is the underlying eventfd context to deliver events to.
	 */
	struct eventfd_ctx	*ki_eventfd;
};


struct sock_iocb

struct sock_iocb为sock 类型io 请求控制块。struct kiocb 中private指向sock_iocb类型。

/* sock_iocb: used to kick off async processing of socket ios */
struct sock_iocb {
	struct list_head	list;

	int			flags;
	int			size;
	struct socket		*sock; //指向socket
	struct sock		*sk;//指向socket中sock
	struct scm_cookie	*scm;
	struct msghdr		*msg, async_msg; //指向msghdr消息
	struct kiocb		*kiocb; 
};

在这里插入图片描述

recvfrom

/*
 *	Receive a frame from the socket and optionally record the address of the
 *	sender. We verify the buffers are writable and if needed move the
 *	sender address from kernel to user space.
 */

SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
		unsigned int, flags, struct sockaddr __user *, addr,
		int __user *, addr_len)
{
	struct socket *sock;
	struct iovec iov;
	struct msghdr msg;
	struct sockaddr_storage address;
	int err, err2;
	int fput_needed;


	//根据文件描述符fd找到对应的socekt结构指针
	sock = sockfd_lookup_light(fd, &err, &fput_needed);

    //构造struct msghdr,用于socket和协议栈层交互
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	msg.msg_iovlen = 1;
	msg.msg_iov = &iov; //指向struct iovec,存储了用户空间buf
	iov.iov_len = size;
	iov.iov_base = ubuf;//存储了用户空间buf
	/* Save some cycles and don't copy the address if not needed */
	msg.msg_name = addr ? (struct sockaddr *)&address : NULL;//用于存储发送端的地址
	/* We assume all kernel code knows the size of sockaddr_storage */
	msg.msg_namelen = 0;
	if (sock->file->f_flags & O_NONBLOCK)
		flags |= MSG_DONTWAIT; //标志是否为阻塞io
	//调用sock接口
	err = sock_recvmsg(sock, &msg, size, flags);

	if (err >= 0 && addr != NULL) { //保存发送端地址到用户空间 地址buf
		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;
}


int sock_recvmsg(struct socket *sock, struct msghdr *msg,
		 size_t size, int flags)
{
	struct kiocb iocb;
	struct sock_iocb siocb;
	int ret;
    //构造一个kiocb ,并且iocb.private存储siocb;用于io请求
	init_sync_kiocb(&iocb, NULL);
	iocb.private = &siocb;
	ret = __sock_recvmsg(&iocb, sock, msg, size, flags);
	if (-EIOCBQUEUED == ret)
		ret = wait_on_sync_kiocb(&iocb);
	return ret;
}

__sock_recvmsg---》__sock_recvmsg_nosec:
static inline int __sock_recvmsg_nosec(struct kiocb *iocb, struct socket *sock,
				       struct msghdr *msg, size_t size, int flags)
{
	struct sock_iocb *si = kiocb_to_siocb(iocb);
    //填充上面构造的sock_iocb 
	si->sock = sock;
	si->scm = NULL;
	si->msg = msg;  //指向了msghdr
	si->size = size;
	si->flags = flags;

    //调用具体socket type的socket接口,比如SOCK_STREAM,SOCK_DGRAM,或者SOCK_RAW等
    //这里io请求控制块从kiocb 转换为了sock_iocb ,也就是从通用socekt层到具体socket type的socket 层面,使用具体的对应iocb
	return sock->ops->recvmsg(iocb, sock, msg, size, flags);
}

实际看SOCK_STREAM,SOCK_DGRAM,或者SOCK_RAW 类型socket的socket管理面recvmsg 接口都为inet_recvmsg

int inet_recvmsg(struct kiocb *iocb, 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);
    
    //调用sock中 sk_prot-》recvmsg,也就是调用socekt类型下,某种具体协议的recvmsg函数,从socekt层面转到协议栈层面
    //比如SOCK_STREAM类型socekt下的tcp,SOCK_DGRAM类型socekt下的udp
	err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT,
				   flags & ~MSG_DONTWAIT, &addr_len);
	if (err >= 0)
		msg->msg_namelen = addr_len;
	return err;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值