【Linux】了解进程的基础知识

news2024/9/27 12:08:47

进程

  • 1. 进程的概念
    • 1.1 进程的理解
    • 1.2 Linux下的进程
    • 1.3 查看进程属性
    • 1.4 getpid和getppid
  • 2. 创建进程
  • 3. 进程状态
  • 4. 进程优先级
  • 5. 进程切换
  • 6. 环境变量
  • 7. 本地变量与内建命令


1. 进程的概念

一个已经加载到内存中的程序,叫做进程(也叫任务)。有些教材也说正在运行的程序叫做进程。

1.1 进程的理解

  1. 一个操作系统不仅仅运行一个进程,可以同时运行多个进程。例如操作系统本身也是个程序,它要运行起来必须先加载到内存,然后再运行某个文件,这样就同时运行两个进程。

  2. 进程这么多,有的进程刚加载到内存,有的进程已经结束,有的进程还在运行。操作系统必须将进程管理起来!如何管理?先描述,再组织。

  3. 任何一个进程在加载到内存,形成一个真正的进程时,OS要先创建进程的结构体对象,这个结构体对象叫做PCB(process control block/进程控制块)。PCB本质就是进程属性的集合。所以一个进程要运行时,首先会根据进程的PCB,创建对应的结构体对象初始化,再把进程的代码和数据加载到进程。所以进程 = 内核数据结构对象(描述这个进程的所有属性值,由OS创建) + 自己的代码和数据(加载到内存的可执行程序,由程序员自己维护)
    在这里插入图片描述

  4. 前面我们提到OS对底层设备做管理,其实是对底层设备的结构体对象做管理,所以以后操作系统对进程做管理,只需对PCB结构体对象进行管理(因为PCB是进程属性的集合),不需要对代码和数据进行管理。操作系统对进程做管理,本质是对进程的内核数据结构(PCB对象)做管理

  5. PCB有指向代码和数据的指针,操作系统要调度进程时,会找到PCB,根据PCB指针信息找到代码和数据,再交给CPU去执行。

  6. 当内存中有多个进程,怎么把所有进程管理?刚刚完成先描述,现在将PCB关联起来,完成再组织。所以,在操作系统中,对多个进程进行管理,变成对PCB单链表进行增删查改

在这里插入图片描述

1.2 Linux下的进程

  1. task_struct
    Linux操作系统下的PCB是: task_struct。Linux先将进程描述成tack_struct对象,再组织。Linux是怎么组织进程的?在Linux内核中,最基本的组织进程task_struct方式,是采用双向链表组织的。但是在操作系统内部,一个task_struct还有其他的链接指针,可以还会二叉树的某个节点,或者队列的某个节点,并不只有一个链接方式的指针。
  2. task_struct
    (1)标示符(PID): 描述本进程的唯一标示符,用来区别其他进程。
    (2)状态: 任务状态,退出代码,退出信号等。
    (3)优先级: 相对于其他进程的优先级。CPU数目要少于进程,这意味着进程要竞争CPU,竞争就意味有优先级。
    (4)程序计数器: 程序中即将被执行的下一条指令的地址。
    (5)内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
    (6)上下文数据: 进程执行时处理器的寄存器中的数据。

1.3 查看进程属性

  1. 用ps或者top这两个指令来查看进程。以ps为例,ps一般带选项ajx。
    在这里插入图片描述
    通常配合管道和grep使用,比如要查找myprocess的进程
    在这里插入图片描述

  2. 进程的信息可以通过 /proc 系统文件夹查看。

操作系统将进程信息可视化,将进程放在/proc这个文件夹中。这些蓝色数字就是目录,数字表示进程的PID,目录里面存放进程的大量属性。
在这里插入图片描述

ls /proc/PID 查看进程的属性
在这里插入图片描述
讲讲两个属性:cwd(当前工作目录),exe(可执行程序)
在这里插入图片描述

  1. 每个进程终止后再启动,其PID大概率是变化的。

1.4 getpid和getppid

  1. 如何获得进程的PID?

操作系统提供了一个系统调用接口getpid,谁调用getpid就返回谁的PID。进程调用getpid,就相当于在这个进程的task_struct中找到它的PID,然后返回给用户。getppid则返回当前进程的父进程的PID。
在这里插入图片描述
证明

test.c
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main()
  5 {
  6   while(1)
  7   {
 8     printf("这个进程的PID是:%d,其父进程的PID是:%d\n",getpid(),getppid());
  9     sleep(1);                                                
 10   }
 11   return 0;
 12 }

运行这个代码后,对比打印出来的PID和PPID,与用指令查看进程属性中的PID和PPID。
在这里插入图片描述

  1. PID只保证每次运行期间有效,下次启动进程PID可能发生变化。

在这里插入图片描述

  1. 在命令行创建的进程,是由bash创建的,并且变成bash的子进程,每条指令都是bash的子进程。这些子进程的创建和销毁,不影响父进程。
    在这里插入图片描述

2. 创建进程

  1. 创建进程需要用到fork函数。

在这里插入图片描述
fork()创建子进程,如果创建成功,给父进程返回子进程的PID,给子进程返回0。如果创建失败,给父进程返回-1。
在这里插入图片描述

  1. 例子
 12   pid_t id = fork();                                                            
 13   if(id == 0)                                                                   
 14   {                                                                             
 15     printf("我是子进程,我的PID是:%d\n",getpid());                             
 16   }                                                                             
 17   else
 18   {
 19     printf("我是父进程,我的PID是:%d\n",getpid());
 20   }                                                                                                                              

在这里插入图片描述

  1. 疑问

(1)为什么fork要给子进程返回0,给父进程返回子进程的PID?
一般而言,fork之后的代码共享,返回不同的返回值,是为了区分,让不同的执行流执行不同的代码块(让父子进程执行不同的事情)。给父进程返回子进程的PID,用来标识子进程的唯一性,一个父进程可以有多个子进程,返回子进程的PID可以告诉父进程我是哪个子进程。
(2)fork函数究竟在做什么?
创建子进程,因为进程= 内核数据结构 + 代码和数据,所以实际上是在内存中创建对应的task_struct对象。子进程拷贝了父进程的内核数据结构,修改了部分属性(比如PID,PPID),但不会拷贝父进程的代码和数据,而是共享父进程的代码和数据。
在这里插入图片描述

(3)一个函数是如何做到返回两次的?
从fork函数的实现就可以看出来

pid_t fork()
{
	//1.创建子进程的PCB
	//2.填充PCB对应的内容
	//3.让父子进程指向同样的代码
	//4.父子进程都是具有独立的task_struct,可以被CPU调度执行
	...
	return ret;
}

return这句代码是在子进程被创建后与父进程共享的代码之一,被执行了两次,所以fork()会有两次返回值。

(4)一个变量怎么会有不同的内容?

  1. 独立性

任何平台,进程在运行的时候具有独立性。因为数据可能被修改,不能让父进程和子进程共享一份数据。

所以让子进程拷贝父进程的数据吗?如果子进程对拷贝的数据不修改,会导致操作系统中可用资源的减少(存在重复的数据)。所以操作系统识别到如果进程要修改数据中的某部分,再将父进程数据中的这部分数据拷贝过来,再进行修改。

  1. 父进程创建子进程后,谁先运行?

谁先运行由调度器决定,是不确定的。调度器挑选一个进程到CPU工作,CPU只负责运行进程。

  1. 所以现在我们创建进程有两种方法:一是./运行程序(指令级别);二是fork()(代码层面创建)。bash创建进程其实就是调用fork函数。

3. 进程状态

Linux操作系统中进程有运行状态(R)、睡眠状态(S)、磁盘休眠状态(D)、停止状态(T)、死亡状态(X)等。

  1. 运行状态(R)

(1)运行状态:表明进程要么是在运行中要么在运行队列里。

每个CPU都有一个运行队列,运行队列中排列着进程的task_struct。凡是处于运行队列的进程,它们所处的状态叫做运行态(R态)。
在这里插入图片描述

(2)一个进程只要把自己放到CPU开始运行,是不是就一直执行到完毕,再将退出运行?
肯定不是,每个进程都有一个叫时间片的时间限制,一旦进程在CPU中运行时间超过时间片,就会从CPU中拉下来,如果这个进程还要运行,需要继续在运行队列排队。所以在一段时间内,所有的进程代码和数据都会被执行,这就是并发执行。如果不是并发执行,我们就不能边听音乐边写文档,不能同时运行多个进程。

把进程从CPU中拿下来放上去的动作叫做进程切换。

(3)问题:为什么运行时是S状态?为什么将打印去掉后运行时是R状态?

//测试代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     
  6     while(1)
  7     {
  8         printf("I am a process,my pid:%d",getpid());
  9         sleep(1);
 10     }
 11     return 0;
 12 }

在这里插入图片描述
代码里面有printf,printf要访问显示器设备,CPU处理速度过快导致进程在显示器等待的时间远远大于进程在CPU上运行时间。所以导致肉眼看到的进程状态是S状态。
当将代码中的打印部分去掉时,
在这里插入图片描述

  1. 睡眠状态(S)

顾名思义,阻塞状态是指进程在等待时间完成,处于等待队列的状态。
理解
操作系统是如何管理底层设备的?先描述再组织。先将底层设备描述成一个个PCB结构体对象,对象里面包括这个对象的类型,这个对象的状态,和进程等待队列。
在这里插入图片描述
当我们的进程在运行时读取到输入(scanf、cin等代码),需要从键盘中读取数据,但是我们不输入,此时进程就没办法在运行队列上运行,因为当前键盘没有就绪,所以我们需要让该进程链入进程的等待队列,等待硬件资源就绪。每个设备都有一个等待队列,当设备没有就绪,进程就会链入进程的等待队列中。
在这里插入图片描述

拓展
当处于睡眠状态的进程过多时,它们的代码和数据在内存中处于空闲的状态,在等待过程中,操作系统内部的资源严重不足。所以为保证其他进程正常运行,需要省出内存资源。操作会把处于等待状态的进程的代码和数据交换到外设(磁盘),只留下进程的PCB在等待队列,节省内存资源。

在这里插入图片描述

问题
平时我们所说的阻塞状态与睡眠状态有什么区别吗?
(1)睡眠状态是进程本身拥有的状态,而阻塞状态则是用来描述进程的状态;
(2)睡眠状态是自动的,进程主动进入睡眠状态,主动进入等待队列等待,不获取CPU资源,不参与进程的调度,让其他进程获取CPU资源,而阻塞状态是被动的,必须等待某件事件的完成才能被CPU调度。
(3)一般可以认为睡眠状态是阻塞状态。

  1. 磁盘休眠状态(D)

磁盘休眠状态一般也叫不可中断休眠状态、深度睡眠。当进程向磁盘写入时,由于磁盘写入速度较慢,进程只能等待磁盘写入完毕才能向上层反馈。在此期间,如果计算机内存严重不足,操作系统已经用尽浑身解数去优化内存,但还是内存不足,此时它就将目标转向等待的进程,直接kill进程,这也是为什么有时内存不足时,有些程序会闪退。杀掉进程后,因为磁盘写入数据有可能失败,如果失败磁盘要向进程报告,但此时进程已被杀掉,磁盘找不到进程就可能把数据丢掉,如果是这份数据异常重要,比如银行的转账记录,那岂不是造成很大的损失。
所以就有了进程的磁盘休眠状态。当进程在等待磁盘写入完毕之前,这个进程不能被任何人杀掉,这就是进程的D状态。当磁盘写入完毕后,进程就从D状态改为R状态。
D状态的进程不响应操作系统的任何请求,只能自己醒来。

  1. 停止状态(T)

(1)可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。利用kill -l将信号名称显示,其中就有我们需要的信号。
在这里插入图片描述
(2)例子
在这里插入图片描述
在这里插入图片描述
(3)T状态与S状态有什么区别
处于T状态的进程可能在等待资源,也可能没在等待(这个进程被控制,单纯不能运行);处于S状态的进程一定在等待资源。

  1. 终止状态(X)

进程结束,释放进程所有资源,这就是进程的终止状态。这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

  1. 僵尸状态(Z)

(1)当一个子进程退出,父进程没有获取子进程的退出信息或者没有调用wait/waitpid回收子进程的资源,这个子进程变成僵尸进程(跳出三界之外,不在五行之中,此时的僵尸进程不能用kill杀死)。
例子

//模拟场景:子进程先退出,但父进程没有回收子进程的资源
int main()
{
    //创建子进程
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
    }
    else if(id == 0)//子进程
    {   
        int cnt = 20;
        while(cnt--)
        {
            cout<<"i am child process,my pid is "<< getpid() <<endl;
            sleep(1);
        }
    }
    else//父进程
    {
        //父进程一直循环,等待子进程退出
        while(1);
    }
}

结果
kill -9 命令也无法杀掉僵尸进程
在这里插入图片描述
子进程一般退出的时候,如果父进程没有主动回收子进程资源,子进程就会一直处于Z状态
在这里插入图片描述

(2)那么僵尸进程的危害是什么?很明显,只要父进程不回收子进程的资源,子进程就一直处于僵尸状态,一直占用内存资源,这就会造成内存泄漏。

(3)怎么避免僵尸进程?父进程得等待子进程先退出,且调用wait/waitpid获取子进程的退出信息。

(4)问题:为什么当杀掉父进程后,父进程不会变成僵尸进程?子进程会被释放?
在这里插入图片描述

  1. 孤儿进程

(1)讨论僵尸进程时,我们注意到其前提:子进程先退出,父进程后退出。那如果是父进程先退出,子进程后退出呢?也就算上面遇到的问题。父进程先退出,子进程后退出时,我们把子进程叫做孤儿进程。
(2)例子

int main()
{
    //模拟场景:父进程先退出,子进程一直循环
    //创建子进程
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
    }
    else if(id == 0)//子进程
    {   
        //让父进程比子进程先退出
        int cnt = 10;
        while(cnt--)
        {
            cout<<"i am child process,my pid is "<< getpid() <<endl;
            sleep(1);
        }
    }
    else//父进程直接退出,不回收子进程的资源
    {
        int cnt = 5;
        while(cnt--)
        {
            sleep(1);
        }
    }
    return 0;
}

结果
在这里插入图片描述
(3)子进程为什么要被领养?
因为孤儿进程未来也要退出,也要被释放。所以只能由OS来承担这个重任。


4. 进程优先级

  1. 概念

CPU资源分配的先后顺序,就是指进程的优先权。优先级和权限没有任何关系,优先级表示对于资源的访问的谁先谁后,权限表示能否访问资源。

  1. 为什么要有进程的优先级?

因为资源是有限的,进程是大量的,这注定了大量的进程要争夺有限的资源。而OS为了保证进程之间公平竞争,确立优先级。如果进程长时间得不到CPU资源,该进程的代码长时间得不到推进,就会导致进程的饥饿问题。

  1. 如何查看进程的优先级?

在这里插入图片描述
a. PRI表示进程优先级,它的取值范围是[0,139]。其中[0,99]之间的值是给实时进程用的,[100,139]是给普通进程用的,我们的进程一般都是普通进程,所以只需关注[100,139]之间的取值。值越小进程优先级越高,被CPU执行的顺序越靠前。
b. NI表示进程的NICE值,它可以修正进程的优先级。公式为PRI(new) = PRI(old) + nice。当nice为负值时,PRI越小,优先级越高。所以在Linux中调整优先级就是调整NI值。
c. 这是否意味着可以任意改变NI值,提供进程的优先级,从而使我的进程一直被调度?并不是。Linux不想让用户过多地参数优先级的调整,只允许让我们在一定的范围内进行优先级调整。即nice的取值范围为[-20,19]。

  1. 如何修改进程的优先级?

