计算机基础(中断、IO)

操作系统

设备交互

CPU 与 IO 设备交互过程
CPU 通过设备控制器(驱动?)与计算机外设进行交互。可以将控制器想象成编程语言中的接口,然后不同地计算机外设的控制器去实现这个接口,CPU 只需要调用接口而无需关注具体地实现,通过这种设计可以让 CPU 指令做得精简。

拓展:软件六大编程原则之一:依赖倒置原则:上层模块不应该依赖底层模块,它们都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

中断

CPU 中断机制:键盘原理
在这里插入图片描述

DMA(Direct Memory Access)

将用户程序的数据源地址、数据目的地址以及数据长度一次性送给相应外设的 DMA 程序,从而减少 CPU 中断次数提高性能。

  1. 中断加 DMA 减少 CPU 开销

注意:DMA 只能完成内核与计算机外设之间的数据拷贝,内核态与用户态之间的数据拷贝还是需要 CPU 介入

系统调用

程序主要分为用户程序和操作系统程序两种,分别运行在用户空间和内核空间,为了安全以及保证硬件设备和操作系统的稳定性,只有操作系统程序才有权限访问硬件设备,所以应用程序只能通过系统调用/中断/异常三方式之一从用户态切换到内核态,经由操作系统程序完成与计算机外设的交互。

注:系统调用分类
在这里插入图片描述

  1. printf 的用户态与内核态切换动画演示
  2. 内核态 vs 用户态:通过内核态程序实现计算机资源的统一调度协调,从而让用户程序有条不紊地运行。
    在这里插入图片描述
  3. Linux 如何实现系统调用

32bit 操作系统的系统调用也是通过发起 ENTER_KERNEL 调用,发起中断指令 0x80 来实现的。
在这里插入图片描述
运行在用户态的函数通过 CPU 中的寄存器将函数执行的必要信息(系统调用号(哪个系统调用函数)、函数参数)传递给系统调用中断程序
在这里插入图片描述
系统中断程序运行过程:
在这里插入图片描述

64bit 操作系统的系统调用则直接通过 syscall 指令来实现的
在这里插入图片描述

I/O

同步阻塞

在 IO 连接建立后,对应的线程会被阻塞,直到收到数据后,该阻塞状态才会被解除。

  • 如果使用一个线程来监听连接,一个进程只能为一个客户提供服务;
  • 如果为每个连接单独分配一个线程可以解决一个进程维护多个连接的目的,但线程调度、上下文切换以及其占用的栈空间都可能成为瓶颈。
代码示例
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <iostream>

using namespace std;

/*
 * 1. 创建套接字
 * 2. 绑定地址信息
 * 3. 进行监听--(告诉OS内核可以接收客户端发送的新请求了并且监听新连接的到来)
 * 4. TCP三次握手建立连接----OS会自动进行,不需要我们进行控制
 * 5. 接收新连接
 * 6. 接收数据、发送数据
 * 7. 关闭套接字
 */ 

int main()
{
    //1. 创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sockfd < 0)
    {
        cout << "TCP socket failed" << endl;
        return 0;
    }

    //2.绑定地址信息
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(18989);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret < 0)
    {
        cout << "bind failed" << endl;
        return 0;
    }

    //3.进行侦听(告诉OS内核可以与客户端建立连接了)
    //listen函数中blocklog参数,设定的是已完成三次握手并已建立连接的队列的容量
    ret = listen(sockfd,1);
    if(ret < 0)
    {
        cout << "listen failed" << endl;
        return 0;
    }

    //4.接收新连接
    struct sockaddr_in client_addr;
    socklen_t socklen = sizeof(client_addr);
    int new_sockfd = accept(sockfd,
    			(struct sockaddr *)&client_addr,&socklen);

    if(new_sockfd < 0)
    {
        cout << "accept failed" << endl;
        return 0;
    }

    printf("accept success: %s:%d, and new_sockfd is %d\n",
    inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),
    			new_sockfd);

    //5.接收和发送数据
    while(1)
    {
        //这里接收数据和发送数据是没有顺序而言的
        //因为已经经过三次握手,客户端和服务端的连接以及建立成功了
        
        char buf[1024] = {0};

        ssize_t recv_size = recv(new_sockfd,buf,sizeof(buf)-1,0);
        if(recv_size < 0)
        {
            cout << "recv failed" << endl;
            continue;
        }
        else if(recv_size == 0)
        {
            cout << "Peer close" << endl;
            close(new_sockfd);
            close(sockfd);
            return 0;
        }
        
        cout << "accrpt data is : " << buf << endl;

        //发送数据
        memset(buf,'\0',sizeof(buf));
        sprintf(buf,"hello lient,i am server,i am %s:%d\n",
        		inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
        		
        ssize_t send_ret = send(new_sockfd,buf,strlen(buf),0); 
        if(send_ret < 0)
        {
            cout << "send failed" << endl;
            continue;
        }

    }

    close(sockfd);
    return 0;
}

