以上的代码看似体现了UDP的accept逻辑 , 但是却不是!它真实地体现了UDP是不可能实现accept的 , 除非你使用类似conntrack的附加因素!我们还是尊重UDP的原本的意义吧 , 如果你非要想为UDP添加一个第四层的conntrack逻辑 , 何必不直接使用TCP呢?也许你会回答 , TCP太低效 , 太复杂 , 哦 , 是的 , 你不喜欢TCP , 是的 , 我也不喜欢 , 但是你可以实现一个用户态的逻辑而不要纠结于第四层 , 毕竟UDP本身就是无连接的 。见过有谁为IP建立过连接机制吗?是的 , 太多了 , ip_conntrack , MPLS , 有状态Firewall...不一而足,,,这到底是协议设计之初的缺陷呢 , 还是人们得寸进尺的证明?如果你想做点什么 , 以上都是例子 , 另外 , 还有更好的 , 那就是DTLS和OpenVPN!
6.问题2:两个进程带PEEK以及不带PEEK同时recv一个socket-排他唤醒问题说实话 , 之所以写下这个失败的UDP accept模型并不是为了想讲UDP怎么怎么地 , 而是想说一下Linux的排他唤醒问题 , 甚至更往前的 , 也就是UDP多进程处理的方案c的提出 , 即所有进程处理所有的socket , 也是想说一下这个排他唤醒的问题 。真正和UDP相关的问题 , 是本小节的子问题 。
所谓的排他唤醒机制 , 简单的说就是:如果多个进程都睡眠在等待一个事件上 , 当该事件发生的时候 , 只能唤醒其中一个 。socket在阻塞recv的时候 , 如果没有数据 , 就会睡眠等待 , 此时如果有数据来 , 会唤醒socket关联的进程 , 该唤醒就是排他唤醒 , 也就是说 , 如果有两个进程都在等待同一个socket上进行阻塞recv , 有数据来的时候 , 只有一个进程会得到数据并返回 , 另一个进程继续睡眠等待 。具体可以参见内核函数__wake_up_common:
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, int wake_flags, void *key){wait_queue_t *curr, *next;list_for_each_entry_safe(curr, next, &q->task_list, task_list) {unsigned flags = curr->flags;if (curr->func(curr, mode, wake_flags, key) &&(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)break;}}
知道了这一点后 , 我们来分析一下UDP accept模型中的一段代码 , 在主进程中:
while (1) {pollif (POLLIN) {recvfrom(with PEEK)}}
在子进程中:
while (1) {recvfrom(...)}
带有PEEK标志的recvfrom是不会把数据从接收缓冲去移走的 , 我们假设数据到来 , 会有唤醒动作 , 如果主进程poll时排在子进程recvfrom之前睡眠 , 那么会先唤醒主进程 , poll返回 , 由于poll被唤醒不是排他的 , 所以子进程接着也会被唤醒 , 而此时主进程会去带有PEEK的读数据 , 但是子进程赶在前面把数据读走了 , recvfrom返回 , 此时主进程的recvfrom扑了个空 , 进而主进程的recvfrom阻塞 , 等待数据 , 此时子进程又一次调用recvfrom , 到此为止 , 两个进程都阻塞在recvfrom上睡眠 , 且主进程排在前面(它先睡眠) , 此时又来了一个数据 , 主进程被排他唤醒 , 子进程继续睡眠等待 , 主进程的recvfrom返回 , 由于带有PEEK标志 , 数据继续留在接收队列里面 , 下一轮的poll直接返回 , 进而继续调用PEEK版的recvfrom也会直接返回 , 如果再也没有新的数据或者信号到来的话 , 子进程将不会被唤醒 , 它将永远也得不到那个一直被主进程PEEK的数据...这就要求 , 子进程必须要用poll+recvfrom , 因为poll保证真正有数据的时候才会返回 , 让进程继而去recvfrom , 而主进程只是PEEK数据 , 所以就不会出现上述死循环 , 另外 , 本人觉得 , 带有PEEK标志的recv系列函数在被唤醒的时候 , 不应该是排他唤醒的!
是的 , 是的 , PEEK唤醒不应该是排他的 , 因为之所以recv是排他的 , 是为了避免“承诺给一个进程”的数据被其他进程取走了 , 这里的关键词是“取走了” , 然而PEEK版的recv是不可能取走任何东西的!但是 , 不管怎样 , 我不想改内核代码 , 何况我又不会真的去用这个UDP accept , 因此也就用不到PEEK , 除了PEEK方式的recv , 其他真正的recv是有必要是排他的 , 否则到底谁执行数据处理呢?鉴于此 , UDP多处理方案c被彻底否决 。
推荐阅读
- 用 Python 开发一个 「聊天室」
- Linux两种处理模式reactor模式proactor模式
- 彩农茶友天下,彩农茶友天下
- 翡翠|单调的翡翠无事牌,为何会受到这么多人的喜爱?我帮你分析一下
- 右眼下眼皮痉挛
- 上眼皮过敏红肿痒
- OPPO|高通骁龙888旗舰芯下放!OPPO K10 Pro明天预售
- 浏览器到底哪个好用?浅谈各大浏览器使用心得
- Windows AD域下批量自动配置客户端有线网络认证功能
- 威武雄壮万贵妃万贞儿死后下 万贞儿万贵妃