操作系统_进程

news2024/7/6 0:15:32

操作系统_进程

  • 1 冯•诺依曼体系结构
  • 2 操作系统(Operator System)
    • 2.1 设计OS的目的
    • 2.2 OS的定位
  • 3 进程
    • 3.1 什么是进程?
    • 3.2 查看进程
    • 3.3 通过fork创建进程
      • 使用 if 进行分流
      • 如何杀死一个进程
    • 3.4 进程状态
      • R - 运行状态
      • S - 浅睡眠状态
      • D - 磁盘休眠状态
      • T - 停止状态
      • X - 死亡状态
      • Z - 僵尸状态
      • 模拟僵尸进程
      • 孤儿进程
      • T and t
      • 退出状态
      • 销毁僵尸进程
      • 僵尸进程和孤儿进程区别
    • 3.5 进程优先级
      • PRI and NI
  • 4 其他概念

1 冯•诺依曼体系结构

所谓冯•诺依曼体系结构,它是一个我们对应的模板。就相当于以后大家在做计算机可以按照我的这个方式来做。它的硬件构成分为输入设备、输出设备,还有中央处理器。中央处理器又分为运算器控制器

注:这里的存储器指的就是内存

常见的输入输出设备和中央处理器:

  • 输入设备:键盘、鼠标、网卡、磁盘、话筒、摄像头、扫描仪等。

  • 输出设备:显示器、音响、网卡、磁盘、打印机等。

  • 中央处理器 (CPU) :

运算器:算数运算、逻辑运算

控制器:CPU是可以响应外部事件的,可以协调外部就绪事件,如拷贝数据时,CPU要对硬件中断做出响应

注意: 同种设备在不同场景下可能属于输入设备,也可能属于输入设备。

我们经常说CPU当中有寄存器,实际上寄存器不仅仅在CPU当中存在,在其他外设当中也是有寄存器的。例如,当我们敲击键盘时,键盘是先将获取到的内容存储在寄存器当中,然后再通过寄存器将数据刷新到内存当中。

根据冯•诺依曼体系结构图,我们可以知道,站在硬件角度或是数据层面上,CPU只和内存打交道,外设也只和内存打交道。到这里我们有一个问题:为什么程序运行之前必须先加载到内存?输入设备直接把数据给CPU处理,处理完直接输出不是更快吗?

  1. 技术角度

CPU的运算速度 > 寄存器的速度> Cache > 内存 >> 外设 >> 光盘磁带

木桶原理都知道,一套系统的快慢是由最慢的设备决定的。如果没有内存,外设直接和CPU交互,那么这些设备就会拖慢整个系 统的运算速度。所以内存在我们看来就是起到缓存的作用,目的是适配外设和 CPU运算速度不均的问题。

我们的操作系统就可以把磁盘上的数据提前加载到内存,CPU就可以直接到内存拿数据,而不是磁盘。木桶的短板从外设变成了内 存,这样就可以在一定程度大大缓解整机的效率问题。

  1. 成本角度

寄存器 >> 内存 >> 磁盘

内存是断电失效的,硬盘不是。

CPU读取数据(数据+代码),都是从内存读取。CPU要处理数据,操作系统或者软件就会先将外设中的数据加载到内存。所以,站在数据的角度,CPU和外设都只与内存打交道

从输入设备输入数据到内存的过程叫做Input,从内存读到输出设备的过程叫做Output,整体就是一次IO过程。比如:scanf 和 printf就是从键盘输入到内存,再从内存输出到显示器。(有些数据被处理完不会直接打印到屏幕,而是加载到缓冲区,再定时或强制刷新到屏幕)

我们所写的C/C++程序,运行之前要先加载到内存,这是由硬件结构所决定的

2 操作系统(Operator System)

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(例如函数库, shell程序等等)

2.1 设计OS的目的

对下与硬件交互,管理所有的软硬件资源 – 手段
对上为用户程序(应用程序)提供一个良好的执行环境 – 目的

在这里插入图片描述

对下与硬件交互

此时这里有一个问题:难道操作系统直接和底层硬件打交道吗?
举个例子,如果操作系统自己来完成键盘的读取操作,那么只要你的键盘读取方式进行了改变,那么操作系统的内核源代码就需要进行重新编译,
这对操作系统来说维护成本太高了。
于是我们又在操作系统与底层硬件之间增加了一层驱动层,驱动层的主要工作就是单独去控制底层硬件的。例如,键盘有键盘驱动,网卡有网卡驱动,硬盘有硬盘驱动,磁盘有磁盘驱动。驱动简单来说就是去访问某个硬件,访问这个硬件的读、写以及硬件当前的状态等等,驱动层就是直接和硬件打交道的。而驱动一般是由硬件制造厂商提供的,或是由操作系统相关的模块进行开发的(例如网卡)。

此时操作系统就只需关心何时读取数据,而不用关心数据是如何读取的了,也就是完成了操作系统与硬件之间的解耦。

那操作系统究竟管理些什么呢?操作系统主要进行以下四项管理:

  1. 内存管理:内存分配、内存共享、内存保护以及内存扩张等等。
  2. 驱动管理:对计算机设备驱动驱动程序的分类、更新、删除等操作。
  3. 文件管理:文件存储空间的管理、目录管理、文件操作管理以及文件保护等等。
  4. 进程管理:其工作主要是进程的调度。

对上与软件交互

而操作系统再往上就是我们所处的位置,在这里我们就可以用命令行或是图形化界面进行各种操作,这一层被称为用户层。

但操作系统为了保护自己,对上只暴露了一些接口,而不会让用户直接访问操作系统,这一系列接口被称为系统调用接口。

但这些系统调用接口对我们普通用户来说使用成本又太高了,因为要使用系统调用前提条件是你得对系统有一定了解。所以在系统调用接口之上又构建出了一批库,例如libc和libc++。实际上在语言级别上使用的各种库,就是封装了系统调用接口的,我们就是通过调用这些库当中的各种函数(例如printf和scanf)进行各种程序的编写。

2.2 OS的定位

在整个计算机软硬件架构中,操作系统就是一款进行软硬件资源管理的软件

管理的本质就是对数据进行管理

管理的核心理念:先描述,再组织(先定义结构,再用数据结构和算法进行管理)

如何做到管理呢?

只要拿到被管理者的核心数据,来进行支持管理的决策,就可以了进行管理和决策了。

由于Linux操作系统是用C语言写的,所以我们可以肯定在Linux内核代码中有大量的 struct 结构体,链表或其他数据结构。

系统调用和库函数概念

  • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
  • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

3 进程

在Windows下,我们双击启动一个软件或者程序,本质就是启动了一个进程!

在Linux下,运行一条命令(如:./xxx),本质就是在系统上创建了一个进程!

我们加载到内存的C/C++程序就是一个进程!

当内存中存在大量的进程是吗,操作系统就需要对进程进行管理。

3.1 什么是进程?

进程 = 代码和数据 + 该进程对应的内核数据结构(PCB)

有了进程我们就要进行管理(也就是上面的内核数据结构)

就是我们熟悉的 PCB (进程控制块)在Linux下就是 task_struct

对进程的管理,就变成了对进程PCB结构体链表的增删查改

来一个进程,就会新建一个PCB,当进程结束的时候,对应的PCB也会销毁。

怎么多进程在内存中,该先执行哪个呢?

在PCB中是有进程优先级的,找到优先级高的那个进程,然后将该进程的代码和数据加载到CPU就可以了。

在内核中,操作系统将进程PCB按照链表的形式串接起来,对进程的管理实际上就是对PCB双向链表的增删查改。

在这里插入图片描述

task_ struct 内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

3.2 查看进程

  1. 大多数进程信息同样可以使用 top 和 ps 这些用户级工具来获取

    在Linux中,想查看一个进程信息的命令叫做ps,,默认只能查看你自己终端下的进程;
    在这里插入图片描述

可以用命令查看所有进程。

    ps axj

在这里插入图片描述

我们先写一个C++程序为“myproc”,可以使用grep命令来查看。

在这里插入图片描述
加上头部信息
在这里插入图片描述

PID进程的ID
PPID父进程
TTY终端ID 正在等待的进程资源
STAT进程状态
PGID进程组ID
TPGIDtty进程组负责人的进程组ID
SID会话负责人的会话id
  1. 进程的信息可以通过 /proc 系统文件夹查看

    proc:内存文件系统(当前系统实时的进程信息)
    在这里插入图片描述

    我们可以查看一个进程的相关属性
    在这里插入图片描述

3.3 通过fork创建进程

fork是系统调用的函数。作用是创建一个子进程

man 2 getpid

在这里插入图片描述

返回值:

创建成功:

  • 父进程返回子进程的PID,是因为父进程要管理子进程,所以要知道子进程的 ID。

  • 子进程返回0,是因为我们只需要知道子进程创建成功没有

创建失败:返回 -1

我们创建一个子进程,并打印信息。

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

在这里插入图片描述

我们看到程序运行起来会循环打印两条信息,在fork() 之后 创建了一个子进程,第一条是父进程打印的,第二条是子进程打印的。

PPID就是当前进程的父进程的PID。子进程的PPID是6970,也就是父进程,但是父进程的PPID是20225,是哪个进程呢?

我们可以看看20225。

在这里插入图片描述

也就是说,我们在命令上所执行的所有的指令,都是bash的子进程。

使用 if 进行分流

像上面的代码,创建一个子进程与父进程执行同一份代码,没有如何意义。我们可以通过返回值不同,使用if语句进行分流就可以让父子进程做不同的事。

fork函数的返回值:

  1. 如果子进程创建成功,在父进程中返回子进程的PID,而在子进程中返回0。
  2. 如果子进程创建失败,则在父进程中返回 -1。
#include <stdio.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0) // child
    {
        while(1)
        {
            printf("I am child process  PID : %d\n", getpid());
            sleep(1);
        }
    }
    else if(id > 0) //parent
    {
        while(1)
        {
            printf("I am parent process PID : %d\n", getpid());
            sleep(1);
        }
    }
    else {
        printf("create child process failed!");
    }
    return 0;
}

在这里插入图片描述

父子进程会循环打印信息,我们将打印语句换成两个不同的任务,就可以同时执行两个任务。

如何杀死一个进程

  1. 直接等他执行完毕

  2. 通过发送信号 如:kill -9

3.4 进程状态

一个进程就像一个人一样,不同时刻有不同的状态。从创建到结束的整个生命期间,有有时处于运行状态,有时又由于某种原因而暂停运行处于等待状态,当使它暂停的原因消失后,它又进入准备运行状态。所以,在一个进程的活动期间至少具备三种基本状态,即运行状态、就绪状态、阻塞状态。

如果有大量处于阻塞状态的进程,进程可能会占用着物理内存空间,显然不是我们所希望的,毕竟物理内存空间是有限的,被阻塞状态的进程占用着物理内存就一种浪费物理内存的行为。

所以,在虚拟内存管理的操作系统中,通常会把阻塞状态的进程的物理内存空间换出到硬盘,等需要再次运行的时候,再从硬盘换入到物理内存。

虚拟内存管理 - 换入换出

那么,就需要一个新的状态,来描述进程没有占用实际的物理内存空间的情况,这个状态就是挂起状态。这跟阻塞状态是不一样,阻塞状态是等待某个事件的返回。

另外,挂起状态可以分为两种:

  • 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现;
  • 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行;

这两种挂起状态加上前面的五种状态,就变成了七种状态变迁。

在这里插入图片描述

在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 */
};

R - 运行状态

就是在运行或者在running 队列里面排队,都叫做运行状态,正在被调度或者已经准备好了随时可以被调度。所以可以同时存在多个R状态的进程。

S - 浅睡眠状态

该进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)。处于浅睡眠状态的进程可以随时被唤醒,也可以随时被杀死。

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("I am a process\n");
    sleep(1000);
    return 0;
}

通过命令查看该进程的状态

ps aux | head -1 && ps aux | grep test | grep -v grep

在这里插入图片描述

可以通过Ctrl + C 杀掉该进程
在这里插入图片描述

D - 磁盘休眠状态

该状态也叫不可中断睡眠状态,处于该状态的进程不会被杀掉,即便是操作系统也不行,只有该进程自动唤醒。

对磁盘进行写入或者读取期间,该进程就处于深度睡眠状态,是不会被杀掉的。

T - 停止状态

可以对一个进程发送SIGSTOP信号,该进程就进入到了暂停状态。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

X - 死亡状态

这个状态只是一个返回状态,你不会在任务列表里看到这个状态

该进程还在!,只不过是永远不运行了,随时等待被释放!

当一个进程被标记为“X”状态时,并不是说该进程仍然在运行。相反,这意味着进程已经被完全删除,包括其PCB和在内存中的所有数据结构都已经被操作系统回收。但是,这个进程仍然存在于操作系统的进程管理器中,以便操作系统可以记录并跟踪所有进程的状态。因此,虽然X状态的进程已经不存在于内存中,但是它仍然存在于操作系统的进程列表中。

Z - 僵尸状态

一个进程退出的时候,不会直接进入X状态,而是进入Z状态,等待操作系统回收该进程。