可以看出socket 编程服务端核心步骤为:

  1. bind:绑定端口
  2. accept:等待客户端连接
  3. rev:接收客户端请求
  4. send:将数据响应给客户端
资料
  1. 动画演示 BIO 原理:socket 结构、进程切换、用户态与内核态的切换。
    在这里插入图片描述
  2. Socket 缓冲区

一个 socket,会带有两个缓冲区,一个用于发送,一个用于接收。因为这是个先进先出的结构,有时候也分别叫它们发送队列、接收队列。
在这里插入图片描述

同步非阻塞

I/O 是否阻塞,由这个连接对应的 fd 阻塞属性决定。
在这里插入图片描述
在 Linux 中,可以在创建 socket 时,直接将它设置为非阻塞模式 int socket (int __domain, int __type, int __protocol),将 __type 增加SOCK_NOBLOCK 就可以创建一个非阻塞的 socket,例如:int socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

异步

Reactor 与 Proactor 为什么性能高?

先来看看阻塞 I/O,当用户程序执行 read ,线程会被阻塞,一直等到内核数据准备好,并把数据从内核缓冲区拷贝到应用程序的缓冲区中,当拷贝过程完成,read 才会返回。

注意:阻塞等待的是「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程。非阻塞的 read 请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲区,read 调用才可以获取到结果。

将数据从内核缓冲区拷贝到用户程序空间是一个同步过程。因此,无论 read 和 send 是阻塞 I/O,还是非阻塞 I/O 都是同步调用。

异步 I/O 在「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程都不用等待。 当我们发起 aio_read (异步 I/O) 之后,就立即返回,内核自动将数据从内核空间拷贝到用户空间,这个拷贝过程是内核自动完成的,和前面的同步操作不一样,应用程序并不需要主动发起拷贝动作。

资料
  1. 动画演示 NIO 原理
    在 IO 连接建立后,对应的线程在未收到数据时不会被阻塞,程序员可以将该连接对应的 fd 用集合保存起来,后续逐次轮询,有数据时再从 fd 对应的连接中取出数据进行处理,达到一个线程可以处理多个网络连接的目的,从而提高效率。

在这里插入图片描述

多路复用

多路:指多个 Socket 连接,复用:使用同一个线程。

动图解释 select、poll、epoll 实现原理

select(man 2 select)

select 中定义的 fd_set 实际是一个 bitmap(默认长度为 1024 ,更改该长度需要修改内核代码然后重新编译),以文件描述符作为 bitmap 的索引设置值。内核会轮询集合中的 fd,当对应的 fd 的数据队列中有数据时,会将其对应的等待进程队列中的进程唤醒,并根据 fd 是否有数重新设置 bitmap 的值后从内核态重新返回到用户态,用户程序从 bitmap 有值的 fd 中读取数据。
在这里插入图片描述
总结:
在这里插入图片描述
使用:MySQL

其它
  1. 位图实现:通过 long 数组模拟而成,每个比特对应一个文件描述符数值。
    在这里插入图片描述通过在 Linux 上执行 man select 可以看到:

An fd_set is a fixed size buffer. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or larger than FD_SETSIZE will result in undefined behavior. Moreover, POSIX requires fd to be a valid file descriptor.

FD_SETSIZE 在 sys/select.h 文件中的定义如下:

// include/uapi/linux/posix_types.h
#define __FD_SETSIZE    1024

typedef struct {
    unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} __kernel_fd_set;

可以看出 Linux 内核的宏限制了 fd_set 最多只支持1024。

注意:

  1. 在 Linux 中,文件描述符数值 0,1,2 已经被系统占用,分别表示为标准输入,标准输出和标准错误。
  2. select 不支持大于 1024 的文件描述符的监听。
  1. select 函数的参数 maxfd (最大文件描述)有什么作用?
    select 使用 1024 比特位图监测最多 1024 个文件描述符,然而实际的情况很少会到达1024文件描述符上限,使用 maxfd 可以避免每次都轮询 1024 个文件描述符,从而提高轮询效率。maxfd 通常设置为已打开最大文件描述符 +1,目的是为了保证位图中每个文件描述符都被轮询到。
  2. 优缺点
  • 优点:
    1. select 支持多种文件描述符类型,包括 socket、标准输入输出、管道、FIFO 等。
    2. select是跨平台的,可以在多种操作系统上使用。
  • 缺点:
    1. select 的效率不高,每次调用 select 都需要将所有托管的文件描述符从用户态复制到内核态,托管的文件描述符中有一个文件描述符事件就绪后,也会将所有托管的文件符从内核空间拷贝到用户空间,这个过程比较耗时。
    2. select返回后需要遍历所有的文件描述符,找到就绪的文件描述符,这个过程也比较耗时。
    3. select支持的文件描述符数量有限,需要保证放入 select 监听的文件描述符不大于 1024,否则程序会出现未知错误。
  1. select 为什么会有 1024 文件描述符限制?
    • select 采用轮询的方式获取读,写,异常事件,如果需要轮询的文件描述符比较多的话,select 执行效率会非常低。
poll(man 2 poll)

下图有些错误:revents 不只是一个状态标识 1,其值为可用的事件(比如 POLLIN)
在这里插入图片描述

注释:

  1. 最常用的网络事件为 EPOLLIN 和 EPOLLOUT,EPOLLIN 对应为有 socket 缓冲区数据可读(当又收到了对端的一些数据,就会触发;或者作为服务端时有连接连过来),EPOLLOUT 对应 socket 缓冲区可写。

总结:
在这里插入图片描述

epoll(extend poll:man 2 epoll_create)

可以通过红黑树以 O(log(n)) 的复杂度找到该 fd 对应的 epitem
在这里插入图片描述
注意:

  • 调用 epoll_ctl 函数将 fd 添加(EPOLL_CTL_ADD)到红黑树时,也会注册一个回调函数,该回调函数在该 fd 关注的事件发生时,会将其对应的 epitem 加入到数据就绪队列中。

红黑树排序规则 :
在这里插入图片描述
总结:
在这里插入图片描述
注意:

  • 由于需要在内核空间查找红黑树,所以实际的时间复杂度为 O(log(n)),这里的 O(1) 指,通过 epoll_wait 返回到用户空间的 epoll_event 数组都是有事件发生的 fd,所以无需像 select 或 poll 那样,在用户空间遍历 fd 集合(时间复杂度为 O(n))判断 fd 是否就绪(select 通过 FD_ISET 函数,poll 通过类似 pollfd.revents & POLLIN 运算)。
  • select、poll 在监听 fd 状态前,每次都需要全量的将需要监听的 fd 集合从用户态拷贝到内核态,在 fd 集合中有一个 fd 发生监听的事件时,也会全量的 fd 集合从内核态拷贝到用户态,这样是及其耗费资源与性能的。epoll 只有在调用 epoll_ctl 时,才会将 fd 从用户态拷贝到内核态,然后在内核态维护该 fd 集合构成的红黑树,在 fd 集合中有 fd 发生了监听的事件时,也只会返回这些发生了事件的 fd。
    在这里插入图片描述