需要用到top指令,输入top后可以看到进程的调度情况。然后输入r 进程PID NICE值。注意设置新的PRI(new)看的是PRI(old),这个PRI(old)是历史上的第一次优先级。

  1. OS是如何根据优先级开展进程的调度的?即OS是怎么把优先级高的进程先执行,优先级低的后执行?
    在这里插入图片描述

综上
我们可以得出进程的两个性质:竞争性和独立性。

  1. 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
  2. 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。

5. 进程切换

  1. 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
  2. 一般我们的CPU都是单核,采用的是并发的方式。我们可以同时听歌、写文章、与他人聊天都得益于进程的并发。但是为什么我们没有感受到并发带来的影响(比如卡顿)?因为CPU切换进程速度快,我们感受不到。
  3. 一个进程放在CPU上,是不是得让这个进程一直跑完,才可以让它从CPU下来?并不是。如果是这样,那其他进程怎么办?OS规定,每个进程都有一个时间片,如果进程在这个时间段内跑完就销毁,如果跑不完就从CPU上剥离下来放到等待队列,继续排队,这个过程就叫做进程切换。这种基于进程切换和时间片轮转的调度算法就是CPU的调度算法。
  4. 为什么从CPU上剥离下的进程会被放到等待队列?这个进程是不能放到运行队列,因为它本是运行队列中优先级最高的,放到运行队列CPU不就又调度它了吗?
  5. 进程怎么切换?通过CPU中的寄存器。CPU中有很多寄存器,包括通用寄存器(eax,ebx,ecx,edx)、维护栈帧的寄存器(ebp,esp,eip)、状态寄存器(status)等。它们主要是为了提高CPU运行效率,将高频使用的数据放入寄存器中。例如,系统如何得知进程当前执行到哪行代码?通过程序计数器(PC指针/eip)记录当前进程正在执行指令的下一行指令的地址。
    CPU寄存器保存的是进程的临时数据,叫做进程的上下文。进程从CPU离开时,要将自己的上下文数据保存甚至带走,主要目的是为了未来恢复。
    所以进程在被切换时做了两个工作:保存上下文(离开);恢复上下文(运行)。
  6. CPU的临时数据保存在哪里?放到进程task_struct的结构体里面。

6. 环境变量

  1. 概念

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。有点抽象,先讲几个环境变量。

  1. 例子

(1)PATH
a. 问题
当我们运行自己的程序和运行系统的指令时,我们发现为什么运行自己的程序时要带 ./,而运行系统的指令时却不用带 ./
在这里插入图片描述
b. 什么是PATH?
PATH是系统自带的指令搜索路径。当运行指令时,shell会在PATH中找路径。
在这里插入图片描述
c. 问题答案
指令的路径放在PATH中,而我们自己的命令放在当前目录下,没有放在PATH中。shell会在PATH中查找运行程序的路径,找不到我们程序的路径,所以只能我们自己带 ./

d. 如果我偏不想带 ./?想让我们的程序像指令一样,该怎么做?
法一,可以将我们的程序拷贝到/usr/bin目录下;法二,直接将我们的程序的路径拷贝到PATH中。
怎么在PATH中新添路径?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(2)HOME:用户登录到Linux系统的默认目录。
在这里插入图片描述
(3)SHELL:当前Shell,它的值通常是/bin/bash。
在这里插入图片描述
(4)HISTSIZE:保存历史命令的条数。
在这里插入图片描述
(5)SSH_TTY:当前终端设备文件。在这里插入图片描述
(6)PWD:记录当前所处的工作目录;OLDPWD:记录上一次所处的工作目录。
在这里插入图片描述
还想获取更多环境变量,可以用指令env查看。

  1. 用getenv获取环境变量

在这里插入图片描述
例子