当进程退出的时候,PCB里的进程状态会被标记为Z状态,等OS回收,回收以后就变成X状态,表示该进程已经被完全删除。

模拟僵尸进程

如果创建子进程,子进程退出了,父进程不退出,也不等待子进程,子进程退出后所处的状态就是Z。它不会自动退出释放所有资源,也不会被kill命令再次杀死。避免僵尸进程的产生采用进程等待(wait/waitpid)方式完成。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
	pid_t id = fork();
	if(id == 0){ //child
		int count = 5;
		while(count){
			printf("I am child process PID : %d, PPID : %d, count : %d\n", getpid(), getppid(), count);
			sleep(1);
			count --;
		}
		printf("child quit !\n");
		exit(1);
	}
	else if(id > 0){ //father
		while(1){
			printf("I am father process PID : %d, PPID : %d\n", getpid(), getppid());
			sleep(1);
		}
	}
	else{ 
        printf("create child process failed!\n");
	}
	return 0;
} 

在这里插入图片描述

我们可以通过监控脚本查看进程状态的变化。此时子进程就是僵尸进程。

while :; do ps axj | head -1 && ps axj | grep test | grep -v grep;echo "######################";sleep 1;done

长时间Z有什么问题?

如果僵尸状态一直不退出,那么PCB就一直需要进行维护。
如果一个父进程创建了很多子进程,都不回收,那么就会造成资源浪费,因为数据结构对象本身就要占用内存。
如果僵尸进程越多,实际可用的资源就越少,所以僵尸进程可能会导致内存泄漏。

孤儿进程

如果父进程提前退出,子进程还在运行,子进程会被1号进程领养!这个1号进程就是操作系统!孤儿进程运行在系统后台

孤儿进程的产生一般都会带有目的性,比如我们需要一个程序运行在后台,或者我们不想一个进程退出后成为僵尸进程之类的需要。

守护进程&精灵进程

这两种是同一种进程的不同翻译,是特殊的孤儿进程不但运行在后台,最主要的是脱离了与终端和登录会话的所有联系,也就是默默的运行在后台不想受到任何影响。精灵进程其实和守护进程是一样的,不同的翻译叫法而已,它的父进程是1号进程,退出后不会成为僵尸进程。

注意:状态后面跟 +号说明是前台进程,可以 Ctrl+C 杀掉,但是没有 +号就是后台进程,Ctrl+C杀不到,我们可以kill -9 n 杀掉。

T and t

都是暂停的功能,比如追剧的暂停等,这两个其实是一样的,唯一的区别就是进程被调试的时候,遇到断点所处的状态,就是t。

退出状态

一个进程在执行系统调用 exit 函数结束自己的生命的时候,并没有真正的被销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在这个退出过程中,进程占有的所有资源,除了task_struct结构(以及少数资源)以外将被回收。于是只剩下task_struct这么个空壳,故称为僵尸。

保留 task_struct 是因为 task_struct 里面保存了进程的退出码以及一些统计信息,其父进程很可能会关心这些信息。比如在 shell 中,$?变量就保存了最后一个退出的前台进程的退出码。内核也可以将这些信息保存在别的地方,而将task_struct结构释放掉,以节省一些空间。但是使用task_struct结构更为方便,因为在内核中已经建立了从 PID 到task_struct查找关系,还有进程间的父子关系。释放掉task_struct,则需要建立一些新的数据结构,以便让父进程找到它的子进程的退出信息。

在子进程中调用 exit/return 可以终结子进程,但是这种终结不是销毁 ,子进程此时变成僵尸态。

如果父进程一直没有去主动获取子进程的结束状态值,那么子进程就一直保持僵尸状态。通过 wait/waitpid 函数就可以获取退出状态值从而回收僵尸进程。一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。该进程的父进程可以调用 wait/waitpid 获取这些信息,然后清除掉这个进程。

一个进程的退出状态可以在Shell中用特殊变量 $? 查看,因为Shell是它的父进程,当它终止时Shell调用wait/waitpid得到它的退出状态同时彻底清除掉这个进程。如果一个进程已经终止,但是它的父进程尚未调用wait/waitpid对它进行清理,这时的进程状态称为僵尸进程。任何进程在刚终止时都是僵尸进程。

销毁僵尸进程

父进程可以通过wait系列的系统调用(如wait、waitpid)来等待某个或某些子进程的退出,并获取它的退出信息。然后wait系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉。父进程通过wait/waitpid等函数等待子进程结束,这会导致父进程挂起,相关用法可以通过man命令查看。