思考
  1. 在并发量不高时,select、poll 通过轮询 fd 集合来判断 fd 是否已经就绪的效率也并不低,所以此时仍然可考虑使用 select 和 poll,但如果在高并发量情况下(比如 C10K),就需要用 epoll 模型,它的速度更快,开销更小。
  2. 红黑树 vs 哈希表

linux 内核提供的哈希表是 hlist,即采用拉链法解决哈希碰撞的方法。hlist 进行哈希查找是 O(1) 是建立在哈希映射到各个桶的概率一致,并保证现有元素比桶个数小于负载因子 α 后,在有足够多元素时可以认为每一个桶中元素的个数服从 π(α) 即泊松分布。然而,在某些极端的情况下(或者说哈希安全性被打破的情况下),某些桶内包含了更多的元素,某些元素的查找时间会远高于其它的,并且单依靠拉链法是无法解决这种退化情况的。在删除某些元素时需要找到对应的节点,也就是说,在扩容/缩容和删除某些节点时,其操作时间可能是不可控的。相比之下,红黑树的强约束就显得可控许多了。

  1. 相比 select、poll 只支持水平触发(LT:Level Triggered),epoll 还支持边缘触发(ET:Eage Triggered),两种触发模式的区别是啥?

水平触发和边缘触发通过 epoll_wait 函数的响应体现,示例。

  • LT:只要缓冲区有数据,epoll_wait 就会一直被触发,直到缓冲区为空;(有数据会连续触发)
  • ET:只有所监听的事件状态改变或者有事件发生时,epoll_wait 才会被触发;(有数据只触发一次)

Nginx ,除了 listenfd(网络服务器中一般涉及两类 sockfd:用于监听是否有连接请求的 listenfd;用于传输数据的 socket(accept 返回的 acceptfd)) 用水平触发之外,其它都是用的边缘触发。

  • 有一些解释是 listenfd 用水平触发,如果多个 client 同时连接进来,accept 一次只处理一个连接,为防止漏掉连接选择水平触发。
  1. 为什么 IO 多路复用要搭配非阻塞 IO?
    • 为什么 IO 多路复用要搭配非阻塞 IO?

    假如 socket 的读缓冲区中已有足够多的数据,需要调用三次 read 才能读取完。

    1. 非阻塞 I/O 的处理方式:循环的 read 或 accept,直到读完所有的数据(抛出 EWOULDBLOCK 异常)
    2. 阻塞 I/O 的处理方式:每次只能调用一次 read 或 accept,因为多路复用只会告诉你 fd 对应的 socket 可读了,但不会告诉你有多少的数据可读,所以在 handle_read/handle_accept 中只能 read/accept 一次,你无法知道下一次 read/accept 会不会发生阻塞。所以只能等 ioloop 的第二次循环,ioloop 告诉你 fd 可用后再继续调用 handle_read/handle_accept处理,然后再循环第三次。

      所以你会发现,阻塞方式稍不注意就会阻塞整个进程:多路复用只能知道有数据可读,但是不知道有多少数据可读,使用阻塞io,读了一次就不知道是否应该继续读,如果继续读,读到第四次就阻塞了。

零拷贝

资料
  1. 动画演示-传统网络读写过程:四次上下文切换,四次数据拷贝(两次 DMA 拷贝 + 两次 CPU 拷贝)
    在这里插入图片描述 动画图解 socket 缓冲区那些事:每个 socket 在内核态都有对应的读写缓冲区(readbuf、writebuf)
    在这里插入图片描述
    在这里插入图片描述

  2. 动画演示-MMAP + write 实现“零拷贝”:通过 mmap 建立了 PageCache 到用户进程的虚拟地址空间的映射,即用户进程可以直接通过 mmap 返回的 fd 直接读取 pagecache 对应的物理内存空间(四次上下文切换 + 三次数据拷贝(两次 DMA 拷贝 + 一次 CPU 拷贝))
    在这里插入图片描述

  3. 动画演示-sendfile 实现零拷贝:二次上下文切换 + 三次数据拷贝(两次 DMA 拷贝 + 一次 CPU 拷贝)
    在这里插入图片描述

