【Linux】玩转操作系统,深入刨析进程状态与调度机制

目录

  • 1. 进程排队
  • 2. 进程状态的表述
    • 2.1. 进程状态
    • 2.2 运行状态
    • 2.3. 阻塞状态
    • 2.4. 挂起状态
  • 3. Linux下具体的进程状态
    • 3.1. 运行状态R
    • 3.2. 可中断睡眠状态S
    • 3.3. 不可中断睡眠状态D
    • 3.4. 停止状态T
    • 3.5. 死亡状态X
    • 3.6. 僵尸状态Z
  • 4. 孤儿进程
  • 5. 优先级
  • 6. Linux的调度与切换
    • 6.1. 四个概念
    • 6.2. 进程切换
    • 6.3. 进程调度

1. 进程排队

  1. 进程排队:指的是操作系统将等待资源的进程组织成队列,以便按照特定的调度策略来决定哪个进程可以获取资源并继续执行,即:将进程放入到等待资源的队列中进行排队。

进程排队,表示进程不是一直在运行的,是在等待某种软硬件"资源"。eg:scanf,等待键盘资源。

即使进程被调度到CPU上执行,它也不会一直运行下去。在多任务操作系统中,进程的执行是分时的,每个进程会分配到一定的时间片来执行,当时间片用完,操作系统会暂停运行当前进程。

  1. 进程排队,一定是进程的task_struct(PCB)进行排队。

  2. 进程的task_struct(PCB)可以放入到多种数据结构中。

进程的PCB既可以被放入到全局的双链表中,又可以被放入到特定的队列中。当进程被放入到全局的双链表中,OS会在进程的每个PCB中创建链表节点的对象;当进程被放入到特定的队列中,OS会在队列的数据结构中创建或复用双链表节点,在PCB中设置队列节点的链接。

image.png

2. 进程状态的表述

前言:教材中描述进程状态的模型和实际操作系统(如:Linux)中实现的进程状态模型可能会有差异。

2.1. 进程状态

  1. 进程状态:本质是一个整形类型的变量,它定义在task_struct结构体中,用来描述进程的不同的不同状态。
  2. 进程状态,决定着进程的后续操作和它在操作系统的行为。

image.png

2.2 运行状态

运行状态:进程已经准备好随时被调度。即:正在CPU上执行以及在运行队列中等待被调度的进程。

💡Tips:一个CPU一个运行队列。
屏幕截图 2024-07-20 104921.png

2.3. 阻塞状态

阻塞状态:进程暂时无法继续执行,在等待某种软硬件"资源"。

💡Tips:每个硬件都有自己的队列!

  1. 当进程在等待软硬件资源的时候,资源没有就绪,操作系统就会先将进程的task_struct设置为阻塞状态、再将task_struct链入到等待资源的等待队列中。

  2. 状态的变迁,引起的是PCB会被操作系统变迁到不同的队列中!
    image.png

  3. 硬件的就绪状态,本质是硬件的资源准备好执行任务的状态,操作系统作为硬件的管理者,负责监控和管理这些硬件资源的状态。

硬件资源就绪,对于键盘的输入,通常是指键盘的中断机制已触发,表明有新的输入数据已经准备好了。

键盘的输入处理流程如下:硬件资源就绪(键盘输入准备完毕)→ OS通过驱动程序将数据从外设(键盘)搬到内存(内核输入缓冲区)→ 用户程序通过系统调用接口(scanf)将数据从内存搬到用户空间。

2.4. 挂起状态

挂起状态:进程被暂时停止执行,它的状态被保存,以便在将来某个时刻恢复。分为内部挂起、外部挂起。

  1. 外部挂起:也称为阻塞挂起。当系统资源紧张,如:内存空间不足,OS会将进程的代码和数据保存到磁盘的swap分区,以释放内存供其他进程使用,此时进程状态为挂起状态。当内存资源变的可用时,OS会将对应的代码和数据重新加载到内存中,并将其状态由挂起变为运行,以便它可以被调度执行。

  2. 唤出过程:当内存资源紧张时,OS会将内存中数据(等待资源的进程的代码和数据)交换到磁盘的swap分区中,以释放内存空间,这个过程称为唤出。

  3. 唤入过程:当需要恢复执行此进程时,就会将磁盘的swap分区保存的内容回到内存中,使进程可以继续执行,这个过程称为唤出。