int main()
{
    //getenv
    cout<<"PATH:"<<getenv("PATH")<<endl;
    cout<<"WHO:"<<getenv("USER")<<endl;
    return 0;
}

在这里插入图片描述

  1. 为什么我们能在我们的程序中获得环境变量?

在这里插入图片描述
我们运行的进程都是子进程,bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给我的所有环境变量。所以环境变量具有全局属性。
证明环境变量被子进程继承
如何证明?在bash新增一个环境变量,然后子进程继承并打印,看看有没有bash中新增的环境变量。

//子进程代码
int main(int argc,char*argv[],char*env[])
{
    int i = 0;
	for(; env[i]; i++)
	{
		cout<<env[i]<<endl;
	}
}

在这里插入图片描述
那如何销毁环境变量?
在这里插入图片描述

那如何在代码中获得环境变量?
(1)法一:通过命令行的第三个参数

int main(int argc, char *argv[], char *env[])//这三个参数等下讲,只需知道env是字符指针数组,指向环境变量
{
	int i = 0;
	for(; env[i]; i++)
	{
		cout<<env[i]<<endl;
	}
	return 0;
}

在这里插入图片描述

(2)法二:通过第三方变量environ获取
environ是C库定义的一个全局变量,指向环境变量表。
在这里插入图片描述

int main(int argc, char *argv[])
{
	extern char **environ;//environ没有包含在任何头文件中,所以在使用时 要用extern声明
	int i = 0;
	for(; environ[i]; i++)
	{
		cout<<environ[i]<<endl;
	}
	return 0;
}

在这里插入图片描述
(3)法三:通过系统调用getenv(),相较于前两种方法,这种方法更适合获取单个环境变量。

  1. 命令行参数

(1)main函数是可以接收参数的,这些参数就是命令行参数。

int main(int argc,char*argv[])

其中argv是指针数组,指向命令行的字符串;argc是命令行字符串的个数。argv有argc个元素。命令行参数以空格为分隔符,将输入的字符串打散成几个字符串,记下有多少个字符串放到argc,将每个字符串的地址放到argv中,再通过传参给main函数。(这表明实际上main函数并不是程序真正的入口)

(2)例子:打印命令行参数

int main(int argc,char*argv[])
{
    int i = 0;
    for(;i<argc;i++)
    {
        printf("argv[%d]:%s\n",i,argv[i]);
    }
    return 0 ;
}

在这里插入图片描述
(3)获取命令行参数的意义是什么?
为指令、工具、软件等提供命令行选项的支持。
例子

int main(int argc,char*argv[])
{
    //如果命令行参数不足两个,或者超过两个,给出提醒
    if(argc != 2) 
    {
        printf("Usage: %s -[a|b]\n", argv[0]);
        return 0;
    }
    if(strcmp(argv[1], "--help")==0)
    {
        printf("Usage: %s -[a|b]\n", argv[0]);
    }
    else if(strcmp(argv[1], "-a") == 0)
    {
        printf("功能1\n");
    }
    else if(strcmp(argv[1], "-b") == 0)
    {
        printf("功能2\n");
    }
}

(4)第三个命令行参数

int main(int argc,char*argv[],char*env[])

env是指针数组,指针指向环境变量。


7. 本地变量与内建命令

  1. 本地变量是在命令行直接定义,只在bash内部有效的变量。本地变量不能被子进程继承。

  2. set可以查看系统中所有的本地变量和环境变量。

  3. 问题:

在这里插入图片描述
bash定义一个本地变量MY_ENV,echo是一个指令,也是bash的子进程,它为什么能打印父进程的本地变量?不是说本地变量不能被子进程继承吗?
命令行上的命令并不是全部要创建子进程。

  1. 命令可以分为两类:一类是常规命令:通过创建子进程替父进程完成;一类是内建命令:bash不创建子进程,而是由自己亲自执行,类似于bash调用了自己写的或者系统提供的函数。

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

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

相关文章

2021年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题

