RAW socket接收数据过程
1、用户层接收逻辑
bytes = recvfrom(fd, buf, length, NULL, NULL)
使用recvfrom接口,参数与udp类似。
2、系统调用解析
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
unsigned int, flags, struct sockaddr __user *, addr,
int __user *, addr_len)
{
return __sys_recvfrom(fd, ubuf, size, flags, addr, addr_len);
}
2.1、__sys_recvfrom分析
int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags,struct sockaddr __user *addr, int __user *addr_len)
{
err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter);//1
sock = sockfd_lookup_light(fd, &err, &fput_needed);//2
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = addr ? (struct sockaddr *)&address : NULL;//3
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;//4
err = sock_recvmsg(sock, &msg, flags);//5
}
1.将用户态接收buff挂到msg.msg_iter上:
iov->iov_base = buf;
iov->iov_len = len;
iov_iter_init(i, rw, iov, 1, len);
{
i->iov = iov;
}
最终&msg.msg_iter->iov = iov;
2.根据fd查找socket
3.如果传入的addr为NULL,则给msg.msg_name赋NULL。
4.socket默认为阻塞模式,因此不会赋值MSG_DONTWAIT
5.调用sock_recvmsg
2.2、packet_recvmsg分析
sock->ops->recvmsg = packet_recvmsg
int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int flags)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);//1
copied = skb->len;
err = skb_copy_datagram_msg(skb, 0, msg, copied);//2
}
1.接收数据,返回skb
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags,
int noblock, int *err)
{
int off = 0;
return __skb_recv_datagram(sk, &sk->sk_receive_queue,
flags | (noblock ? MSG_DONTWAIT : 0),//1
&off, err);
}
struct sk_buff *__skb_recv_datagram(struct sock *sk,
struct sk_buff_head *sk_queue,
unsigned int flags, int *off, int *err)
{
struct sk_buff *skb, *last;
long timeo;
timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);//2
do {
skb = __skb_try_recv_datagram(sk, sk_queue, flags, off, err,
&last);//3
if (skb)
return skb;
if (*err != -EAGAIN)
break;
} while (timeo &&
!__skb_wait_for_more_packets(sk, sk_queue, err,
&timeo, last));//4
return NULL;
}
1.这里传入的flags = 0
2.返回sk->sk_rcvtimeo, sock创建时,sk->sk_rcvtimeo赋值为极大值,相当于阻塞。
3.接收数据包,读取 sk-> sk_receive_queue 接收队列。
4. __skb_wait_for_more_packets 会令用户进程休眠。
struct sk_buff *__skb_try_recv_datagram(struct sock *sk,
struct sk_buff_head *queue,
unsigned int flags, int *off, int *err,
struct sk_buff **last)
{
struct sk_buff *skb;
spin_lock_irqsave(&queue->lock, cpu_flags);//加锁
skb = __skb_try_recv_from_queue(sk, queue, flags, off,&error,last);
spin_unlock_irqrestore(&queue->lock, cpu_flags);//解锁
}
struct sk_buff *__skb_try_recv_from_queue(struct sock *sk,
struct sk_buff_head *queue,
unsigned int flags,
int *off, int *err,
struct sk_buff **last)
{
struct sk_buff *skb;
skb_queue_walk(queue, skb)//获取接收队列头的next节点,即最早的skb
}
2.3、阻塞等待数据到来
int __skb_wait_for_more_packets(struct sock *sk, struct sk_buff_head *queue, int *err, long *timeo_p, const struct sk_buff *skb)
{
DEFINE_WAIT_FUNC(wait, receiver_wake_function);//1
prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);//2
*timeo_p = schedule_timeout(*timeo_p);//3
}
1.创建一个wait队列,注册回调,并把当前进程current挂到private成员上。
#define DEFINE_WAIT_FUNC(name, function) \
struct wait_queue_entry name = { \
.private = current, \
.func = function, \
.entry = LIST_HEAD_INIT((name).entry), \
}
sk_sleep(sk)返回sk->sk_wq->wait,即sk的等待队列的wait成员(队列头)。
struct socket_wq {
/* Note: wait MUST be first field of socket_wq */
wait_queue_head_t wait;
…
}
然后将新创建的wait挂到wait_queue_head_t 上,当数据就绪时,内核从sk的sk_wait上取下线程的current指针,唤醒线程。
bool
prepare_to_wait_exclusive(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)
{
unsigned long flags;
bool was_empty = false;
wq_entry->flags |= WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&wq_head->lock, flags);
if (list_empty(&wq_entry->entry)) {
was_empty = list_empty(&wq_head->head);
__add_wait_queue_entry_tail(wq_head, wq_entry);
}
set_current_state(state);
spin_unlock_irqrestore(&wq_head->lock, flags);
return was_empty;
}
最后调用*timeo_p = schedule_timeout(*timeo_p)切换线程。
整个流程如下: