初始计算机操作系统——进程与线程,多线程以及Thread类的创建和属性

news2024/11/28 2:43:51

目录

通过前半篇文章需要了解 

1.进程(process/task):运行起来的可执行文件。

为啥要有进程?

如何解决这个问题?

 (1)进程池:

 (2)使用线程:

为啥线程比进程更轻量?

2.线程:线程是进程内的一部分......进程包含线程。

进程与线程之间的区别与联系:

3.操作系统如何管理进程?

4.注:

5.PCB中的一些基本属性

6.进程之间的通信 

7. Java 的线程 和 操作系统线程 的关系

8.Thread类

继承thread类并且重写run方法

实现Runnable接口 

匿名内部类,继承Thread

匿名内部类,实现Runnable

使用Lambda表达式 

Thread中的常见属性 

Thread 的几个基本属性

isDaemon()是否后台线程

isAlive() 是否存活

isInterrupted() 是否被中断

等待线程(join)

获取当前线程的引用

线程的休眠sleep

start和run的区别

9.明天 


通过前半篇文章需要了解 

1.进程是啥?

2.进程时如何进行管理的?描述+组织

3.进程的PCB里有啥?

4.进程的调度咋进行?时间管理大师~ 

5.进程的独立性是咋回事?

6.进程之间如何进行通信?

7.进程和线程的区别和练习

操作系统居然是搞管理的软件,对上,要管理好各种硬件设备,对下,要对各种软件提供稳定的运行环境。

1.进程(process/task):运行起来的可执行文件。

为啥要有进程

我们的系统支持多任务,程序员就需要并发编程。

通过多进程是完全可以实现并发编程的 

如果频繁的创建/销毁进程(创建进程就需要给进程分配资源1.内存,2.文件(对于资源的申请和释放本身就是一个比较低效的过程)),这件事情成本是比较高的如果需要频繁的调度进程,这件事成本也是比较高的,

如何解决这个问题?

 (1)进程池

虽然可以解决上述问题,提高效率,同时也有问题。即使不使用,也在消耗系统资源,消耗的系统资源太多

 (2)使用线程:

线程比进程更轻量,每个进程能够执行一个任务,每个线程也能执行一个任务(执行一段代码),也能够并发编程。创建线程的成本比创建进程的成本低很多,销毁线程的成本比销毁进程低很多,调度线程的成本比调度进程低很多。在Linux上也把线程称为轻量级进程。

为啥线程比进程更轻量?

进程重在资源申请与释放,线程是包含在进程中的,一个进程中包含多个线程,共同用同一份资源(同一份内存+文件),只是在创建进程的第一个线程的时候(由于要分配资源,成本是相对高的)。后序这个进程再创建其他进程的时候,成本都是要更低一些。(不必再分配资源了)。

但是多加线程,效率并不一定会高,当线程多了,这些线程可能会竞争同一个资源,整体的速度就会受到限制。

2.线程:线程是进程内的一部分......进程包含线程。

一个线程就是一个 "执行流 ". 每个线程之间都可以按照顺讯执行自己的代码 . 多个线程之间 " 同时 " 执行 着多份代码.

对于java代码来说,最终都是通过java进程(jvm)跑起来的。

进程与线程之间的区别与联系:

1.进程包含线程,一个进程之间可以有一个线程,也可以有多个线程;

2.进程和线程都是为了处理并发编程这样的场景;

但是进程在频繁创建和释放资源以及调度效率低,开销大。相比之下,线程更轻量,创建和释放的效率更高(为啥更轻量,为了申请释放资源的过程)

3.操作系统创建进程,要给进程分配资源,进程是操作系统资源分配(系统分配资源如:内存,文件资源)的基本单位,

操作系统创建的线程,是要在CPU上调度执行,线程是操作系统调度执行的基本单位(CPU),(一个线程就是一个pcb)

4.进程有独立性,每个进程有独自的虚拟地址空间,一个进程挂了不会影响其他进程;

同一个进程中的多个线程,共同用同一个内存空间,一个线程挂了,可能会影响其他线程

(同一个进程中的若干线程之间,共享着内存,资源,文件描述符表, 每一个线程被调度执行都有自己的状态,上下文,记账信息); 

3.操作系统如何管理进程?

(1)先描述一个进程(明 确出一个进程上面的相关属性);里面主要通过C/C++实现,操作系统中描述进程的这个结构体,称为PCB;

(2)再组织若干个进程(使用一些数据结构,把很多描述进程的信息放在一起,方便增删查改--eg:linux通过PCB通过双向链表将每个进程的PCB串起来);

4.注:

"创建进程":先创建出PCB,然后把PCB加到双向链表中;

"销毁进程":找到链表中的PCB,并且从链表中删除;

"查看任务管理器":遍历链表;

5.PCB中的一些基本属性

pid(进程id): 进程的身份标识~进程的身份账号;

内存指针:知名这个进程要执行的代码/指令在内存的哪里,以及这个进程执行中依赖的数据在哪。当运行一个exe,操作熊会把这个exe加载到内存中,变成进程。内存中包含了进程要执行的二进制指令(通过编译器生成),除了指令之外还有一些数据。

文件描述符表程序运行过程中,经常要和文件打交道,文件是在硬盘上的。

进程每次打开一个文件,就会再文件描述表上多增加一项;这个文件描述表就可以视为一个数组,里面的每个元素又是一个结构体,就对应一个文件的相关信息。

一个进程只要一启动,不管代码中是否写了打开/操作文件的代码,都会默认打开三个文件(系统自动打开的,分别是:标准输入System.in,标准输出System.out,标准错误Systmp.err);

这个文件描述符表的下标,就被称为文件描述符

要想让一个进程正常工作就得给他分配资源:如内存,硬盘,CPU;


上面的属性是一些基础属性,下面的属性主要是为了能够实现进程调度(是理解进程管理的重要话题)。(现在的操作系统一般是"多任务操作系统")

进程调度:系统上任务的数量(进程的数量)很多,但CPU核数有限。

并行:微观上,两个CPU核心,同时执行两个任务的代码;

并发:微观上,一个CPU核心,先执行一会任务1,在执行一会任务2......再继续执行任务1,只要切换足够快,宏观上看起来,就好像多任务在同时执行......

实际上通常使用并发来代指并行+并发,只在研究操作系统稍作区分。

所谓"调度"就是“时间管理”, 在并发过程中,规划时间表的过程,就是调度的过程


状态:描述了当前这个进程接下来应该如何 调度;

就绪状态:随时可以去CPU上执行;

阻塞状态/睡眠状态:暂时不可以去CPU上执行;

优先级:先给谁分配时间,后给谁分配时间,以及给谁分的多,给谁分的少

上下文: 示了上次进程被调度出CPU时,当时程序的执行状态,下次进CPU就可以恢复之前的状态,然后继续向下执行。

进程被调度出CPU之前,要先把CPU中所有的寄存器中的始数据都保存在内存中(PCB当中的上下文字段中)相当于存档;

下次进程在被调度上CPU的时候就可以从刚才的内存中回复这些数据到寄存器中,相当于读档, 存档+读档~,就称为上下文;

记账信息:统计了每个进程,都分别被执行了多久,分别执行了那些指令,分别排队等了多久.......给进程的调度提供了指导依据

6.进程之间的通信 

早期的操作系统,里面的进程都是访问同一个内存的地址空间,如果有一个进程出现bug,把内存的数据写错了,就可能引起其他进程崩溃。(如果将进程按照虚拟地址空间的方式划分出很多分,这时候虽然系统中有百八十个进程,但实际上从微观上看,同时执行的进程就6个)

因此 ,现在的操作系统就是“进程的独立性”来保证的,就依仗了"虚拟地址空间"(进程之间现在通过"虚拟地址空间"隔开,但是在实际工作中,进程有的时候还是需要交互的);

两个进程之间,也是隔离开来的,也是不能直接交互的,操作系统也是提供了类似的”公共空间“,进程A就可以把数据放在公共空间上,进程B再取走。

进程间通信!操作系统中也提供了多种进程间通信机制。常用的1.文件操作;2.网络操作(socket);

7. Java 的线程 和 操作系统线程 的关系

线程是操作系统中的概念 . 操作系统内核实现了线程这样的机制 , 并且对用户层提供了一些 API 供用户使 用( 例如 Linux pthread ).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进一步的抽象和封装 .

8.Thread类

Java进行多线程编程 ,在Java标准库中就提供了Thread类,表示/操作线程 ;

Thread类也可以视为Java标准库提供的API ;

创建好的thread实例,和操作线程中的线程是一一对应的关系 ;

操作系统提供了一组关于线程的API,Java对这组API进一步封装就成了thread类;

可以通过jdk自带的工具jconsole查看当前Java进程中所有线程;

继承thread类并且重写run方法

class Mythread extends Thread{
    @Override
    public void run() {//当run方法执行完毕,新的这个线程自然销毁;
        super.run();
        System.out.println("hello thread");
        //run方法里的逻辑是在新创建出来的线程里被执行的代码
        //‘分配活‘
        //这里的创建线程都是在同一个进程内部创建的
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread t=new Mythread();
        t.start();
        //start里面没有调用run,start是创建了一个新的线程,新的线程负责执行run方法;
        //新的线程就是调用操作系统的API,通过操作系统内核创建新线程的PCB,并且把要执行的指令交给这个PCB,
        //当PCB被调度到CPU上执行的时候,也就到了线程run方法中的代码了

        //操作系统调度线程的时候是抢占式执行的

    }
}//new thread()并不是创建线程,说的线程是系统内核里的PCB;
//调用start才是创建pcb(真正的线程)
//一个线程对应一个pcb;
//同一个进程中PCB的pid相同,不同进程的pid是不同的
//PCB不是一个简称,PCB是一个数据结构,体现的是进程或者线程是如何实现的,如何被描述出来的

实现Runnable接口 

//Runnable是描述一个“要执行的任务”,run方法就是任务的执行细节
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("hello");
    }
}
public class Demo3 {
   

    public static void main(String[] args) {
        //这只是描述了这个任务
        Runnable runnable=new MyRunnable();
        //把任务交给线程来执行
        Thread t=new Thread(runnable);
        t.start();
   
    }
}

解耦合,就是为了让线程和进程干的活分离开 

匿名内部类,继承Thread

    public static void main(String[] args) {
        Thread t=new Thread(()->{
            System.out.println("hello");
        });
        t.start();
    }

匿名内部类,实现Runnable

   public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }

使用Lambda表达式 

    public static void main(String[] args) {
        Thread t=new Thread(()->{
            System.out.println("hello");
        });
        t.start();
    }

Thread中的常见属性 

 

 

Thread 的几个基本属性

getId() Id  ID 是线程的唯一标识,不同线程不会重复
getName()名称
getState()状态
getPriority()优先级

isDaemon()是否后台线程

是否是守护线程//

前台进程会阻止进程结束,前台进程工作没完,进程是完不了的;
后台进程不会阻止进程的结束,后台线程工作没完,进程也是可以结束的;
代码中手动创建的线程,默认都是前台的;包括main也是的其他jvm自带的线程都是后台的;
也可以 使用setDeamon设置成后台线程(守护线程)(被设置的这个线程就与t无关了) , 
关于后台线程,需要记住一点: JVM 会在一个进程的所有非后台线程结束后,才会结束运行
是否存活,即简单的理解,为 run 方法是否运行结束了

isAlive() 是否存活

就是判断是否真的有线程,
调用start后,才会在内核创建一个PCB,此时的一个PCB才表示一个真正的线程,此时isAlive()才是true;
另外,如果内核把线程把run干完了,此时线程销毁,pcb随之销毁,但是Thread t这个对象不一定释放 此时isAlive()还是false;
t神魔时候不在了?当引用不指向这个对象,被GC回收;
run方法在执行的时候 isAlive ()->true;
执行完->false

isInterrupted() 是否被中断

中断一个线程,不是让线程立刻停止,而是通知线程需要停止了,是否真的停止,取决于具体代码的书写方式;

为true,表示被终止;

为false,表示为被终止;

1.自定义方式

缺点就是不能及时响应,尤其是在sleep休眠的时候比较久的情况

//使用标志 
public static boolean flg=true;
    public static void main(String[] args) throws InterruptedException {
        
        Thread t=new Thread(()->{
            while(flg){
                System.out.println("hello thread");
            }
            try{
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        Thread.sleep(3000);
        flg=false;
    }

2.使用Thread自带的标志位

这种的是可以唤醒sleep的

使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定
义标志位.
(Thread.currentThread()是Thread的静态方法,通过这个对象可以获取到当前的线程,哪个方法调用线程,得到的就是哪个线程对象的调用)
Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记 .
在main中调用t.interrupted(),就相当于main通知t要终止了;

    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("hello thread");
                try{
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();//把boolean操作封装到Thread的方法里了
    }

如果线程在sleep中休眠 ,此时调用interrupt会把t唤醒 ,从sleep中提前返回 ;

interrupt会做两件事情 ,

1.把线程内部的标志位设置成true;

2.如果线程在进行sleep ,就会触发异常,把sleep唤醒;

但是sleep在唤醒的时候 ,还会把刚才设置的标志位再设置回false(清空标志位);

像wait,join这样的造成代码暂停的方法都会有类似的清除标志位的设定

所以这个时候执行 ,还可能会导致sleep中被interrupted之后 ,还会继续执行,即线程t忽略了终止请求;

catch里面可以加上break(也可以根据需求添加sleep)/catch里面改成throw new RuntimeException(e);让线程t立即相应你的终止请求

so , 唤醒之后到底让线程立即终止还是稍后 ,就把选择权交给程序员自己了 。


为啥不涉及成A让B终止 ,B就终止呢 ? A,B之间是并发执行的,随即调度的 ,导致B这个线程执行到哪了,A都是不清楚的 。


等待线程(join)

线程是一个随机调度的过程,等待线程就是控制两个线程之间的结束顺序 。

public class demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        t.start();
        System.out.println("join之前");
        t.join();
        //这里的join是让main等待t执行结束;(等待t的run执行完)
        //本身执行完start之后,t线程和main线程就并发执行,分头执行main继续向下执行,t也继续向下执行就会发生阻塞block
        //一直阻塞到t线程执行完,main才能从join里面恢复过来
        //必然是t先结束
        System.out.println("join之后");
    }
}

——> main线程中的打印“join之前”执行,遇到join,等待t线程执行完后才执行后续的;


 

😅如果执行join的时候 ,t 已经结束了,join 就不会阻塞 ,立即返回 ;

 so,有 join 也不一定必须得等待 ;


获取当前线程的引用

public static Thread currentThread();
返回当前线程对象的引用
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

线程的休眠sleep

1.原本链表中的PCB都是随叫随到的就绪状态

2.线程A调用sleep, A就会进入休眠状态,把A从上述链表拎出来,放到另一个链表上,这个链表的PCB都是“阻塞状态”,暂时不参与CPU的调度执行。

pcb是使用链表来组织的(并不具体)

3.操作系统每次需要调度一个线程去执行,就是从就绪队列中选一个就好

一旦线程进入阻塞状态,对应的PCB就进入阻塞队列,就暂时无法参与调度了

eg: 调用sleep(1000) , 对应的线程PCB就要再阻塞队列中待1000ms ,当PCB回到了就绪队列,会被立即调度吗?

虽然sleep(1000),考虑到调度的开销,对应的线程是无法在唤醒之后立即被执行的,实际的时间大概率要大于1000ms的 。

start和run的区别

start是真正创建了一个线程,线程是独立的执行流;

run只是描述了要干什莫活,如果直接在main中调用run,此时没有创建新的线程,全是main在干活 

9.明天 

学线程的状态以及多线程带来的风险(线程安全)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国考省考行测:判断推理,类比推理1,概念关系,包含关系,交叉关系,并列关系,全同关系

国考省考行测&#xff1a;判断推理&#xff0c;类比推理1&#xff0c;概念关系&#xff0c;包含关系&#xff0c;交叉关系&#xff0c;并列关系&#xff0c;全同关系 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#x…

chatgpt赋能Python-libreoffice_python扩展

LibreOffice Python扩展: 提升办公效率的利器 如果你一直在寻找一种提高办公效率的方法&#xff0c;那么你肯定会喜欢LibreOffice Python扩展。作为LibreOffice的一个特性&#xff0c;它可以让你使用Python编写宏程序自动化你的日常办公任务。 什么是LibreOffice Python扩展&…

力扣sql中等篇练习(二十一)

力扣sql中等篇练习(二十一) 1 最大数量高于平均水平的订单 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 a 示例输入 b 示例输出 1.2 示例sql语句 # Write your MySQL query statement below WITH t1 as (SELECT order_id,avg(quantity) AquantityFROM OrdersDeta…

chatgpt赋能Python-numpy数据预处理

Numpy数据预处理综述 介绍 Numpy是Python中最流行的数学库之一&#xff0c;可以用于高效的处理大型数据。Numpy提供了各种强大的数据结构和函数&#xff0c;使得数据分析和处理变得更加容易和直观。本文将介绍numpy中的一些数据预处理技术&#xff0c;包括数据清洗、缩放、归…

chatgpt赋能Python-mingw编译python

Mingw编译Python&#xff1a;一种常用的解决方案 在Python开发中&#xff0c;为了获得更好的性能&#xff0c;我们通常会选择编译Python源代码。而在Windows平台上&#xff0c; Mingw编译器是一种常用的解决方案。本文将介绍Mingw编译Python的过程&#xff0c;并探讨其优缺点。…

chatgpt赋能Python-numpy创建

Numpy&#xff1a;Python中的数学计算利器 作为Python中进行数学计算和科学计算最重要的库之一&#xff0c;Numpy已经成为了Python编程中的标配。Numpy以其出色的数组处理能力和矩阵运算效果&#xff0c;让Python用户的数学计算和科学计算变得更加简单高效。在本篇文章中&…

【半监督学习】Match系列.4

介绍几篇关于半监督学习的论文&#xff1a;CLS&#xff08;arXiv2022&#xff09;&#xff0c;Ada-CM&#xff08;CVPR2022&#xff09;&#xff0c;SemiMatch&#xff08;CVPR2022&#xff09;. CLS: Cross Labeling Supervision for Semi-Supervised Learning, arXiv2022 解…

mysql增量备份

目录 一、修改配置文件&#xff0c;开启增量备份功能 &#xff08;1&#xff09;查看是否已经开启了 &#xff08;2&#xff09;修改配置文件开启 &#xff08;3&#xff09;增量记录文件 二、还原增量备份 &#xff08;1&#xff09;修改了数据 &#xff08;2&#xff…

使用thrift进行RPC通信(附c程序示例)

前言 为了实现不同语言的程序跨进程、跨主机通信&#xff0c;一般可以采用mq或rpc框架来实现。 对于异步通知的场景可以使用mq&#xff0c;如zeroMQ。 但对于某些实时性较强且同步的应用场景&#xff0c;使用成熟的rpc框架来实现也是一种比较更好的选择。 开源的rpc框架有很…

MySQL---游标,异常处理,循环构建表

1. 游标 游标(cursor)是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用光标对结果集进行 循环的处理。光标的使用包括光标的声明、OPEN、FETCH 和 CLOSE. -- 声明语法 declare cursor_name cursor for select_statement -- 打开语法 open cursor_name -- 取值语…

由浅入深Netty基础知识NIO三大组件原理实战

目录 1 三大组件1.1 Channel & Buffer1.2 Selector1.3 多线程版设计1.4 多线程版缺点1.5 线程池版设计1.6 线程池版缺点1.7 selector 版设计 2 ByteBuffer2.1 ByteBuffer 正确使用姿势2.2 ByteBuffer 结构2.3 调试工具类2.4 ByteBuffer 常见方法2.4.1 分配空间2.4.2 向 buf…

chatgpt赋能Python-numpy查找

Numpy查找 - 了解numpy中的查找功能 什么是Numpy&#xff1f; Numpy是Python语言中的一种开源的数学计算库&#xff0c;允许开发者轻松高效地进行数学运算。它提供了一整套矩阵运算方式&#xff0c;支持各种各样的数学函数和数据类型&#xff0c;并且可以与其他Python库良好地…

chatgpt赋能Python-macbook怎么用python

使用MacBook进行Python编程的完全指南 如果您是一名Python编程工程师&#xff0c;那么您需要一台性能良好的电脑来进行编程工作。今天&#xff0c;我们将探讨如何使用MacBook来编写Python代码&#xff0c;以及如何使您的Mac运行最佳状态。 安装Python 在开始使用Python之前&…

还在老一套?STM32使用新KEIL5的IDE,全新开发模式RTE介绍及使用

Keil新版本出来了&#xff0c;推出了一种全新开发模式RTE框架( Run-Time Environment)&#xff0c;更好用了。然而网上的教程资料竟还都是把Keil5当成Keil4来用&#xff0c;直接不使用这个功能。当前正点原子或野火的教程提供的例程虽有提到Keil5&#xff0c;但也是基本上当Kei…

Qt Quick系列(1)—开发界面以及相关文件介绍

作者&#xff1a;CCAccept 专栏&#xff1a;Qt Quick 文章目录 开发界面相关文件介绍.pro文件.pri文件&#xff08;这个一般要稍微大一点的Qt项目才会用到&#xff09;main.cppmain.qml 开发界面 如何具体的写代码实现Qt Quick的UI界面&#xff0c;首先我们需要新建一个空的…

Java面向对象程序设计实验报告(实验三 继承的练习)

✨作者&#xff1a;命运之光 ✨ 专栏&#xff1a;Java面向对象程序设计实验报告 ​ 目录 ✨一、需求设计 ✨二、概要设计 ✨三、详细设计 ✨四、调试结果 ✨五、测试结果 ✨附录&#xff1a;源程序代码&#xff08;带注释&#xff09; demo3类 Person类 Student类 …

[架构之路-201]-《软考-系统分析师》- 关键技术 - 结构化分析方法与面向对象分析(分析与设计的区别)

目录 前言&#xff1a; 一、分析与设计的区别 二、结构化分析方法 2.1 实体关系图&#xff1a;E - R 图 &#xff08;名词&#xff09; 2.2. 数据流图&#xff08;数据的流动&#xff09; (1) 顶层图。 (2) 逐层分解。 2.3. 状态转换图&#xff08;动作&#xff09; …

电压比较器

电压比较器&#xff1a; 应用一&#xff1a;过压&#xff0c;低压检测&#xff08;over and undervoltage detectors&#xff09; 如果Vin<Vs&#xff0c;则VoutVpullup。 如果Vin>Vs&#xff0c;则Vout0V。 应用二&#xff1a;窗口比较器&#xff08;window comparat…

Zadoff-Chu序列

ZC序列的定义 ZC序列有两个重要的参数&#xff1a; 根索引&#xff08;root index&#xff09; q 1 , 2 , ⋯ , N z c − 1 q1,2,\cdots,N_{zc}-1 q1,2,⋯,Nzc​−1ZC序列的长度 N z c N_{zc} Nzc​&#xff0c;一定得是奇数&#xff08;常常是质数&#xff09; 给定上述两…

网络:网络分层与协议/OSI七层模型/(TCP/IP模型)

一、简单理解 OSI模型(Open System Interconnection)&#xff1a; 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般…