子进程在退出的过程中,内核会给其父进程发送一个信号,通知父进程来“收尸”。这个信号默认是SIGCHLD,通过clone系统调用创建子进程时,可以设置这个信号。可以用signal函数为SIGCHLD安装handler回调函数,子进程结束后父进程会收到该信号,可以在handler中调用wait回收。

如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后内核会回收, 并不再给父进程发送信号。

fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后init会回收。不过子进程的回收还要自己做。

僵尸进程和孤儿进程区别

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程没有调用wait/waitpid获取子进程的状态,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程。

孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为1号进程(init进程),称为init进程领养孤儿进程。子进程的死亡需要父进程来处理,当父进程先于子进程死亡时,子进程死亡没有父进程处理,这个死亡的子进程就是孤儿进程。

僵尸进程占用一个进程ID号,占用资源,危害系统。孤儿进程与僵尸进程不同的是,由于父进程已经死亡,系统会帮助父进程回收处理孤儿进程。所以孤儿进程实际上是不占用资源的,因为它最终是被系统回收了,不会像僵尸进程那样占用ID。

3.5 进程优先级

为什么会存在优先级?
是因为资源不够,进程要竞争资源

可以使用命令列出当前系统所有进程的详细信息

ps -al

在这里插入图片描述

PRI and NI

  • PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
  • 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
  • PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
  • 所以,调整进程优先级,在Linux下,就是调整进程nice值
  • nice其取值范围是-20至19,一共40个级别
  • PRI(old)默认为80,即PRI = 80 + NI

可以使用top命令更改已存在进程的nice

top命令就相当于Windows操作系统中的任务管理器,用于实时监控系统的资源使用情况。

top,进入top后按“r”–>输入进程PID–>输入nice值,按“q”即可退出。

这个优先级只是用户建议的,至于操作系统按不按照这个优先级执行就不确定了。

4 其他概念

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级

  • 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰

  • 并行性:多个进程在多个CPU下分别,同时进行运行,这称之为并行

  • 并发性:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

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

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

相关文章

Java中关于ConditionObject的signal()方法的分析

代码块的展示 isHeldExclusively()这个仅持有锁资源的方法&#xff0c;在ReentrantLock中重写进行判断&#xff0c;要是没有持有锁资源那么会返回false&#xff0c;就会出现直接抛异常IllegalMonitorStateException&#xff08;非法监视器状态异常&#xff09;获取排在Conditi…

zookeeper相关,安装,认识......

目录 Zookeeper 1 第一章 Zookeeper简介... 1 1.1 Zookeeper概述和功能... 1 1.2 Zookeper安装... 1 1.3 Zookeper数据模型... 3 第二章 Zookeeper命令操作... 4 1.1 Zookeeper Client 4 1.2 Zookeeper JavaClient 6 第三章 集群角色... 28 Zookeeper 第一章 Zookeepe…

详解Servlet API

目录 前言 HttpServlet HttpServletRequest 代码实例 打印请求信息 通过URL中的queryString进行传递。 通过post请求的body&#xff0c;使用form表单传递 通过POST 请求中的 body 按照 JSON 的格式进行传递 HttpServletResponse 核心方法代码实例 设置状态码 自动刷…

【C++初阶】string 类的认识与学习

在学习string类之前&#xff0c;先在这里推荐2个好用的网站&#xff0c;可以用来查阅C的相关知识 https://cplusplus.com https://en.cppreference.com/w/ 上面的是非官方的&#xff0c;下面的官方的&#xff0c;但是个人感觉还是上面的好用。 一.string 类是什么 简单来说&…

State of GPT (ChatGPT 原理及现状介绍)

State of GPT 演讲信息&#xff1a; 演讲人&#xff1a;Andrej Karpathy (现在OpenAI任职)&#xff0c;之前是特斯拉视觉研发负责人&#xff0c;斯坦福深度学习入门课程 CS231N 讲师演讲主题&#xff1a;受到微软 BUILD2023 邀请&#xff0c;介绍 GPT 的原理及研发现状&#x…

VMware虚拟机安装Ubuntu 22.04详细教程(2023年新版教程)

VMware虚拟机安装Ubuntu 22.04详细教程&#xff08;保姆级教程&#xff09; 大家好&#xff0c;我是洲洲&#xff0c;欢迎关注&#xff0c;一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得10G学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群欢…

代码创作世界——pytorch深度学习框架数据类型