注意:linux kernel 2.1 开始支持 sendfile

  1. 动画演示-sendfile + DMA gather copy 实现零拷贝:sendfile 利用 DMA 将文件内容拷贝到内核缓冲区(PageCache),然后将带有文件位置和长度信息的缓冲区描述添加到 socket 缓冲区(这一步不会将内核中的数据拷贝到 socket 缓冲区中),DMA 会直接将内核缓冲区的数据拷贝到网卡设备通过网络发送出去。(两次上下文切换 + 两次 DMA 拷贝)
    在这里插入图片描述

注意:linux kernel 2.4 才支持这种拷贝方式,并且需要硬件以及驱动程序的支持

  1. 动画演示-splice + DMA 实现零拷贝: splice 通过在 PageCache 和 Socket 缓冲区之间建立一个环型管道,这样DMA 可以直接通过 socket 缓冲区将 PageCache 中的数据拷贝到网卡设备(两次上下文切换 + 两次 DMA 拷贝)
    在这里插入图片描述

注意:linux kernel 2.6.17 才引入 splice。splice 利用 Linux 的管道缓冲区机制, 所以要求绑定的两端的描述中至少有一个为管道设备。

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

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

相关文章

记录三菱:Works2-计数器

参数设置&#xff1a;D200-D511掉电保持&#xff0c;这个范围可以更改 加减计数器 第一种&#xff1a; 第二种&#xff1a; 第三种&#xff1a; 例如&#xff1a;完成下面的功能 可以在触摸屏上仿真测试一下

unity学习(70)——编译游戏发生错误2

1.全屏问题其实无所谓&#xff0c;windows用tab可以切出来的。 2.现在主要问题是服务器try了以后虽然不崩溃了&#xff0c;但不再显示2个实例对象了&#xff0c;unity和exe此时都只能看到一个实例对象 2.1把之前报错位置的try-catch先注释掉 2.2 unity中此时登录666账号&…

2015年认证杯SPSSPRO杯数学建模D题(第二阶段)城市公共自行车全过程文档及程序

2015年认证杯SPSSPRO杯数学建模 D题 城市公共自行车 原题再现&#xff1a; 城市交通问题直接影响市民的生活和工作。在地形平坦的城市&#xff0c;公共自行车出行系统是一种很好的辅助手段。一般来说&#xff0c;公共自行车出行系统由数据中心、驻车站点、驻车桩、自行车&…

【Linux】信号量与信号

目录 先导知识 信号量 信号 信号概念及产生信号的一般方式 进程递达、阻塞和捕捉 信号集操作函数 信号的捕捉 可重入函数 先导知识 信号量与信号没有任何关系&#xff0c;它们是两个完全不同的概念&#xff01; 操作系统的本质&#xff0c;就是一个死循环&#xff1b;…

Django日志(四)

一、Filters介绍 过滤器用于从logger传递给handler的哪些日志要做额外控制 默认情况下,满足日志级别的任何消息都将处理。只要级别匹配,任何日志消息都会被处理。不过,也可以通过添加 filter 来给日志处理的过程增加额外条件。例如,可以添加一个 filter 只允许某个特定来源…

【C++】模板与泛型编程

文章目录 1. 泛型编程2. 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 3. 类模板3.1 类模板的定义格式3.2 类模板的实例化 4. 非类型模板参数5. 模板的特化5.1 概念5.2 函数模板特化5.3 全特化5.4 偏特化5.5 类模板…

Android 系统应用 pk8签名文件转jks或keystore教程

一、介绍 签名文件对于我们在做应用开发中&#xff0c;经常遇到&#xff0c;且签名文件不仅仅是保护应用安全&#xff0c;还会涉及到应用与底层之间的数据共享和API文件等问题。 在Android中&#xff0c;签名文件同样也存在这个问题。但是android中又区分系统应用和普通应用。系…

