linux shell操作 - 05 进程 与 IO 模型

文章目录

  • 计算机内存分配
  • 进程与子进程
  • IO模型
  • 非阻塞IO
  • IO多路复用
  • 网络IO模型
    • 简单的socket
    • 并发的socket

计算机内存分配

一个32位,4G内存的计算机,内存使用分为两部分:

  • 操作系统内核空间
  • 应用程序的用户空间
  • 使用的操作系统不同,分配方式不同;
    在这里插入图片描述

进程与子进程

  • 进程是操作系统中资源管理的最小单位,它是将静态程序加载到内存中的一次动态的执行,包括进程创建、进程调度、进程销毁;

  • 每个进程有自己独有的内存(在用户空间内),进程的私有内存是相互独立的,且进程间无法直接通信;

  • 不同进程间通信,可以采用队列、管道、信号、共享内存(内核空间内)等方式;

  • 每个进程中可以创建一个或者多个线程,多个线程共享当前进程的部分内存资源,如代码、全局变量等;

  • 进程的内存分布
    在这里插入图片描述

  • 比如上图中的shell脚本,运行时,shell是父进程,python3开启子进程,父进程会等待子进程退出;当关闭shell进程(父进程)时,python3子进程由init进程接管。

  • 父进程中,当以fork()系统调用创建子进程时,子进程执行exit()系统调用退出后,内核中仍然存有子进程的信息,如pid, exit code, run time等,这些信息要等父进程通过wait/waitpid来回收,若一直未回收,则退出的子进程成为僵尸进程,一直占用系统资源。

  • 僵持进程无法通过kill关闭,随着数量增多,系统资源耗尽,导致系统瘫痪。

  • 可以进行IO(input输入、output输出)操作的内核对象
  • 如文件、管道、socket…
  • 流的入口是fd (file descriptor);

IO模型

  • 阻塞IO, 一直等待,不占用资源;无法同时处理多个任务;
    用户进程发起读的系统调用,当内核中socket fd未就绪时,一直阻塞等待;
    socket fd 就绪时,将内核中的socket数据拷贝到用户空间(拷贝期间阻塞等待);
    accept()阻塞
    在这里插入图片描述

  • 非阻塞IO, 忙轮询,占用CPU;
    应用程序不断轮询内核,对应的socket fd是否就绪,未就绪则返回(非阻塞);
    若已就绪,则拷贝内核中socket的数据到用户空间(阻塞)。
    accept()不阻塞
    在这里插入图片描述

  • IO多路复用,多个IO复用一个进程/线程,既可以阻塞等待不占用资源,又可以同时并发处理多个任务;

    • linux 支持select, poll, epoll
    • 应用程序通过系统调用让内核同时监控多个socket fd,一旦有网络事件发生,内核就遍历找到对应的socket,将其标记为可读,然后将所有的socket fd返回给应用程序;
    • 应用程序遍历所有的fd,找到就绪的fd,通过系统调用复制对应socket的数据到用户空间;
      在这里插入图片描述
  • 异步IO;

  • 应用程序发起异步read操作后,立即返回;

  • 内核中的fd就绪,复制数据完成,触发信号通知应用程序;

  • 全程无阻塞;
    在这里插入图片描述

  • 信号驱动IO

  • 首先注册信号处理函数;

  • 检查内核socket fd 是否就绪,未就绪直接返回;

  • 已就绪,则内核发送信号给应用程序,触发信号处理函数;

  • 信号处理函数,发起系统调用,从内核空间拷贝socket数据到用户空间(阻塞);
    在这里插入图片描述

 

非阻塞IO

  • 忙轮询,占用CPU;
  • 性能不如阻塞IO;
  • 代码流程,不停地 遍历所有的fd,查看是否就绪;
    在这里插入图片描述

 

IO多路复用

多个IO复用一个进程/线程,既可以阻塞等待不占用资源,又可以同时并发处理多个任务;

  • select

    • 最大连接数默认1024;
    • 两次拷贝,先将所有的fd 从用户空间拷贝到内核空间,由内核监控是否有fd就绪(可读或可写),也就是有网络事件发生;一旦有fd就绪,则遍历所有的fd集合,找到对应的fd并将其 标记为就绪态(可读、可写),然后将所有的fd(fd集合)从内核空间拷贝到用户空间,用户进程内遍历所有的fd,找出就绪的从进行读写;
    • 两次遍历
    • 并发量大时,性能指数式下降;
    • 代码流程:
      在这里插入图片描述
  • poll,与select 没有本质的区别,只是连接数比select多;

    • select 使用固定长度的 BitMap表示文件描述符集合,而poll 使用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,还会受到系统文件描述符限制。
    • select/poll 都是使用线性结构存储进程的 socket 集合,都需要遍历文件描述符集合来找到可读或可写的 socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合。
  • epoll,高性能的IO多路复用(仅linux支持)

    • 连接数更大,上限为进程的最大连接数;查看最大连接数cat /proc/sys/fs/file-max
    • 内核中采用红黑树结构,可高效地增删查(O(logn)),仅返回就绪的fd;
    • 将就绪的fd拷贝给用户进程,避免无用的遍历;
    • 适合并发量大的场景,可以解决C10K问题(单台服务器并发1w),
      在这里插入图片描述
    • 操作流程,使用epoll_create 创建内核epoll对象;epoll_ctl将要监控的socket加入红黑树,内核检测到有网络事件发生,则将对应的socket 连接放入一个就绪链表中,并复制给用户空间(epoll_wait返回);
      在这里插入图片描述
    • epoll支持水平触发边缘触发,边缘触发效率更高;select/poll仅仅支持水平触发,即内核socket缓冲区有数据就绪,在数据没有被进程读取完之前,会多次通知进程来读取;而边缘触发则仅仅通知一次,需要进程一次性读取所有的数据。例如,当快递放入快递站点时,管理员可能给你打多个电话催促你取快递,这种通知多次的方式就是水平触发;而当快递放入快递柜时,就只给你发送一次短信,仅仅通知一次,这种仅仅通知一次的方式就是边缘触发。
    • IO多路复用中有socket 就绪,并不一定可读、可写,此时为避免进程阻塞,需要结合非阻塞IO一起使用。

 

网络IO模型

  • 基于socket网络通信
    在这里插入图片描述
  • 客户端与服务端建立连接的过程
    • 客户端的socket对象调用connect((ip, port));
    • 服务器的网卡接收请求,并转发给OS ,实现TCP三次握手;同时在操作系统内核中维护两个队列,TCP半连接队列 & TCP全连接队列;半连接队列表示未完成三次握手,全连接队列表示完成三次握手,已完成socket连接;
    • 内核从TCP全连接队列取出当前socket连接,存储到内核文件列表中,同时将其fd返回用户空间,存入进程数组;
    • 应用程序中就可以拿着这个已连接的socket进行读写;
    • 读/写时 就对应阻塞IO、非阻塞IO、IO多路复用、异步IO、信号驱动的IO的情况;
      在这里插入图片描述

下面以python3语言为例演示socket的使用。

简单的socket

同时只能处理一个客户端的请求。

server:

import socket
import time
import sys
import signal  # 注册信号的处理函数


def handler(signum: int, frame):
    """
        接收到SIGINT信号时,打印一句话,并退出进程
    """
    print("received signal:", signum)
    sys.exit(0)


# 注册信号的处理函数
signal.signal(signal.SIGINT, handler)  # 使用 Ctrl + C 发送SIGINT信号


# 网络层使用Ipv4
# 传输层使用TCP
sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 默认为阻塞的socket

# 绑定ip
sock_server.bind(("localhost", 8000))
# 最多监听1000个连接
sock_server.listen(1000)  # 监听的socket

while True:
    # TCP三次握手完成,接收socket连接
    conn, addr = sock_server.accept()  # accept阻塞 等待 socket连接就绪

    # 处理当前的socket连接,conn是已连接的socket
    print("conn:", conn, addr)
    # 读 IO操作
    data = conn.recv(1024)  # 阻塞等待 内核中socket就绪,并拷贝数据到用户空间 
    print("received data:", data.decode())

    # 写 IO操作,拷贝到内核空间,写入socket缓冲区
    conn.send(b"hello, i am server. I have got your data.")
    
    
    # 在当前socket连接 的请求处理完之前,服务端不会接收下一个客户端的socket连接
    time.sleep(20)

client:

import socket
import time


# 创建客户端
sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立连接
sock_client.connect(("localhost", 8000))

# 发送数据
sock_client.send(b"hello, i am jack")
print("发送数据完成.")

# 接收数据
data = sock_client.recv(1024) # 接收 1024 bytes    # 阻塞  内核fd未就绪->就绪  内核socket数据复制到用户空间
print("received data:", data.decode())

并发的socket

同时可以处理多个客户端的请求。

  • 阻塞+多进程
    • 随着请求数量的增多,子进程越来越多,(fork创建子进程复制父进程所有的资源)占用的系统资源越来越多,并发量大时会影响系统的性能,甚至导致系统崩溃;
    • 多进程上下文的切换包括用户空间、内核空间,消耗系统性能;
    • 所以并发量特别大时,多进程不是理想的方案。
# server.py
import socket
import os
import time
import sys
import signal  # 注册信号的处理函数
import multiprocessing


def handler(signum: int, frame):
    """
        接收到SIGINT信号时,打印一句话,并退出进程
    """
    print("received signal:", signum)
    sys.exit(0)


# 注册信号的处理函数
signal.signal(signal.SIGINT, handler)  # 使用 Ctrl + C 发送SIGINT信号


# 处理请求
def handle_request(conn, addr):
    print("subprocess:", os.getpid())
    print("conn:", conn, addr)
    
    flag = False
    while not flag:
        # 接收数据
        data = conn.recv(1024) # recv from kernel
        print("received data:", data.decode())

        # 发送数据
        conn.send(b"I am server. I have got your data.")

        # 检测客户端的断开
        data = conn.recv(1024)
        print("客户端断开:", data.decode())
        if not data:
            conn.close()
            print("客户端已断开.")
            flag = True
    print(f"{os.getpid()}子进程退出.")




if __name__ == "__main__":
    # 网络层使用Ipv4
    # 传输层使用TCP
    sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 默认为阻塞的socket

    # 绑定ip
    sock_server.bind(("localhost", 8000))
    # 最多监听1000个连接
    sock_server.listen(1000)  # 监听的socket

    while True:
        # TCP三次握手完成,接收socket连接
        conn, addr = sock_server.accept()  # accept阻塞 等待 socket连接就绪
        # 父进程 阻塞等待连接
        
        print("创建子进程.")
        # 子进程处理 请求
        sub_process = multiprocessing.Process(target=handle_request, args=(conn, addr))
        sub_process.daemon = True
        sub_process.start()



# client.py
import socket
import time


# 创建客户端
sock_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立连接
sock_client.connect(("localhost", 8000))

# 发送数据
sock_client.send(b"hello, i am jack")
print("发送数据完成.")

# 接收数据
data = sock_client.recv(1024) # 接收 1024 bytes
print("received data:", data.decode())

sock_client.close() # 客户端断开连接,会发送空数据到服务端
  • 阻塞+多线程
    • 线程是轻量级进程,同一个进程的多个线程可以共享当前进程的部分资源(代码、全局变量等),避免了过多的资源消耗;
    • 多线程的上下文切换,虽比多进程轻量,但大量的线程来回切换,也会给系统造成不小的开销;
    • 多线程需要考虑线程安全问题,另外每个线程也有自己的栈空间,也消耗内存;大量的线程必然会消耗大量的栈空间;
    • 所以对于特别大的并发量时,多线程也不是理想的方案。