代码创作世界——pytorch深度学习框架数据类型 torch中的数据类型张量&#xff08;tensor&#xff09; pytorch中的 在数学中&#xff0c;一个单独的数可以成为标量&#xff0c;一行或者一列数组可以称为向量&#xff0c;一个二维数组称为一个矩阵&#xff0c;矩阵中的每一个元…

编译安装nginx服务

目录 编译安装nginx服务 1.关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下 2.安装依赖包 3.创建运行用户、组&#xff08;Nginx服务程序默认以nobody身份运行&#xff0c;建议为其创建专门的用户账号&#xff0c;以便更准确的控制访问权限&#xff09; 4.编译…

11.PasswordEncoder详解与实战

security/day07 这节课我们开始讲PasswordEncoder&#xff0c;如果大家还有印象的话&#xff0c;我们前面有提到过PasswordEncoder: 为什么密码使用{noop}开头呢&#xff1f;我们也做出了相应的解释&#xff0c;这节课开始带大家真正的了解PasswordEncoder, PassworderEncoder…

Android以aar包形式引入hunter-timing,Java(4)

Android以aar包形式引入hunter-timing&#xff0c;Java&#xff08;4&#xff09; &#xff08;1&#xff09;参照这篇文章&#xff1a; https://zhangphil.blog.csdn.net/article/details/130603231https://zhangphil.blog.csdn.net/article/details/130603231 生成.aar文件…

路径规划算法:基于象群优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于象群优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于象群优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法象群…

python基础知识(六):列表

目录 1. 列表及其创建2. 列表的特点3. 获取指定元素的索引4. 获取列表中指定索引的元素5. 列表的切片操作6. 列表元素的判断及遍历7. 列表元素的添加操作8. 列表的删除9. 列表元素的修改10. 列表元素的排序11. 列表生成式 1. 列表及其创建 列表由一系列按特定顺序排列的元素组…

如何设计一份优秀的 PPT 文档?

设计一份优秀的 PPT 需要考虑以下几个方面&#xff1a; 目标和受众&#xff1a;明确你的 PPT 的目标是什么&#xff0c;以及你的受众是谁。这将帮助你确定内容的重点和风格。结构和流程&#xff1a;确定清晰的结构和流程&#xff0c;使你的 PPT 有逻辑性。使用标题、副标题和分…

算法与数据结构(三)——排序算法大总结

六大排序算法&#xff1a;插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序 一、插入排序二、选择排序三、冒泡排序四、归并排序 一、插入排序 1.从第一个元素开始&#xff0c;该元素可以认为已经被排序 2.取下一个元素tem&#xff0c;从已排序的元素序列从后往前扫…

三、DMSP/OLS、NPP/VIIRS等夜间灯光数据能源碳排放空间化——灯光指数计算(二)

一、前言 续上一节,建成区提取方式有很多种,本文主要介绍一种区别于参考比较法、突变检测法等的领域分析提取建成区范围。 本文以广州市为例,基于领域分析提取2020年广州市建成区范围。 二、具体步骤 (1)加载数据并将广州市夜间灯光数据提取出来 (2)搜索"邻域…

【前端】Vue项目中 JSON 编辑器的使用

Vue项目中 JSON 编辑器的使用 一、背景描述二、vue-json-edit2.1 依赖安装2.2 示例代码2.3 效果图 三、vue-json-pretty3.1 依赖安装3.2 示例代码3.3 效果图 四、bin-code-editor4.1 依赖安装4.2 示例代码4.3 效果图 五、vue-json-views5.1 依赖安装5.2 示例代码5.3 效果图5.4 …

json库——jsoncpp

一、jsoncpp简介 JsonCpp是一个开源的C库&#xff0c;用于解析和生成JSON&#xff08;JavaScript Object Notation&#xff09;数据格式。JSON是一种轻量级的数据交换格式&#xff0c;广泛用于各种应用程序和网络服务中。 JsonCpp提供了简单和易用的API&#xff0c;可以方便地解…

C Primer Plus第十七章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 有些题目函数定义在书本存在&#xff…

路径规划算法:基于鸽群优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于鸽群优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于鸽群优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法鸽群…

Python编程面试题及答案(20例)

以下是一些常见的Python编程面试题以及它们的答案&#xff1a; 1.解释Python中的 GIL&#xff08;全局解释器锁&#xff09;是什么&#xff0c;它对多线程编程有什么影响&#xff1f; 答案&#xff1a;GIL是Python解释器中的一个机制&#xff0c;它确保在任何给定时间只有一个…