暂无图片
暂无图片
暂无图片
暂无图片
1
暂无图片

Linux之进程阻塞为什么不占用cpu资源

3023

参考文档:https://zhuanlan.zhihu.com/p/63179839

阻塞是进程调度的关键一环,指的是进程在等待某事件(如接收到网络数据)发生之前的等待状态,recv、select和epoll都是阻塞方法。

如下面所示一段服务端server的代码:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 6999))  # 绑定要监听的端口
server.listen(5)
while True:
    conn, addr = server.accept()  # 等待链接
    print(conn, addr)
    while True:
        try:
            data = conn.recv(1024)  # 接收数据
            print('recive:', data)  # 打印接收到的数据
            conn.send(data.upper())  # 然后再发送数据
        except Exception as e:
            print('关闭了正在占线的链接!')
            break
    conn.close()

复制

如上面代码片段中的 data = conn.recv(1024),recv是个阻塞方法,当程序运行到recv时,它会一直等待,直到接收到数据才往下执行。

那么阻塞的原理是什么?我们知道进程状态转换有如下图所示的流程,正在运行的进程由于提出系统服务请求(如I/O操作),但因为某种原因未得到操作系统的立即响应,或者需要从其他合作进程获得的数据尚未到达等原因,该进程只能调用阻塞原语把自己阻塞,等待相应的事件出现后才被唤醒。那么更详细的解释是什么呢?

工作队列

操作系统为了支持多任务,实现了进程调度的功能,会把进程分为“运行”和“等待”等几种状态。运行状态是进程获得cpu使用权,正在执行代码的状态;等待状态是阻塞状态,比如上述程序运行到recv时,程序会从运行状态变为等待状态,接收到数据后又变回运行状态。操作系统会分时执行各个运行状态的进程,由于速度很快,看上去就像是同时执行多个任务。

下图中的计算机中运行着A、B、C三个进程,其中进程A执行着上述基础网络程序,一开始,这3个进程都被操作系统的工作队列所引用,处于运行状态,会分时执行。

等待队列

当进程A执行到创建socket的语句时,操作系统会创建一个由文件系统管理的socket对象(如下图)。这个socket对象包含了发送缓冲区、接收缓冲区、等待队列等成员。等待队列是个非常重要的结构,它指向所有需要等待该socket事件的进程。

当程序执行到recv时,操作系统会将进程A从工作队列移动到该socket的等待队列中(如下图)。由于工作队列只剩下了进程B和C,依据进程调度,cpu会轮流执行这两个进程的程序,不会执行进程A的程序。所以进程A被阻塞,不会往下执行代码,也不会占用cpu资源。

唤醒进程

当socket接收到数据后,操作系统将该socket等待队列上的进程重新放回到工作队列,该进程变成运行状态,继续执行代码。也由于socket的接收缓冲区已经有了数据,recv可以返回接收到的数据。

注:这是以前我发表在其他平台的文章,最近决定以后主要在公众号更新,所以准备把之前其他平台发表的文章移到这边来,便于归档和查找。祝大家安好!


点个“赞 or 在看” 你最好看!


关注我,和我一起拯救吧!





👇典典下面的小咔片给作者鼓励下吧,这对我很重要🐶

文章转载自PostgreSQL运维技术,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

一介俗人
暂无图片
2年前
评论
暂无图片 0
"等待队列是个非常重要的结构,它指向所有需要等待该socket事件的进程" 某个进程创建的socket对象,在哪种情况下会指向其他进程呢???
2年前
暂无图片 点赞
评论