在这里插入代码片
  • IO多路复用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/196567.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用低代码平台加速应用开发?

目录 一、背景 二、低代码开发和传统开发的区别 1.低代码开发方式能够实现业务应用的快速交付 2.低代码开发平台还能够降低业务应用的开发成本 三、低代码开发对你有什么帮助? 四、低代码工具的使用者是谁? 五、典型的低代码开发平台有哪些&#xff1f…

02-鸿蒙学习之4.0todoList练习

02-鸿蒙学习之4.0todoList练习 代码 /*** 1:组件必须使用Component装饰* 2.Entry 装饰哪个组件,哪个组件就呈现在页面上* 3.被Entry 装饰的入口组件。build()必须有且仅有一个根 ** 容器 ** 组件* 其他的自定义组件,build() 中…

一篇总结 Linux 系统启动的几个汇编指令

学习 Linux 系统启动流程,必须熟悉几个汇编指令,总结给大家。 这里不是最全的,只列出一些最常用的汇编指令。 一.数据处理指令 1.数据传送指令 【MOV指令】 把一个寄存器的值(立即数)赋给另一个寄存器,或者将一个…

spring boot 整合 spring security

项目结构 添加依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.9.RELEASE</version><relativePath/></parent><dependency><grou…

leetcode刷题详解十

188. 买卖股票的最佳时机 IV&#xff08;最多买卖k次&#xff09; 注意事项 这道题和最多买卖两次是一模一样的思路就是把2换成k了但是还是有几个地方需要注意的 给的整数数组可能为0k其实没有很大&#xff0c;可以想一下&#xff0c;最多为n/2(n是数组的长度) int maxProfit…

大型网站系统架构演化(Web)

大型网站系统架构演化 大型网站系统架构演化需要关注的维度涉及的技术演进过程单体架构垂直架构使用缓存改善网站性能缓存与数据库的数据一致性问题缓存技术对比Redis分布式存储方案Redis集群切片的常见方式Redis数据类型Redis 淘汰算法 大型网站系统架构演化 需要关注的维度 …

2024年最受欢迎的项目管理工具盘点

十大项目管理系统包括&#xff1a;1.产品研发项目管理工具&#xff1a;PingCode&#xff1b;2.通用项目协作工具&#xff1a;Worktile&#xff1b;3.开源项目管理系统&#xff1a;Redmine&#xff1b;4.IT/敏捷项目管理系统&#xff1a;Jira&#xff1b;5.免费个人项目管理&…

NX二次开发UF_CURVE_create_arc_point_point_radius 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_create_arc_point_point_radius Defined in: uf_curve.h int UF_CURVE_create_arc_point_point_radius(tag_t point1, tag_t point2, double radius, UF_CURVE_limit_p_t l…

问答社区运营的核心是什么?

问答社区是用户在平台上获得信息的一种方式&#xff0c;一般问答社区适用于医疗行业&#xff0c;法律行业等专业领域的行业&#xff0c;可以划分为知识型分享社区的一种&#xff0c;用户提供提问&#xff0c;邀请回答&#xff0c;选择最佳回复&#xff0c;设置问题围观&#xf…

字符串逆序问题

写一个函数&#xff0c;可以将任意输入的字符串逆序&#xff08;要可以满足多组输入&#xff09; 这个题有三个点 1.要读入键盘输入的字符串&#xff0c;所以要用到字符串输入函数 2.可以进行多组输入 3.把输入的n组字符串都逆序 #define _CRT_SECURE_NO_WARNINGS 1 #incl…

Portraiture2024最新Photoshop磨皮插件更新啦

Portraiture是一款由Imagenomic公司研发的Photoshop磨皮插件。该插件以其优秀的磨皮效果&#xff0c;成为了众多摄影师和化妆师使用的首选插件。Portraiture主要用于影楼、婚纱、时尚摄影等各个领域。其主要特点是能够轻松地模拟人眼的视觉感受&#xff0c;自然地修饰人像照片。…

【Linux专题】http(s)代理

【赠送】IT技术视频教程&#xff0c;白拿不谢&#xff01;思科、华为、红帽、数据库、云计算等等_厦门微思网络的博客-CSDN博客文章浏览阅读444次。风和日丽&#xff0c;小微给你送福利~如果你是小微的老粉&#xff0c;这里有一份粉丝福利待领取...如果你是新粉关注到了小微&am…

元宇宙的八个关键技术介绍!

人工智能&#xff08;AI&#xff09;、物联网、增强现实、虚拟现实、区块链、NFT、3D建模、空间和边缘计算等技术使最元宇宙开发成为可能。本文对元宇宙的8个关键技术进行了介绍。 人工智能 人工智能技术中的目标分割、目标追踪、姿态估计等是元宇宙场景中感知现实的关键工具&…

【笔记】小白学习电路维修

学习视频&#xff08;b站&#xff09;&#xff1a;从0开始学电路 从0开始学电路维修 p1 黄色长方体元件P2 故障率最高的元件p3带芯铜丝线圈是什么区分电感和变压器接入电路分析&#xff1a; p4 交流和直流分界线整流桥接线整流桥故障判断 带色环的不一定是电阻 p1 黄色长方体元…

51单片机项目(16)——基于51单片机的水箱冷却系统

1.项目背景 汽车水箱又称散热器&#xff0c;是汽车冷却系统中主要机件&#xff1b;其功用是散发热量&#xff0c;冷却水在水套中吸收热量&#xff0c;流到散热器后将热量散去&#xff0c;再回到水套内而循环不断。从而达到散热调温的效果。它还是汽车发动机的重要组成部分。 汽…

解决ssh使用public key远程登录服务器拒绝问题

目录 使用场景windows安装ssh客户端使用powershell ssh登录服务器生成密钥文件ubuntu ssh服务器配置使用vscode远程登录使用Xshell远程登录使用MobaXtem远程登录Server refused our key问题解决方案 使用场景 使用vscode远程ssh登录使用public key不需要输入密码,比较方便. w…

Java 之 lambda 表达式(一)

目录 一. 前言 二. lambda 表达式语法 2.1. 语法1&#xff1a;无参&#xff0c;无返回值 2.2. 语法2&#xff1a;一个参数&#xff0c;无返回值 2.3. 语法3&#xff1a;两个参数&#xff0c;lambda 体中有多条语句 2.4. 语法4&#xff1a;两个以上参数&#xff0c;有返回…

Java 常用容器

目录 列表栈&#xff08;类&#xff09;队列(接口)setMap 列表 package com.czl;import java.util.ArrayList; import java.util.List; //AltEnter导入包 public class Main {public static void main(String[] args) throws Exception{List<Integer> list new ArrayLis…

群晖NAS配置之自有服务器ngrok实现内网穿透

群晖NAS配置之自有服务器ngrok实现内网穿透 前言-内网穿透 内网穿透是指通过一种技术让外部网络可以访问到内网的NAS设备&#xff0c;这样即使在不同网络环境下&#xff0c;也能够远程访问和管理NAS设备。以下是一些常见的内网穿透方案&#xff1a; Synology官方提供的Quick…

【Java Spring】SpringBoot常用插件

文章目录 1、Lombok1.1 IDEA社区版安装Lombok1.2 IDEA专业版安装Lombok1.3 Lombok的基本使用 2、EditStarters2.1 IDEA安装EditStarters2.2 EditStarters基本使用方法 1、Lombok 是简化Java开发的一个必要工具&#xff0c;lombok的原理是编译过程中将lombok的注解给去掉并翻译…