image.png

问1:创建一个新进程,是先创建进程的PCB,还是先把进程的代码和数据加载到内存中?

答:先创建进程的PCB,再把进程的代码和数据加载到内存中,确保了OS对进程的创建和管理。

问2:磁盘的swap分区的大小是越大越好吗?

答:不是,会导致性能下降、资源浪费等问题。与内存相比,磁盘速度慢的多,如果swap分区很大,会导致OS过度依赖它,而频繁的唤入唤出操作会显著降低系统的性能,因为磁盘的IO速度远低于内存的读写速度。

  • swap分区不能设置过大,通常大小等于内存大小或内存大小的一半。

3. Linux下具体的进程状态

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

image.png

3.1. 运行状态R

运行状态R:进程正在运行或者准备运行。它可能正在CPU上执行,或者在运行队列中等待被调度。

在这里插入图片描述

  1. grep是单独指令,是程序,只有被运行,变成进程,它才会执行过滤操作,所以它的状态总是运行状态R。

  2. 前台进程(带+),可以被键盘(ctrl + c)终止;后台进程(不带+),不可以被键盘终止,可以通过发信号(kill -9 进程的PID),才可终止后台进程 ;前台进程 -> 后台进程:./程序 & 。

3.2. 可中断睡眠状态S

可中断睡眠S:也称为浅度睡眠或睡眠状态,意味着进程正在等待某个事件的完成,如: I/O操作、定时器到期(sleep函数)等,它可以被信号中断,如:ctrl + c、kill -9终止进程。

💡Tips:可中断睡眠睡眠S == 阻塞状态。

#include<stdio.h>
#include<unistd.h>
 
int main()
{
    while(1)
    {
        printf("I am a process, id: %d\n", getpid());
        sleep(1);
    }
}

在这里插入图片描述
问:为什么这个进程处于睡眠状态?

答:sleep函数会将进程由运行状态变为睡眠状态,等待指定的时间,进程会被OS唤醒。printf函数的执行速度通常是非常快的,因为CPU的执行速度很快,而printf是向显示器打印,需要访问外设,printf可能会等待I/O操作完成时使进程短暂的进入睡眠状态。

3.3. 不可中断睡眠状态D

不可中断睡眠D:也称为深度睡眠或磁盘休眠状态,它通常会等待硬件操作(如:磁盘I/O)完成,不能响应信号,直到它所等待的事件完成。

💡Tips:不可中断睡眠睡眠D == 阻塞状态。

eg:当一个进程发起磁盘的写入操作时,当内存资源十分吃紧,如果此进程处于睡眠状态S,OS就会将此进程杀掉,可能会导致数据丢失,所以此进程的状态被设置于不可中断睡眠状态D,OS不可以杀掉此进程,进程等待磁盘的结果以作出相应的应答。

3.4. 停止状态T

停止状态T:意味着进程已经被暂停或者正在被追踪,通常是因为接受到一个信号,或者是在调试器中被断点所拦截。

💡Tips:停止状态T == 阻塞状态。

  1. 可以通过发送SIGSTOP信号(kill -19)给进程来停止进程,通过发送SIGCONT信号(kill -18)让处于暂停状态的进程继续运行。

image.pngimage.png

3.5. 死亡状态X

死亡状态X:意味着进程已经终止,且资源已经被回收,它只是一个返回状态,不再存在于进程列表中。

3.6. 僵尸状态Z

僵尸状态:进程已经退出,但其父进程尚未调用wait( )来收集其退出信息。即: 进程是已退出但未清理的状态,等待父进程回收资源。

  1. 前提:子进程比父进程先退出。只要子进程退出了,父进程还在运行,但父进程没有读取子进程退出信息,子进程就进入Z状态,当父进程回收了子进程的资源,子进程由Z状态变为X状态。

