百科问答小站 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 网友的相关建议: 
      

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

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

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

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




  

相关话题

  为什么WinXP的驱动无法用于Win7、Vista,但是Win7,Vista驱动可用于Win10? 
  为什么桌面领域没有一款 Linux 或 UNIX 能与 Windows 鼎足而立? 
  页表到底是保存在内核空间中还是用户空间中? 
  Linux交换空间可以和其他目录放在不同的硬盘中吗? 
  C++ 难在哪? 
  如何看待明尼苏达大学因插入实验性漏洞,被禁止贡献 Linux 内核代码? 
  为什么每款 ARM 电脑都需要专门为它编译的内核,而 x86 的可以通用? 
  现在的编程语言越来越多,为什么 C 和 C++ 还没有被现在的时代淘汰呢? 
  怎么看待 Linus 和 Richard Stallman 对 C++ 的态度? 
  C++可否将父类的对象cast子类,并调用子类的private函数? 

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





© 2024-12-22 - tinynew.org. All Rights Reserved.
© 2024-12-22 - tinynew.org. 保留所有权利