linux socket-recvfrom系统调用
Posted osnet
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
以上是关于linux socket-recvfrom系统调用的主要内容,如果未能解决你的问题,请参考以下文章