百科问答小站 logo
百科问答小站 font logo



Unix网络编程里的阻塞是在操作系统的内核态创建一个线程来死循环吗? 第1页

  

user avatar   yao-dong-27 网友的相关建议: 
      

举个通俗易懂的例子吧

你去餐馆吃饭,你作为顾客相当于应用程序,餐馆厨房相当于系统内核,服务员相当于内核和应用层之间的接口。

你点好菜写下来,交给服务员,相当于调用内核接口,这时你就阻塞了,一直等待上菜。

服务员把你点的菜交给厨房,相当于给了内核一个请求,这个订单进入了内核的任务队列。服务员没有一直等待这个订单,他就去招待其他客人了。

如果有空闲的厨师,他就会接受一个订单开始做菜,相当于响应请求。

厨师做好了一个菜,就召唤服务员上菜,如果某个服务员有空就会去上菜,相当于请求完成通知。

顾客得到一个菜,一次系统调用完成。

你看,这个过程里,只有顾客是一只在等待的,服务员和厨师一直都在工作,没有死循环。当然聪明的顾客其实也没有死等,他会去刷手机,等服务员上菜,他才放下手机开始吃。

现实世界里都不会有真的死循环。


user avatar   haozhi-yang-41 网友的相关建议: 
      

不搞长篇大论, 简要说一下:

首先:题主没弄清楚“中断”是个什么东西。中断(无论软硬),都是由外部主动发起,进而触发内部某些工作流程的。具体到你这个问题,基本流程就是由网卡接收到数据之后,通过DMA写数据到内存中,然后触发中断通知CPU,CPU再根据中断向量表执行对应的程序,接下来就是kernel代码接管并执行相关操作了(这就是问题中各个“感知”的基础过程)。

然后,一般来说,kernel里面是不存在进程/线程这种概念的。这两概念本身就是os封装/虚拟出来给上层应用的,kernel是跳脱于这个层次之外的。类似的,阻塞/非阻塞之类的概念也是由kernel/lib等提供的。

最后,本质上kernel就是一组庞大的围绕着刚才说的中断向量表工作的程序,和大多数常见的应用程序的工作模式有本质的区别。


user avatar   phantompoc 网友的相关建议: 
      

充分说明了为什么完整的计算机基础知识是必要的,以及为什么有那么多人难以寸进

因为没有计算机基础知识,所以才有这种想法

以下是操作系统关于这方面会做的事情


网络编程通常要开启一个socket

socket是一个操作系统的资源,受系统管理

根据绑定的地址和端口,操作系统维护一个socket和某个网卡之间的通信

当调用socket接收消息时,执行系统调用,系统处理socket和网卡的通信

系统检查对应网卡数据,有数据时交付给socket,进入下一次调度

没有数据时,即可分配资源不足,系统将挂起socket对应的进程,即阻塞

系统不会做死循环

当网卡接收到消息时,向产生中断信号,在下一个时钟周期,操作系统进行中断处理

当来自网卡的消息地址端口与对应的socket对应时,阻塞队列需要的资源被满足,操作系统将其唤醒,并交付数据

这么一个简述的过程中,涉及的有:系统调用、进程通信、资源管理、进程管理、中断处理

有对计算机基础的了解,这些个是必然知道的

其中,最主要的部分是:操作系统判断进程所需资源是否满足,进而将其挂起或者唤醒

socket与网卡数据是不直接相关的,操作系统完全可以随便给点数据,或者其他进程往其中写入数据,典型如虚拟网卡


有CPU或者操作系统在做死循环的观点,这属于操作系统和硬件两方面的配合的问题

while true和select有本质的差别

while true会一直跑其中的代码,select则会让出执行权,进入等待,OS会调入别的程序运行

OS会轮询进程是否满足资源?并不是,toyOS可以这么设计,好一点的则要有信号量、阻塞队列等

那操作系统轮询信号量?也不是,操作系统维护信号量,不需要去轮询

那操作系统轮询中断?也不是?中断是CPU处理的,OS提供了一套中断服务

那说白了还是CPU每个周期轮询嘛,好吧,取指,译码、执行也是个死循环......

另外现在中断仲裁都独立了

如果OS发现没有东西可调度了,也没有要关机,就会拉起一个进程,去降低各个设备的功耗,连CPU自己的频率都会降低

在risc-v特权级指令中,还有个wait for interrupt指令,这时候也不会跑满CPU跑死循环检测针脚

在支持远程开机的设备中,网卡保活,收到数据后,产生中断,唤醒操作系统,CPU也没有在一直跑死循环

CPU这种人造物巅峰,实际上还是凭借高速处理重复逻辑,大力出奇迹,就此而言,啥都是在跑死循环

以下是评语,满足于看到系统调用、阻塞、挂起、信号量、中断等名词的,慎入



zhzgj出没


类似的还有JVM调优,高并发系统架构,分布式系统,连Redis调优也开始有点苗头了

拿JVM调优举例,对STW Eden Surviver和各个参数,各个工具的名字如数家珍

然而啥也不会,不知道该怎么调JVM启动参数

如果你知道系统动态内存分配,malloc free,你看到那个jvm最大最小内存和初始内存,你就会知道防止申请和释放内存应该把他们写成一样大

这是jvm调优常用手法之一


user avatar   zhang-yan-fei-26-61 网友的相关建议: 
      

大家天天都在说阻塞,实际上95%的程序员并没有真正理解阻塞是啥。这里并没有循环的事情,我们来从内核视角详细剖析一下阻塞到底是啥,它是如何工作的。

把问题再具体一下,recv 接收数据阻塞的原理是啥? 理解了这个就能真正理解所有的阻塞了。用一段大家都熟悉的代码来举例!

       int main() {  int sk = socket(AF_INET, SOCK_STREAM, 0);  connect(sk, ...)  recv(sk, ...) }     

