唤醒阻塞进程 sock_def_readable(进接收队列唤醒)将数据放入接收队列后,需要唤醒那些因为数据不足而阻塞的进程,这是通过上面的sk->sk_data_ready()回调实现的,对于UDP,该函数就是 sock_def_readable 。
static void sock_def_readable(struct sock *sk, int len){ //先获取读锁 read_lock(&sk->sk_callback_lock); //如果有正在阻塞的进程,唤醒它们 if (sk_has_sleeper(sk))wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN |POLLRDNORM | POLLRDBAND); sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); read_unlock(&sk->sk_callback_lock);} static inline int sk_has_sleeper(struct sock *sk){ /** We need to be sure we are in sync with the* add_wait_queue modifications to the wait queue.** This memory barrier is paired in the sock_poll_wait.*/ smp_mb__after_lock(); //block的进程都阻塞在了sk->sk_sleep等待队列上 return sk->sk_sleep && waitqueue_active(sk->sk_sleep);}
数据包进后备队列 sk_backlog在下半部接收时,如果传输控制块已经被进程锁定,那么会先将数据放入到后备队列中,等进程释放传输控制块时再进行处理,这种设计可以使得软中断能够尽快的结束 。
/* The per-socket spinlock must be held here. *///调用该函数时,要确保已经使用自旋锁sk_lock.slockstatic inline void sk_add_backlog(struct sock *sk, struct sk_buff *skb){ //将skb放入后备队列的末尾 if (!sk->sk_backlog.tail) {sk->sk_backlog.head = sk->sk_backlog.tail = skb; } else {sk->sk_backlog.tail->next = skb;sk->sk_backlog.tail = skb; } skb->next = NULL;}
【linux内核协议栈 UDP之数据报接收过程】
推荐阅读
- 常用开源协议对比
- Linux系统目录结构介绍
- Linux的sz和rz命令
- linux系统上查看载体为实体机还是虚拟机
- Linux上,最常用的十条命令
- Linux内核快速处理路径尽量多用kmem_cache而慎用kmalloc
- Linux和Windows的区别是什么?
- 一身漏洞狂奔24年!WiFi协议被曝重大漏洞,随时成为监控你的工具
- linux权限管理
- 编写Linux下的UDP Client/Server程序