文章目录 2021年全国硕士研究生入学招生考试英语二试题SectionⅠUse of EnglishSection Ⅱ Reading ComprehensionText 1Text 2Text 2Text 3Text 4 Section III TranslationSection Ⅳ Writing 2021年全国硕士研究生入学招生考试英语二试题 SectionⅠUse of English Directio…

vue项目多个不同的服务器请求地址管理

vue项目多个不同的服务器请求地址管理 在vue项目开发过程中,获取不同的数据可能会出现需要请求多个不同服务器地址的域名,这个时候需要对不同域名的请求地址进行管理以及跨域的代理。 一、单服务器域名地址的跨域代理和请求配置: 跨域配置: 在vue项目的vue.config.js文件…

GAN:ImprovedGAN-训练GAN的改进策略

论文&#xff1a;https://arxiv.org/abs/1606.03498 代码&#xff1a;https://github.com/openai/improved_gan 发表&#xff1a;NIPS 2016 一、文章创新 1&#xff1a;Feature matching&#xff1a;特征匹配通过为生成器指定新目标来解决GANs的不稳定性&#xff0c;从而防止…

很清楚展示GPT插件的调用过程,人工智能(AI)的潜在危险与好处 超级智能 未来

好处&#xff0c;未来 很清楚展示GPT插件的调用过程&#xff1a; 把请求和要求发chatGPT chatGPT返回markdown格式发给插件 插件返回结果给用户。 你不用别人用。 人工智能&#xff08;AI&#xff09;的最危险之处通常与以下几个方面有关&#xff1a; 自主决策能力过强&…

一篇博客带你了解TCP网络传输协议

Transmission Control Protocol&#xff08;TCP&#xff0c;传输控制协议&#xff09;是互联网协议套件中的一种主要协议之一&#xff0c;负责在网络上可靠地传输数据。下面是TCP协议的详细讲解&#xff1a; 1. 基本概念&#xff1a; 面向连接&#xff1a; TCP是一种面向连接的…

2.安装docker

目录 1 安装依赖 2 安装docker 3 测试 目前docker分为三类 Docker-CE(社区版),Docker-EE(企业版)和Moby。Moby是docker社区用户自己写的&#xff0c;所以Moby我们一般用不上 每一类的每一个版本中都有Edge与Stable版&#xff0c;Stable维护4个月&#xff0c;Edge维护1个…

在柯桥西班牙语论文写作,连接词只会用porquepero?西语连接词大全来啦~

Adicin -agregan nuevos datos al desarrollo de una idea o introduce otro aspecto del tema. 为观点的论述增添新的信息&#xff0c;或介绍主题的另一个方面。 1 Lista de conectores 连接词列表 Adems. As mismo. Hay que mencionar, adems. Habra que decir tambin. Mas …

当消费增值模式遇上Dapp:擦出创新商业的火花

随着区块链技术和智能合约的不断发展&#xff0c;去中心化应用&#xff08;Dapp&#xff09;逐渐成为一种创新的商业模式。当消费增值模式与Dapp相遇&#xff0c;它们之间擦出了怎样的火花呢&#xff1f; 一、Dapp与消费增值模式的结合 Dapp是一种基于区块链技术和智能合约的去…

分层理解Java字符串常量池

Java是一门计算机编程语言&#xff0c;但我们脑海中所理解的Java不仅仅是一门语言。它还包括Java虚拟机&#xff08;JVM&#xff09;的一系列规定&#xff0c;及具体Java产品&#xff08;如Hotspot&#xff09;的实现原理。 不管我们日常在Java中用到的任何一种语法&#xff0…

python如何抓取携程酒店的价格,让工作更简单点

有时候老板没事安排点事&#xff0c;为了偷懒&#xff0c;只能使出大招&#xff0c;毕竟自己不是那么老老实实干活的人&#xff0c;整理数据这类累和繁琐的活&#xff0c;我怎么能轻易动&#xff0c;好在gpt可以帮我来实现&#xff0c;有人可能会说&#xff0c;这么点内容你还不…