在上面的 demo 中虽然只是简单的两三行代码,但实际上用户进程和内核配合做了非常多的工作。大致的工作流程如下:

看到这里,你可能还没看着阻塞的原理。别着急,往下看。我们来看 recv 函数依赖的底层实现。首先通过 strace 命令跟踪,可以看到 clib 库函数 recv 会执行到 recvfrom 系统调用。

进入系统调用后,用户进程就进入到了内核态,通过执行一系列的内核协议层函数,然后到 socket 对象的接收队列中查看是否有数据,没有的话就把自己添加到 socket 对应的等待队列里。最后让出CPU,操作系统会选择下一个就绪状态的进程来执行。整个流程图如下:

以上这个流程图是我根据 Linux 内核源码的执行过程总结后画出来的。

注意上面的第四步和第五步。第四步中是在访问 sock 对象下面的接收队列,如果接收队列中还没有数据到达,那么就会进入第五步,把当前进程阻塞掉。

但是在把自己阻塞掉之前,进程干了一件事, 给 socket 上留了个标记。告诉内核,如果这个 socket 上数据好了,记得叫我起来哈!就是源码 prepare_to_wait 函数中的 __add_wait_queue 这一句。

       //file: kernel/wait.c void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) {  unsigned long flags;   wait->flags &= ~WQ_FLAG_EXCLUSIVE;  spin_lock_irqsave(&q->lock, flags);  if (list_empty(&wait->task_list))   __add_wait_queue(q, wait);  set_current_state(state);  spin_unlock_irqrestore(&q->lock, flags); }     

接下来 Linux 就会选择下一个就绪状态的进程来执行。这就是阻塞原理的上半段,就是进程修改自己的状态,主动交出 CPU 的执行权。

当有数据到达的时候,内核首先将数据包放到该 socket 的接收队列中。然后扫描一下 socket 等待队列,然后发现:“呦呵,有进程阻塞在这个 socket 上面哎,好唤醒它”。

具体到代码里就是 __wake_up_common 这个函数会访问 socket 的等待队列。

       //file: kernel/sched/core.c 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;  } }     

在 __wake_up_common 中找出一个等待队列项 curr,然后调用其回调函数 curr->func,来完成进程的唤醒。不过,要注意的是,这个唤醒只是把相应的进程放到可运行队列里而已。真正的执行还得等其它进程主动释放 CPU 或者是时间片到了之后,内核把其它进程拿下以后才能真正获得 CPU 并开始执行。

参考:图解 | 深入理解高性能网络开发路上的绊脚石 - 同步阻塞网络 IO

说到这里,你可能还会问了。内核是如何接收包的,毕竟唤醒用户进程是它干的。难道它不是一个死循环么?是的,并不是。

网卡上收到数据包的时候,是通过硬中断唤醒内核进程处理,硬中断会触发软中断。有了软中断请求以后,ksoftirqd 内核线程才开始执行。来从网卡上取包,处理,放到接收队列,然后唤醒用户进程。

参见:图解Linux网络包接收过程

究其根源,是由网卡的硬中断来触发的。如果一段时间内没有网络包处理,那么没有死循环来消耗 CPU 的。

对网络底层还有啥不理解的,来看看我的公众号「开发内功修炼」 或许可以帮你解开一些困惑。

Github: GitHub - yanfeizhang/coder-kung-fu: 开发内功修炼

哦对了,想理解多路复用,来看看我的这一篇吧,也是从源码角度深入分析的。图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!


user avatar   s.invalid 网友的相关建议: 
      

一句话就能说清楚的事…完美体现了“会者不难难者不会”这句俗话。


很简单:当你调用select或别的什么、暂时被阻塞时,实际上就是操作系统判断你“需要的资源无法满足”,于是就把你的进程放进了“挂起”队列,不给你分配CPU资源了。


那什么时候你能回到就绪队列、重新获得被调度资格呢?


很简单,当网卡或你等待的别的什么硬件发出一个中断,让操作系统知道你需要的资源来了,它自然就把你移出挂起队列,重新给你安排时间片。


user avatar   zhao-hong-xin-17 网友的相关建议: 
      

从道理来说,早减晚增本身是没啥毛病的,毕竟只是个选项,丰俭由人。

大家怕的是某些人通过这些选项,再加点私货。而且这个说法和推迟退休一起出来,由不得大家多想。

按照目前的舆论情况,如果你敢允许早退减拿,估计只要不在体制内的人就统统早退了,反正也没啥规定领了社保就不能接着打工,对吧?甚至还可以把原来交给社保的那块放自己口袋。

所以,让你早退减拿是不可能的,忽悠大家晚退多拿的可能性比较大。




  

相关话题

  如何看待CentOS 8将于2021年结束支持? 
  音频程序是如何在现代操作系统抢占式时间分片机制下持续输出信号的? 
  如何看待 2018 年 1 月 2 日爆出的 Intel CPU 设计漏洞? 
  Windows 9x的存储控制器驱动程序是怎么样的? 
  kali linux是否是最好的linux渗透测试系统? 
  linux为什么不能硬链接目录? 
  大家第一次碰Linux是什么时候呢? 
  有哪些值得学习的国内 c++ 开源项目? 
  企业用哪个版本的 Linux? 
  计算机系本科生有必要学习汇编语言吗? 

前一个讨论
美国动不动就枪击,那些富人、科学家在美国就不担心自己的安全吗?
下一个讨论
程序员有必要知道为什么做某个功能吗?





© 2024-05-20 - tinynew.org. All Rights Reserved.
© 2024-05-20 - tinynew.org. 保留所有权利