汉明校验·简明教程

汉明校验 一、简介 汉明码是由 Richard Hanming 于 1950 年提出的&#xff0c;它具有一位纠错能力。 新增的汉明码校验位数应满足如下关系&#xff1a; 2 k ⩾ n k 1 2^{k}\geqslant nk1 2k⩾nk1&#xff0c;其中k为校验位位数&#xff0c;n位数据位数。 二、汉明码生成 确…

centos7 的redis的安装

文章目录 查看本机redis⾸先安装 scl 源, 再安装 redis 基本配置启动redis停止redis 查看本机redis ⾸先安装 scl 源, 再安装 redis 安装scl源 yum install centos-release-scl-rh安装redis5 yum install rh-redis5-redis安装成功 基本配置 修改etc/redis/redis.conf 文件…

代码随想录算法训练营第二十一天(二叉树VII)| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先(JAVA)

文章目录 530. 二叉搜索树的最小绝对差解题思路源码 501. 二叉搜索树中的众数解题思路源码 236. 二叉树的最近公共祖先解题思路源码 530. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&a…

如何在 Ubuntu 安装桌面环境

在 Ubuntu 上安装不同的桌面环境 如果你正在使用官方的 Ubuntu 发行版&#xff0c;它运行在 GNOME 上&#xff0c;那么你可以很容易地从默认的包管理器安装其他流行的桌面环境&#xff08;DE&#xff09;。让我们开始吧… 在 Ubuntu 上安装 KDE Plasma 如果你正在使用 GNOME…

JAVA使用POI实现Excel单元格合并-02

JAVA使用POI实现Excel单元格合并 实现效果 解释&#xff1a;只要是遇见与前一行相同的数据就合并 引入jar <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></depe…

第114讲:Mycat实践指南:按照单位为月的日期实现水平分表

文章目录 1.按月分片的概念1.按月分片的概念 2.按照天数对某张表进行水平拆分2.1.在所有的分片节点中创建表结构2.2.配置Mycat实现字符串按月分片的水平分表2.2.1.配置Schema配置文件2.2.2.配置Rule分片规则配置文件2.2.3.配置Server配置文件2.2.4.重启Mycat 2.3.写入数据观察分…

ora-00314 00312

背景&#xff1a;某医院数据库打不开&#xff0c;alter database open报错&#xff08;跟我说是被勒索了。。&#xff09; 查看日志组信息&#xff1a; select group#,sequence#,archived,status from v$log;处理方法&#xff1a; 若该组是非当前状态&#xff0c;而且未归档&…

Kubernetes Pod深度解析:构建可靠微服务的秘密武器(上)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Kubernetes概述 2、Pod概述 二、Po…

FastAPI+React全栈开发02 什么是FARM技术栈

Chapter01 Web Development and the FARM Stack 02 What is the FARM stack and how does it fit together? FastAPIReact全栈开发02 什么是FARM技术栈 It is important to understand that stacks aren’t really special, they are just sets of technologies that cover…

Python学习:条件控制

Python条件控制概念 条件控制是编程中的一个重要概念&#xff0c;用于根据不同情况执行不同的代码逻辑。在Python中&#xff0c;条件控制通常使用if语句来实现。if语句的基本语法如下&#xff1a; if 条件:执行语句 elif 其他条件:执行语句 else:执行语句其中&#xff0c;if…

2016年认证杯SPSSPRO杯数学建模C题(第二阶段)如何有效的抑制校园霸凌事件的发生全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 C题 如何有效的抑制校园霸凌事件的发生 原题再现&#xff1a; 近年来&#xff0c;我国发生的多起校园霸凌事件在媒体的报道下引发了许多国人的关注。霸凌事件对学生身体和精神上的影响是极为严重而长远的&#xff0c;因此对于这些情况我们应该…

网络七层模型之网络层:理解网络通信的架构(三)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

基于傅里叶描述子和HSV颜色特征的KNN水果类型识别,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…