在线陪诊系统: 医疗科技的崭新前沿

在医学科技的快速发展中&#xff0c;在线陪诊系统正成为医疗服务领域的创新力量。通过结合互联网和先进的远程技术&#xff0c;这一系统为患者和医生提供了更为便捷、高效的医疗体验。本文将深入探讨在线陪诊系统的技术背后的核心代码和实现原理。 技术背后的关键代码 在线陪…

面试题:说一下MyBatis动态代理原理?

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.MyBatis简介2.使用步骤2.1、引入依赖2.2、配置文件2.3、接口定义2.4、加载执行 3.原理解析 1.MyBatis简介 MyBatis是一个ORM工具&#xff0c;封装了JDBC的操作&a…

k8s中pod的hostport端口突然无法访问故障处理

故障背景&#xff1a; 租户告知生产环境的sftp突然无法访问了&#xff0c;登录环境查看sftp服务运行都是正常的&#xff0c;访问sftp的hostport端口确实不通。 故障处理过程 既然访问不通那就先给服务做个全面检查&#xff0c;看看哪里出了问题&#xff0c;看下sftp日志&#…

Java学习路线第二篇:Java Web

这篇则分享Java学习路线第二part&#xff1a;Java Web 恭喜你已经成功追到第二章节啦&#xff0c;要被自己的努力感动到了吧&#xff0c;而这节将承担起学完Java web 的使命&#xff0c;本使命为单向契约&#xff0c;你可选择YES或者选择YES。 HTMLCSSJavaScript(JS) 【动…

Linux—进程状态、僵尸进程、孤独进程、优先级

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、进程状态二、僵尸进程、孤儿进程1、Z(zombie)-僵尸进程2、僵尸进程危害3、孤儿进程 三、进…

DHCP协议及实验omnipeek抓包工具分析 IPv4协议

一 抓包命令 adb shell tcpdump -i wlan0 -w /data/tcpdump.pcap 抓包后截图如下 二 DHCP是什么 2.1 DHCP定义 DHCP( Dynamic Host Configuration Protocol, 动态主机配置协议)定义: 存在于应用层(OSI) 前身是BOOTP(Bootstrap Protocol)协议 是一个使用UDP(User …

【沁恒蓝牙mesh】程序烧录 硬件电路设计与原理

本文基于沁恒CH58X 单片机的程序烧录硬件电路设计原理解释 【沁恒蓝牙mesh】目录 1. 程序烧录方式2. USB 烧录过程描述3. 硬件设计原理图3.1 官方开发板原理图3.2 自开发设计原理图 4. 电容在上电瞬间为什么相当于短路&#xff1f; &#x1f4cb; 个人简介 &#x1f496; 作者简…

关于AM5系列微机保护装置在某产业园配电工程中的应用-安科瑞 蒋静

1 摘要 目前&#xff0c;微机保护装置广泛应用于电力系统中&#xff0c;该类装置能够合理监测电力系统的运行状况&#xff0c;并实时记录电力系统出现故障的位置及性质&#xff0c;从而为故障的快速处理提供合理的参考信息。本文介绍的AM5系列微机保护装置&#xff0c;可以针对…

DC电源模块的散热措施

BOSHIDA DC电源模块的散热措施 DC电源模块的散热措施可以分为以下几种&#xff1a; 1. 增加散热器&#xff1a;在DC电源模块的电路板上增加散热片或散热器&#xff0c;通过增加散热面积和散热能力来提高散热效果。 2. 增加风扇&#xff1a;在散热器的基础上增加风扇&#xff…

竞赛选题 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

文章目录 0 简介1 思路简介1.1 车牌定位1.2 畸变校正 2 代码实现2.1 车牌定位2.1.1 通过颜色特征选定可疑区域2.1.2 寻找车牌外围轮廓2.1.3 车牌区域定位 2.2 畸变校正2.2.1 畸变后车牌顶点定位2.2.2 校正 7 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享…