处于僵尸状态的进程会一直保存在进程列表中。通常情况下,进程先变为Z状态,再变为X状态。

  1. 当一个Shell( bash )会话结束时,如果没有显示的调用wait( )回收子进程的资源,而Shell作为父进程,会自动调用wait( )来清理所有已经终止的子进程的资源,避免留下僵尸进程,后台进程除外。

  2. 当进程退出后,先会释放进程的代码和数据,进程的PCB不能被释放,因为它里面存储着结果数据,只有当父进程完成了资源的回收和状态的读取,PCB才会被释放。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<wait.h> 

int main()
{
    pid_t id = fork();
    if(id < 0) return 1;
    else if(id == 0)
    {
        //子进程
        int cnt = 5;
        while(cnt--)
        {
            printf("I am child process, pid: %d, ppid:%d\n",getpid(    ),getppid()); 
            sleep(1);
        }
        exit(0);
    }
    else
    {
        //父进程
        int cnt = 10;
        while(cnt--)
        {
            printf("I am father process, pid: %d, ppid:%d\n",getpid    (),getppid());
            sleep(1);
        }
    }
    wait(NULL);
                                                                     
    printf("father wait child done...\n");
    sleep(5);
}
//现象:父子进程同时运行5s, 5s后子进程退出(变Z状态), 父进程在运行5s, 5s后父进程回收子进程资源(子进程由Z->X状态), 5s后父进程退出

a. 为什么要有僵尸状态Z?

  • 创建进程是希望这个进程执行特定任务,子进程在完成工作后,往往需要向父进程报告结果或者状态信息(子进程必须由结果数据保存在自己的PCB中) , 从而使父进程能够根据子进程的执行结果作出适当的响应。

b. 什么是僵尸状态Z?

  • 进程已经退出,但是当前进程的状态需要自己维持住,供父进程读取。

c. 如果父进程不读取子进程的退出数据呢?

  • 僵尸状态的进程会一直存在,而维护退出状态本质是用数据维护,存储在进程的PCB,所以task_struct对象也要一直存在,而数据结构对象本身就要占用内存,会造成内存泄漏。

4. 孤儿进程

孤儿进程:父进程比子进程先退出,子进程就称为"孤儿进程"。

  • 孤儿进程被PID为1的进程(操作系统)领养,由OS回收其资源。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
    pid_t id = fork();
    if(id < 0) return 1;
    else if(id == 0)
    {
        //子进程
        int cnt = 10;
        while(cnt--)
        {
            printf("I am child process, pid: %d, ppid:%d\n",getpid(    ),getppid()); 
            sleep(1);
        }
        exit(0);
    }
    else
    {
        //父进程
        int cnt = 5;
        while(cnt--)
        {
            printf("I am father process, pid: %d, ppid:%d\n",getpid    (),getppid());
            sleep(1);
        }
    }                                                       
}

5. 优先级

优先级:CPU资源分配的先后顺序。

  1. 前提:进程要访问某种资源,进程通过一定的方式(排队),确认享受资源的先后顺序。

  2. 为什么要存在优先级?因为资源相对过少。

  3. 优先级本质是一个整数类型的变量,数值越小,优先级越高,具有优先执行的权力。

  4. 优先级PRI(new) = 优先级PRI(old) + nice。

Linux系统允许用户调整进程的优先级,但不能直接修改pri,而是修改进程的nice值。

nice值:是进程优先级的修正数据,!=进程优先级;优先级PRI(old)为默认优先级(值为80)。

  1. Linux优先级默认值为80,优先级的范围为[60 , 99],nice值的范围为[-20,19],一共40个。

Linux为什么调整优先级是要受限制的?

答:如果不受限制,则用户能够随意提高自己的进程优先级调整的非常高,那么很可能他们会过度提升自己的进程,使其始终处于高优先级的状态,导致常规进程难以得到CPU资源,产生进程饥饿现象。

💡Tips:任何分时系统(基于时间片进行轮转执行),确保了进程调度的公平性和系统的稳定性!

ps -l

  • 功能:显示当前登录用户的所有进程的信息。

XQO_I_HJQOJ~HJ{N96`XFI5.png
UID:代表执行者的身份、PID:进程的唯一标识符、PPID:子进程的父进程的唯一标识符、PRI:进程的优先级、NI:进程优先级的修正数据。

top -> r -> 输入进程的PID -> 输入nice值

  • 功能:用top命令更改已存在进程的nice值,从而调整进程的优先级。

优先级.gif

renice nice 进程的PID

  • 功能:更改已存在进程的nice值,从而调整进程的优先级。

6. Linux的调度与切换

6.1. 四个概念

现代操作系统可以分成不同类别,分时操作系统和实时操作系统是两种重要的类型。

对于分时OS,进程都是基于时间片进行轮转执行的。每个就绪进程在轮到自己时,将会获得一个时间片来执行,如果一个进程在时间片结束时还没有执行完,它就会立即被暂停,CPU将被分配给下个进程,造成多个进程在"同时"运行的假象。

对于实时OS,采用的是优先级驱动的调度策略,要求OS对用户有超高响应,其中一旦高优先级的进程一旦就绪,将立即抢占CPU执行,如:车载系统。

  1. 竞争性:系统中进程的数量多、而CPU资源少量,所以进程之间对于共享资源是具有竞争属性的。

  2. 独立性:多进程在运行期间互不影响。每个进程在执行时都有自己的私有执行环境,包括代码、数据等资源,它们各自独享自己的执行资源。

  3. 并行:多个进程在多个CPU下,分别同时进行运行。

  4. 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内,多个进程看起来同时执行。

6.2. 进程切换

进程切换 = 保护进程的硬件上下文 + 进程的硬件上下文恢复。是OS在多任务环境中实现进程间公平调度和并发执行的关键机制。

  1. 进程在运行的过程中,会产生大量的临时数据,小部分被存储在CPU的寄存器中,大部分被存储在内存中。

  2. 进程的硬件上下文:在某一时刻,CPU执行进程时,所有硬件状态信息的集合。如:CPU内部的寄存器中存储的所有临时数据等。

  3. 保存进程的硬件上下文:当一个进程的时间片结束、主动放弃CPU(等待某种资源scanf)、有更高优先级的进程就绪需要暂时停止执行,OS会保存该进程的硬件上下文到进程的PCB中。

  4. 进程的硬件上下文恢复:在保存完当前进程的上下文后,OS会选择下一个要运行的进程,如果该进程是第二次被调度,就会将该进程的PCB中上下文重新加载到CPU上,CPU会从上次的运行位置继续运行。

💡Tips:所有的保存都是为了最终的恢复,所有的恢复都是为了继续上次的运行位置继续执行。

  1. CPU中的寄存器从物理上是被所有进程共享,在任意时刻,CPU的寄存器由当前正在执行的进程独占使用,而寄存器内部保存的数据,是被该进程私有的。即:CPU中的寄存器只能有一套,寄存器内部保存的数据可以有多套。

💡Tips:寄存器 != 寄存器的内容。

6.3. 进程调度

在这里插入图片描述
背景:Linux的进程调度O(1)算法,需要考虑优先级、进程饥饿、以及效率等问题,这主要是通过是特定的数据结构和算法设计来实现的。

一、####数据结构

  1. queue[140]

这是个优先级数组(prio_array),它是一个长度为140的指针数组,每个元素包含一个链表头,该链表包含具有相同优先级的所有就绪进程,即:每个元素对应一个优先级队列,每个队列中的进程具有相同的优先级,它维护了140个优先级队列。

实时优先级:0~99,用于实时进程,以确保它们能够及时响应并完成任务。

普通优先级:100~139,通过nice值[-20,19]可以调整进程在该范围内的优先级,用于非实时进程,遵循时间片轮转调度策略,确保进程公平的共享CPU资源。image.png

  1. bitmap[5]

这是一个位图(prio_map),用于快速检测哪些优先级队列是非空的。它是一个5个整数的数组,每个整数有32个比特位,共有5*32=160个比特位 > 140,位图操作都是在比特级别进行的,时间复杂度为O(1),非常高效。

位图中的每个比特位的位置,对应着一个优先级队列;每个比特位的内容,表示这个队列是否为空。即:检测某个优先级队列中是否有进程,转为为检测对应比特位是否为1。

  1. nr_active

这是一个计数器,用于记录当前活跃进程队列中进程的总数。

struct q
{
    int nr_active;
    int bitmap[5];
    task_struct queue[140];
}

二、####算法实现

  1. 优先级

通过优先级数组和位图,调度器,可快速定位到最高优先级的非空队列。当需要调度下一个进程时,调度器首先会遍历位图,直到找到’第n位’为1才会停下,再访问queue数组中的下标值为’第n位’的元素,取出该队列中第一个进程进行运行。

  1. 避免进程饥饿

OS会将时间片耗尽的进程放到过期队列中,不让他们一直占用活跃队列的位置,确保其他进程被调度到CPU上执行;新增进程也会被加入到过期队列,不论这个新进程的优先级高低,避免了较高优先级的进程抢占CPU资源。而CPU只会执行活跃队列中的进程,当活跃队列上的进程都被执行完毕,两队列交换。

  1. 效率

因为bitmap[5]数组的存在,调度器只需要遍历bitmap[5]数组,而不是整个queue[140]数组,大大减少了遍历的次数。位图的查询操作时间复杂度为O(1),意味着无论多少个优先级队列,调度器只需要遍历bitmap[5],就可以快速找到非空优先级队列,大大提高了查找效率。

三、活跃进程队列、过期进程队列

  1. 活跃进程队列:包含了当前准备就绪,可以立即被执行的进程。

  2. 过期进程队列:包含了时间片耗尽,但未执行完毕的进程。

四、active指针、expired指针

  1. active指针永远指向活跃队列、expired指针永远指向过期队列。

  2. 进程的时间片一直存在,就会使活跃队列的进程一直在减少、过期队列的进程一直在增多。

struct q  
{
    int nr_active;
    int bitmap[5];
    task_struct queue[140];
}

struct q array[2]; 

struct q* active = &array[0];    //活跃队列
struct q* expired = &array[1];  //过期队列

swap(&active, &expired); //更改的只是指针变量的内容,队列内容并未改变

image.png

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

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

相关文章

第二证券:台风的后遗症竟然是台股!

今日早盘&#xff0c;中国台湾的台湾加权指数开盘大跌逾4%&#xff0c;台积电跌超6%。此前&#xff0c;因为飓风的原因&#xff0c;台湾股市暂停生意两天。暂停生意前&#xff0c;台湾加权指数收报22871点&#xff0c;上升614点&#xff0c;涨幅高达2.76%。 剖析人士认为&…

域名解析到ipv6,并用CF隐藏端口

要求&#xff1a;域名解析到 IPv6 地址并隐藏端口 ‍ 效果&#xff1a;用域名 https://myhalo.soulio.top​ 访问http://[2409:8a62:867:4f12:56c7:5508:f7x6:8]:8080​。唯一缺点是延迟有点高。 ​​ ‍ 难度&#xff1a;需要有一定域名解析、cloudflare使用基础 ‍ 实…

[数据集][目标检测]躺坐站识别检测数据集VOC+YOLO格式9488张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9488 标注数量(xml文件个数)&#xff1a;9488 标注数量(txt文件个数)&#xff1a;9488 标注…

【C/C++】内存管理

内存管理 一.C/C内存分布二.C内存管理方式1.new、delete操作内置类型2.new、delete操作自定义类型3.32位、64位机器的区别 三.operator new与operator delete函数四.new和delete的实现原理1.内置类型2.自定义类型1.new的原理2.delete的原理3.new T[N]的原理4.delete[]的原理 五…

5G赋能新能源,工业5G路由器实现充电桩远程高效管理

随着5G技术的广泛应用&#xff0c;新能源充电桩的5G应用正逐步构建起全新的生态系统。在数字化转型的浪潮中&#xff0c;新能源充电桩行业正迎来数字化改革。工业5G路由器的引入&#xff0c;为充电桩的远程管理提供了强有力的技术支持&#xff0c;新能源充电桩5G路由器网络部署…

MySQL窗口函数详解

MySQL窗口函数详解 MySQL从8.0版本开始引入了窗口函数&#xff0c;这是一个强大的特性&#xff0c;可以大大简化复杂的数据分析任务。本文将详细介绍MySQL窗口函数的概念、语法和常见用法&#xff0c;并结合实际应用场景进行说明。 什么是窗口函数? 窗口函数是一种能够对结…

docker搭建ES 8.14 集群

参考&#xff1a;【docker搭建es8集群kibana】_docker 安装生产级 es 8.14 集群-CSDN博客 1、之前已搭建一台单机版的dockerES集群 参见 Elasticsearch docker 安装_docker 安装es8.14.3-CSDN博客 2、现在需要重新搭建为docker ES集群 准备新搭建3个点 一、准备工作 提前开…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.3应用架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

计蒜客T3473(丑数) 优先队列,质因数,小题大做一下

文章目录 丑数思考过程代码 丑数 思考过程 其实现在这一题&#xff0c;我还没把代码敲完。但想赶紧把自己的思考过程给记录下来&#xff0c;不然一会儿&#xff0c;一些细节就捕捉不到了。因为才看了一个“广告”&#xff0c;关于抓住瞬间的灵感&#xff0c;虽然我这纯属是小儿…

分类常用的评价指标-二分类/多分类

二分类常用的性能度量指标 精确率、召回率、F1、TPR、FPR、AUC、PR曲线、ROC曲线、混淆矩阵 「精确率」查准率 PrecisionTP/(TPFP) 「召回率」查全率RecallTP/(TPFN) 「真正例率」即为正例被判断为正例的概率TPRTP/(TPFN) 「假正例率」即为反例被判断为正例的概率FPRFP/(TNFP)…

Visual Studio 2022美化

说明&#xff1a; VS版本&#xff1a;Visual Studio Community 2022 背景美化 【扩展】【管理扩展】搜索“ClaudiaIDE”&#xff0c;【下载】&#xff0c;安装完扩展要重启VS 在wallhaven下载壁纸图片作为文本编辑器区域背景图片 【工具】【选项】搜索ClaudiaIDE&#xff…

YOLOX+PyQt5交通路口智能监测平台设计与实现

1.概述 交通要道的路口上人车穿行&#xff0c;特别是上下班早高峰&#xff0c;且时常发生交通事故。因此对交通路口的车流量和人流量的监测必不可少。 2.检测模型 使用的检测模型为YOLOX模型&#xff0c;模型权重为训练VOC数据集得来&#xff0c;其中包括了二十个类别&#…

前端:Vue学习 - 购物车项目

前端&#xff1a;Vue学习 - 购物车项目 1. json-server&#xff0c;生成后端接口2. 购物车项目 - 实现效果3. 参考代码 - Vuex 1. json-server&#xff0c;生成后端接口 全局安装json-server&#xff0c;json-server官网为&#xff1a;json-server npm install json-server -…

【运算放大器】输入失调电压和输入偏置电流(2)实例计算

概述 根据上一篇文章的理论&#xff0c;分别计算没有输入电阻和有输入电阻两种情况下的运放总输出误差。例题来自于TI高精度实验室系列课程。 目录 概述实例计算 1&#xff1a;没有输入电阻实例计算 2&#xff1a;有输入电阻总结 实例计算 1&#xff1a;没有输入电阻 要求&am…

antdesgin table 组件下载成excel

文章目录 发现宝藏一、需求二、报错 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 一、需求 原组件如下&#xff0c;需要添加下载功能 import React, { useState } from rea…

React间的组件通信

一、父传子&#xff08;props&#xff09; 步骤 父组件传递数据&#xff0c;子组件标签身上绑定属性子组件接收数据&#xff0c;props的参数 // 子组件 function Son(props) {return (<div>this is Son, {props.name}</div>) }// 父组件 function App() {const n…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

谷粒商城实战笔记-60-商品服务-API-品牌管理-效果优化与快速显示开关

文章目录 一&#xff0c;显示状态列改为switch开关二&#xff0c;监听状态改变 首先&#xff0c;把ESLint语法检查关掉&#xff0c;因为这个语法检查过于严格&#xff0c;在控制台输出很多错误信息&#xff0c;干扰开发。 在build目录下下webpack.base.conf.js中&#xff0c;把…

matlab实验:实验六MATLAB 数值计算与符号运算

题目1&#xff1a;&#xff08;线性方程组数值求解&#xff09; 1&#xff0e; 用不同的方法求解下面方程&#xff1a;&#xff08;方程原式参考 P369 实验 10&#xff0c;第 1 题&#xff09; 第 1 种&#xff0c;左除和求逆函数(inv) 第 2 种 &#xff0c; 用 符 号 运 算 的…