Linux《进程概念(上)》
在之前的Linux学习当中我们已经了解了基本的Linux指令以及基础的开发工具的使用,那么接下来我们就要开始Linux当中一个非常重要的部分的学习——进程,在此进程是我们之后Linux学习的基础,并且通过进程的学习会让我们了解更多的操作系统的相关知识以及基本的底层原理。在本篇当中我们会先了解冯诺依曼体系、进一步了解操作系统概念、了解进程的概念。那么接下来就开始本篇的学习吧!!!
1.冯诺依曼体系
在了解进程的相关概念之前我们需要先来了解在计算机当中一个非常重要的体系——冯诺依曼体系。
冯诺依曼体系的结构示意图如下所示:
其实在当前我们常见的计算机,如笔记本等都是遵循冯诺依曼体系的。通过以上图示就可以看出当中的中央处理器其实就是CPU,而存储器就是内存。
在此就可以发现CUP=运算器+控制器,存储器其实就是内存。
注:在此内存不是日常当中说的内存,日常当中我们提到的512GB/1TB等其实都是外存,而像16GB/32GB等才是内存。
在以上的图示当中的输入设备以及输出设备其实就是外设,而其中的输入设备就是键盘、鼠标、网卡、摄像头、话筒等;输出设备就是显示器、磁盘、网卡、打印机等。
注:以上的磁盘其实就是外存。
那么在了解了冯诺依曼体系的基本结构之后接下来就来了解在计算机当中按照该体系进行运转时会有什么特点?
通过以上的图示就看到在CUP当中处理的数据都是内存给他的,在这之前是需要输入设备将数据输入到内存当中的,之后CPU处理完之后再将对应的数据传输给内存之后再从内存将数据传输给输出设备。那么通过该过程就可以看出CPU在获取、写入等操作时只能从内存当中来进行。
那么此时问题就来了,为什么在冯诺依曼体系当中要这么设计呢?
这其实是因为当代的计算机其实是性价比的产物,接下来来看以下的图
通过以上的图示就可以看出存储设备的进行存取的速度其实是和设备的成本程正比的,这时也是有什么相同大小的内存会比磁盘要贵的多。
那么此时就可以思考如果在计算机当中直接将CPU和磁盘等输入设备打交道会有什么问题?
这时如果像以上这样直接将CUP从输入设备当中进行数据的读取再将处理之后的数据输出给输出设备,这时其实就会出现一个问题了,那就是CPU进行数据的处理的速度相比从磁盘等设备进行读写的速度是要快非常多的,这也就使得在这种情况下会出现CPU已经早已经将读取的数据处理之后接下来就会进入到等待数据的过程当中,这时磁盘已经使出了全力还是无法匹配CPU的处理速度,那么这就会造成程序运行的效率十分的低下,即使CPU的性能非常的高也无法正常的发挥其的性能;处理数据的效率转而由更慢的磁盘决定了,这是非常不合理的。
那么此时你可能就会想那么直接数据的存储放在内存当中不就好了,这样不就不会出现以上的问题了?
话是这么说,但是大内存太贵了啊,即使现在16GB的内存基本也是不便宜的,如果都使用内存来进行数据的存储那么计算机就不会像现在这样成为普通人也能使用的科技产物。
因此为了平衡效率和价格就出现了冯诺依曼体系,在该体系下内存进行外部设备的数据的读写,之后再将得到的数据给CPU进行处理,那么这样就不会出现CPU大量时间在闲置的问题。
总的来说在冯诺依曼体系当中CPU在数据层面,只和内存打交道。外设只和内存打交道。
接下来我们就通过一个实际的例子来了解在当你登录上qq开始和某位朋友聊天开始,数据的流动过程。
其实大致的过程就如下所示,在此你在键盘输入对应的信息之后就会先传输到内存当中,之后再由内存将数据信息转至CPU进行处理之后再将处理完的数据传输给网卡,网卡再通过网络的传输将你的数据信息传输到指定用户下,由该用户的网卡接收数据之后再传输到内存当中再由CPU处理后传输到显示器上,最终就可以在该用户的显示器上显示出你发的信息。
以上就可以看出你的键盘在你的体系当就是输入设备,网卡就是输出设备,在另一个用户下输入设备就是网卡,输出设备就是显示器。
2.操作系统(Operator System)
在之前的Linux《权限》当中我们就初步了解了Shell命令以及运行原理,我们知道了操作系统与shell外壳之间的关系,但是在之前我们是感性的了解,这还是不够的,在此接下来我们就将理性的理解。
2.1 概念
任何计算机系统都包含⼀个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
• 内核(进程管理,内存管理,⽂件管理,驱动管理)
• 其他程序(例如函数库,shell程序等等)
在此之前我们就提到了安卓的底层是基于Linux的,在此其实就是安卓的·底层结构是基于的是Linux的内核。之后再设计适合手机的外壳程序就形成的安卓。
2.2 设计OS的目的
在此要通过是什么、为什么、怎么办三步来了解操作系统,以上我们已经了解了是什么,那么接下来就来了解为什么要有OS
以上就是操作系统与硬件以及用户之间的关系图,通过之前的学习我们知道在计算机当中硬件部分是符合冯诺依曼体系的,此时再看以下的关系图就可以看出在操作系统和硬件之间其实是存在驱动。
那么驱动存在的作用是什么呢?
首先要了解的是不同的硬件设备是拥有不同的读取方式的,例如网卡和磁盘进行读取的方式就完全不同。因此为了能让操作系统能对相关的硬件进行管理就需要驱动的辅助,当外设物理链接计算机之后还要将对应的驱动也安装才能使得硬件能正常的运行,就例如在我们是使用鼠标的适合,只有在电脑当中也安装了鼠标相应的驱动才能使得鼠标能正常的使用。
那么通过以上的驱动以及硬件和操作系统之间的关系就可以看出在操作系统其实对下,是要进行硬件的交互的,管理所有的软硬件资源
但是其实操作系统在将底层的硬件进行管理不是目的而是手段,真正的目的是为了给用户提供一个稳定的环境。就例如在我们使用电脑的时候操作系统对屏幕进行管理的目的就是为了让我们能从屏幕当中看到想要的信息,而不能出现蓝屏等异常德情况。
因此总的来说在计算机当中操作系统的作用就分为两个方面:
• 对下,与硬件交互,管理所有的软硬件资源
• 对上,为用户程序(应用程序)提供⼀个良好的执行环境
接下来在了解了为什么要有操作系统之后接下来就来了解几个补充的知识点
1.在计算机当中软硬件的体系结构其实是层状结构的
通过以上的图示其实就可以看出计算机当中体系结构是分层的,有用户、操作系统、驱动、硬件等
那么此时我们就要思考为什么在计算机的体系当中要设计为以上的层状结构呢?
这其实是在软件工程当中有一个非常重要的理念——高内聚低耦合。其实在之前C语言的学习当中我们就已经初步了解了这一基本的理念,在刚学习C语言的时候我们写的代码都是直接在main函数内的,到了之后学习了函数之后就开始逐渐将实现不同的功能使用函数进行划分。并且之后在C++当中还了解了面向对象的三大特点:封装、继承、多态。其实这些设计的根本目的就是为了增强代码的可维护性,这些其实就是高内聚低耦合的体现。
因此以上提到的是在软件上的设计,而在硬件上设计成为层状的结构也可以使得某一层次出现问题时只需要修复对应的层次即可其他的层次不需要进行修改,就例如当你的计算机网卡出现问题时只需要更换新的网卡即可、当计算机的网卡驱动出现问题时只需要再重新下载对应的网卡驱动即可。
2.访问操作系统,必须使用系统调用,在此我们使用的函数就是封装系统调用的
3.我们的出现,只要你判断出了它访问了硬件,那么它必须贯穿整个软硬件体系结构
4.库底层可能封装了系统调用
操作系统其实是不允许用户直接访问文件、内存;读取进程,在此必须要通过相应的系统调用来访问对应的进程等。但是这样不就说明要了解对应的系统调用才能实现对相关硬件的操作呢?那这不是要了解操作系统相关的知识吗?但是这样对应用户来说使用系统调用的学习成本太高了,因此语言就封装了系统调用。就例如之前我们C语言当中使用的printf是将你的数据写到硬件显示器上,但其实printf不是直接将对应的信息直接输出到显示器上的,而是调用了相应的系统调用,之后系统调用再将对应的数据输出的显示器上的。
2.3 核心功能
在此在整个计算机软硬件架构中,操作系统的定位是:⼀款纯正的“搞管理”的软件
那么接下来我们就来理解管理
那么接下来我们就通过一个校长-辅导员-学生的例子来理解管理
在学校当中其实是有很多的角色的,但是在此假设我们就只有学生、辅导员、校长三种角色。
以下是这三种角色的关系图
在此校长就作为决策者要对被管理的学生进行管理的时候那么接下来就可以通过执行者辅导员来将学生的信息通过表格的方式编辑好之后给到校长,之后校长就可以通过这些表格来得到学生的信息。在此过程当中校长是没有见到学生的,但是还是实现了对学生的管理,因此要管理,管理者和被管理者之间其实是可以不需要见面的
当管理者要对被管理者进行管理时就只需要根据“数据”就可以进行进行
就例如当校长要选拔出学校的篮球队时就只需要通过辅导员统计的信息来进行
并且当有了表格之后,校长对学生管理就转化为了对表格的管理!可以对表格进行增删查改
但是校长是程序员出生的,还是觉得表格的方式进行管理不太好,此时他就想到可以使用链表来进行管理啊!链表每个节点就是一个学生的信息,在此使用一个结构体表示。
以上有了链表校长对学生的管理就转化为了对链表的增删查改
以上校长的管理过程就是建模的过程,其最本质就是先描述再组织,这也是对任何“管理”场景进行建模的过程。这也就可以解释为什么在C++、Java、php等面向对象的语言当中要提供类以及标准模板库就例如C++当中的STL。
在此类就是解决了先描述,而STL就是实现再组织
那么在此也就能理解为什么有人说当前面向对象语言已经成为主流,这就是因为我们世界的特点就是先描述再组织形成的。因此在计算机当中舍友的软硬件其实也是先描述再组织的。
2.4理解系统调用
以上已经提到了系统调用,那么系统调用实际上要如何去理解呢?
在此我们就通过银行的例子来理解。在银行当中银行要给用户提供存款取款等的服务,但是银行又不能直接将银行内不能完全信任用户,具体的表现就是用户在存款取款时不能直接进入到银行的保险库当中,但是银行还要给用户提供存款取款的服务啊,因此这就有了柜台
在柜台上用户就可以将需求告知,之后柜台就可以将用户的需求进行解决。那么在此还有一种情况就是如果用户完全对存取款不了解,此时就需要银行的大堂经理来协助用户进行操作;此时用户将自己的需求转至大堂经理,之后大堂经理再将用户的需求转至柜台。
其实系统调用就和银行当中的柜台类似,普通的开发者是无法自己使用系统调用的,那么此时就有了库函数,在此库函数就和银行当中的大堂经理类似。用户只需要使用库函数,库函数就可以自主的调用系统调用
3. 进程
3.1 进程概念
以上我们了解了操作系统的基本知识之后接下来我们就来了解进程的基本概念,不过和课本当中不同不会一开始就直接引入一些概念的名称,就像以下一样
• 课本概念:程序的⼀个执⾏实例,正在执行的程序等
• 内核观点:担当分配系统资源(CPU时间,内存)的实体。
在此是通过实际的情况以及图示来了解
在此我们知道当形成可执行程序之后是存储在磁盘当中的,当我们调用对应的可执行程序之后就会将磁盘当中对应的可执行程序的代码和数据传输到内存当中, 那么是否这些代码和数据就是进程呢?
此时我们就要思考在一些情况下是会出现多个可执行程序同时运行的,那么这时就会有多份的代码额和数据在内存当中,那么此时操作系统要怎么分辨出不同的进程呢?
因此在此为了对不同的进程进行管理,就给每个进程都形成了一个对应的内核数据结构对象,在该数据结构对象当中存储着进程的信息。在此之后再将每个进程的数据结构对象使用链表连接起来对这些进程的管理就转化为了对链表的管理,这符合之前提到的先描述再组织。
在操作系统当中将这些存储进程信息的数据结构对象叫做进程控制块,可以理解为进程属性的集合。在操作系统当中称为PBC(process control block)。在Linux当中将该结构体对象称为task_struct
其实进程就是由内核数据结构对象PBC再加自己的代码和数据构成的,而不是之前认为的是进程只是代码和数据。
进程=PBC(task_struct)+自己的代码和数据
那么以上提到的task_struct内会存储哪些的信息呢?
其实会存储的信息非常的多,会有以下的数据等,在这当中有一些是想要我们了解的,只不过这要之后再慢慢的了解
• 标示符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。
• 状态: 任务状态,退出代码,退出信号等。
• 优先级: 相对于其他进程的优先级。
• 程序计数器: 程序中即将被执⾏的下⼀条指令的地址。
• 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
• 上下文数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。
• I / O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
• 记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。
• 其他信息
3.2 查看进程
在以上我们了解了进程的基本概念,那么接下就来学习如何查看进程
其实我们之前执行的所有的指令、工具、自己的程序,只要运行起来就都是进程。
并且当每个进程在运行起来之后都会有自己的ID,在Linux当中要查看当前进程的ID就想要使用到以下的系统调用
在此getpid的返回值其实就是当前进程的pid,在此返回值的类型为pid_t,其实类型就是int
在此接下我们就使用以下的代码来查看一个进程的pid
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h> int main()
{ printf("pid:%d\n",getpid()); return 0;
}
我们将以上代码的.c文件编译链接之后形成mytest的可执行程序,运行该程序就可以看出该程序的pid如下所示
以上我们了解了getpid这个系统调用,但是在使用man手册查询getpid还看到有另一个系统调用getppid,那么这个又是什么作用的呢?
接下来我们就来了解,其实每个进程都是存在父进程的,而getppid这个系统调用就是用来获取父进程的id的。接下来在以上的代码当中加上getppid的使用。
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h> int main()
{ while(1){printf("ppid:%d\n",getppid()); printf("pid:%d\n",getpid()); }return 0;
}
以上这样确实能获取到当前进程的ID,但是一般情况下我们是想要查看当中系统当中所有运行的进程的,那么就需要了解以下的指令
ps ajx
在此ps指令就可以查看所有的进程,之后的选项作用如下所示:
• a:显示⼀个终端所有的进程,包括其用户的进程。
• x:显示没有控制终端的进程,例如后台运⾏的守护进程。
• j:显示进程归属的进程组ID、会话ID、父进程ID,以及与作业控制相关的信息
但是以上使用了该指令之后会将当前系统当中的所有进程信息都显示在屏幕上
如果我们只是要获取一个进程的信息呢?此时就需要使用到管道外加grep
grep ajx | grep 程序名
以上我们启动mytest之后接下来再使用ps来获取该进程的信息
但是此时问题就是以上虽然显示出了对应的信息,但是我们无法分辨各个信息表示的是什么,那么还需要对使用ps指令时进行修改,在此我们就需要使用以下的指令
ps ajx | head -1;ps ajx | grep 程序名
以上你这时就会发现此时的./mytest进程的id和之前不一样了,这是为什么呢?
其实只不过是我将之前的进程停止了,重新再运行一次,这时系统就会重新为该进程分配pid
此时你可能还有还有问题就是为什么在查询mytest的进程信息时会有两个进程的信息,在此我们知道第一行表示的是进程的信息,那么第二行又表示的是什么呢?
其实在查询的时候会出现第二行是因为grep指令也是进程,那么在ps指令之后使用grep就会再形成一个进程。此时如果要忽略grep进程就可以在以上的指令之后加上以下的指令
ps axj | head -1;ps axj | grep 程序名 | grep -v grep
以上我们的mytest可执行程序运行起来就不会停止,之前我们就知道要使得一个进程停止就需要使用CTRL+c,那么将进程停止之后再使用ps指令就可以看到对应进程也消失了
其实除了以上的方式可以杀掉进程之外还可以使用kill指令来杀掉进程,在此只需要使用以下指令即可
kill -9 进程ID
在此kill指令为什么能将进程结束以及是如何实现的具体要等到之后我们学习Linux信号时再了解
以上我们要查看当前系统内的进程除了可以使用ps指令外其实还可以通过系统内的一个目录进行查看,在此该目录路径如下所示:
/proc
此时使用ls指令就可以将当中系统内所有进程显示出来 ,在此每个目录就表示一个进程,每个命令内会存储进程对应的信息
以上我们将mytest再运行起来之后使用进程的pid进行查询就可以看到再pro目录下是存在对应的目录的
那么对应进程内的信息接下来就来补充两个小知识点
我们使用ls -l就可以看到在进程当中是存在许多的信息的,在此有两个是需要我们了解的分别是cwd和exe,在这其中cwd存储的就是当前进行所在的路径,而exe存储的就是进程对应的可执行文件所在的路径。
了解了以上的知识也就可以解释了为什么之前在C语言当中使用fopen打开一个文件时,当使用w方式打开时如果要打开的文件不存在时就会在当前路径下创建对应的文件,其实此时fopen会根据当前进程路径下创建对应的文件,在此路径就是从当前进程PCB内获取的。
此时在运行mytest程序的时候就会发现每次父进程的ID都是一样的,这又是为什么呢?
那么这时就使用ps来查询pid为27763的进程是什么
此时就可以看到其实我们创建的进程的父进程就是bash,bash不就是之前我们提到的命令行解释器吗?
因此我们执行可执行程序的时候系统新创建的进程的父进程就是bash进程,也就是进行命令行解释的时候创建的进程都是bash进程的子进程,这就和之前我们提到的王婆——实习生的模式吻合了。
其实只要我们登入上了Linux操作系统就会给我们分配一个bash进程
在此我们可以使用以下的指令来实现每隔一秒来将系统当中的所有的bash信息打印出来
while :;do ps axj | head -1;ps axj | grep bash | grep -v grep ; sleep 1; done
此时再打开一个Xshell在大于时就会发现当前操作系统内存在三个bash,之后退出一个Xshell就会方向bash的数量会减少一个
3.3 创建子进程
以上我们了解了进程的基本概念之后接下来就来了解如何创建子进程
创建子进程需要使用到以下的系统调用
那么在程序内使用fork之后会出现什样的现象呢?接下来就来看以下的代码
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h> int main()
{ printf("父进程开始运行,pid:%d\n",getpid()); fork(); printf("进程开始运行,pid:%d\n",getpid()); return 0;
}
运行编译以上的代码形成可执行程序之后就会看到输出结构如下所示:
此时就可以看到以上的代码在调用fork之前是只有父进程在执行,调用了fork之后就会让父进程和子进程都执行之后的代码
以上就了解了使用了fork之后程序会如何运行,fork的返回值又是什么呢?接下来继续看看
通过使用man手册内fork函数的返回值的说明就可以看出fork的返回值有两个,当返回值为0时表示子进程,返回值大于0时表示父进程。当返回值为-1时表示子进程创建失败 。
接下来先来看以下的代码
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
int main()
{ printf("父进程开始运行,pid:%d\n",getpid()); pid_t id=fork(); if(id<0) { perror("fork"); return 1; } else if(id==0) { while(1) { sleep(1); printf("我是一个子进程,我pid是%d,我的父进程是:%d\n",getpid(),getppid()); } } else { while(1) { sleep(1); printf("我是一个父进程,我pid是%d,我的父进程是:%d\n",getpid(),getppid()); } } return 0;
}
运行以上代码形成的可执行程序就会输出以下的内容
这时就可以看出确实我们通过父进程创建了子进程,并且在子进程内可以得到父进程的pid
那么此时一系列的问题就来了
1.为什么fork给父子进程各自不同的返回值?
在此其实这个问题很显而易见,我们创建子进程一般都是需要让子进程去完成一些工作的,那么也个父进程其实可能会创建多个子进程的,那么要在查询各个子进程是否将对应的任务完成就需要有对应子进程的pid,这也是为什么在是使用fork之后给父进程返回的是子进程的pid。其实根本的原因也就是父进程:子进程是1对多的
2.为什么一个fork函数会有两个返回值?
不管是在学习C原因还是在学习C++的时候我们都知道一个函数的返回值不管怎么样都只能有一个的,那么在fork函数当中为什么返回值能有两个呢?
其实在fork函数当中在函数的内部实际上会进行申请新的pcb、拷贝父pcb给子进程、子进程pcb放在进程list甚至调度队列当中等操作,在进行了这些操作之后就会调用return语句,但其实在fork函数内调用return之前子进程就已经被创建,并且由于return也是语句那么这时return就会被执行两遍。
3. 为什么一个变量,即==0,又大于0?为什么在if……else语句当中两个分支能同时成立?
在此我们现在的知识还无法解释这个问题,要等到之后了解了虚拟地址空间相关的知识之后再进行解答
以上的问题3我们现在还无法解释,不过在此可以先了解一下写时拷贝
其实在我们通过父进程创建子进程之后子进程是先不会将父进程的代码和数据进行拷贝的,而是要等到数据原本的数据出现修改才会将父进程的代码和数据重新拷贝一份给子进程
来看以下的代码:
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>int cnt=100;
int main()
{printf("父进程开始运行,pid:%d\n",getpid());pid_t id=fork();if(id<0){ perror("fork"); return 1; } else if(id==0) { printf("我是一个子进程,我pid是%d,我的父进程是%dcnt:%d,\n",getpid(),getppid(),cnt); while(1) { sleep(5); cnt+=10; printf("我是一个子进程,我pid是%d,我的父进程是%d,cnt:%d\n",getpid(),getppid(),cnt); } } else { while(1) { sleep(1); printf("我是一个父进程,我pid是%d,我的父进程是%d,cnt:%d\n",getpid(),getppid(),cnt);} }return 0;
}
以上代码的结构输出如下所示: 这时就可以发现父进程内的cnt变量的值是没有发生变化的,这也说明了在子进程内cnt发生变化时进行了写时拷贝。
其实以上子进程进行写时拷贝的目的就是为了保持进程的独立性
以就是本篇的全部内容了,接下来在《进程(中)》当中将继续带来进程的相关知识,未完待续……
相关文章:
Linux《进程概念(上)》
在之前的Linux学习当中我们已经了解了基本的Linux指令以及基础的开发工具的使用,那么接下来我们就要开始Linux当中一个非常重要的部分的学习——进程,在此进程是我们之后Linux学习的基础,并且通过进程的学习会让我们了解更多的操作系统的相关…...
游戏开发中的贝塞尔曲线:感受丝滑的数学之美
这是一篇vip文章,如果你还不是vip,可以移步https://www.ilikexff.cn/articles/165免费阅读。 介绍 贝塞尔曲线是计算机图形学中最重要的概念之一,以其在表示曲线时的灵活性和精确性而闻名。广泛应用于计算机图形学、动画、路径规划等领域的数学曲线。 贝塞尔曲线的数学原理基…...
Java【多线程】(6)定时器
目录 1.前言 2.正文 2.1库中定时器 2.2手搓定时器 3.小结 1.前言 哈喽大家好呀,今天继续给大家分享Java中定时器的学习,正文包括定时器的三种实现方式,正文如下。 2.正文 在 Java 中,定时器(Timer)…...
Epub转PDF软件Calibre电子书管理软件
Epub转PDF软件:Calibre电子书管理软件 一款好用的电子书管理软件,可快速导入电脑里的电子书并进行管理,支持多种格式,阅读起来非常方便。同时也有电子书格式转换功能。 第一步:添加电子书 将需要转换的电子书添加到…...
使用FastExcel时的单个和批量插入的问题
在我们用excel表进行插入导出的时候,通常使用easyexcel或者FastExcel,而fastexcel是easy的升级版本,今天我们就对使用FastExcel时往数据库插入数据的业务场景做出一个详细的剖析 场景1 现在我们数据库有一张组织表,组织表的字段…...
nginx https配置
一.https配置 HTTPS 协议是由HTTP 加上TLS/SSL 协议构建的可进行加密传输、身份认证的网络协议,主要通过数字证书、加密算法、非对称密钥等技术完成互联网数据传输加密,实现互联网传输安全保护。 1.生成证书 openssl genrsa -des3 -out server.key 20…...
git --- cherry pick
git --- cherry pick cherry pick cherry pick Cherry Pick 是 Git 中的一个操作,它允许你选择某个分支的某次(或多次)提交,并将其应用到当前分支,而不会合并整个分支的所有更改。 cherry pick 的作用 只提取某个特定的…...
虚拟机安装linux系统无法上网的解决方法
在虚拟环境中运行Linux系统时,有时会遇到网络连接问题,特别是在使用虚拟机软件如VMware或VirtualBox时。本文将详细介绍一种针对“虚拟机安装Linux系统无法上网”问题的解决方案,以CentOS 6.5为例,适用于其他基于NAT模式的虚拟机环…...
北大人工智能研究院朱松纯:“中国的AI叙事” 存在认知偏差
3月29日,在2025中关村论坛通用人工智能论坛上,北京通用人工智能学院院长,北京大学人工智能研究院、智能学院院长朱松纯表示,目前,行业对AI的讨论几乎被大模型能力所占据,而基础学科、原始创新与智能本质的研…...
Java高频面试之集合-20
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:讲讲 HashSet 的底层实现? HashSet 是 Java 集合框架中用于存储唯一元素的高效数据结构,其底层实…...
使用Qemu模拟32位ARM系统
一、环境 实验环境如下: 主机:x86_64 操作系统:Ubuntu 20.04.6 LTS Qemu版本:QEMU emulator version 4.2.1 Linux内核版本:linux-4.4.240 Busybox版本:busybox-1.35.0二、前置准备 下载 linux-4.4.240 源…...
【初阶数据结构】栈
文章目录 一、概念与结构 二、栈的实现 栈的定义 1.初始化 2.入栈 3.判断栈是否为空 4.出栈 5.取栈顶元素 6.获取栈中有效元素个数 2.销毁 三、完整码源 总结 一、概念与结构 栈: 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据…...
docker-compose部署prometheus+grafana+node_exporter
目录 docker-compose文件 配置文件 文件层级关系,docker-compose和配置文件位于同级目录 node_exporter页面json文件 涉及离线包 一.docker-compose文件 [rootsulibao prometheus]# cat docker-compose.yml version: 3services:prometheus:image: registry.c…...
maya调整全局关节显示大小
请按以下步骤操作: 在 Maya 主菜单栏中,找到 Display (显示) 菜单。 在 Display 菜单下,找到 Animation (动画) 子菜单。 在 Animation 子菜单中,点击 Joint Size... (关节大小...)。 这时会弹出一个小窗口或者直接在界面上出现…...
“屏幕“的实现_程序中如何将数据映射到硬件_C++实战
前言 程序里的数据,最后都需要将数据对象写入硬件.C/C最大的优势体现也是在这里,他既是高级语言方便被程序员使用,又能和硬件沟通. 引入 以"屏幕"的实现,总结数据映射到硬件的代码写法 分析 软件部分 1.屏幕是数据对象---一切都是数据,一切都是对象;数据有类型,屏…...
R --- Error in library(***) : there is no package called ‘***’ (服务器非root用户)
步骤 步骤一:在自己目录下创建R包安装路径步骤二:配置用户本地的R库路径步骤三:安装缺失的包(在终端)步骤四:验证安装 步骤一:在自己目录下创建R包安装路径 mkdir -p ~/R_libs步骤二࿱…...
Go中的逃逸分析
什么是逃逸? 逃逸是指一个变量本来应该分配在栈(stack)上,但由于某些原因,最终被分配到了堆(heap)上。 类比: 栈就像一个临时的快餐盒,用来存放短期使用的数据。堆就像…...
解决 Android AGP 最新版本中 BuildConfig 报错问题
在最新版本的 Android Gradle Plugin (AGP) 中,Google 对构建系统做了不少改动,可能会导致一些与 BuildConfig 相关的问题。以下是常见问题及解决方案: 常见问题及修复方法 1. BuildConfig 类完全缺失 原因:AGP 8.0 默认不再为库模…...
Rollup系列之安装和入门
Rollup Rollup.js的主要用途是将小的代码片段编译成更大、更复杂的代码,例如库或应用程序。它特别适用于将ES模块编译成不同的模块形式,如AMD、CommonJS、UMD等,以便在不同的环境中使用。 Rollup的应用场景与好处: 插件或…...
Kafka 4.0 发布:KRaft 替代 Zookeeper、新一代重平衡协议、点对点消息模型、移除旧协议 API
KRaft 全面替代 ZooKeeper Apache Kafka 4.0 是一个重要的里程碑,标志着第一个完全无需 Apache ZooKeeper 运行的主要版本。 通过默认运行在 KRaft 模式下,Kafka 简化了部署和管理,消除了维护单独 ZooKeeper 集群的复杂性。 这一变化显著降…...
MQTT之重复消息(6、在项目中遇到的问题)
项目背景: 在 Spring Boot MQTT 5.0 环境中,RTU设备向SpringBoot平台发送心跳数据、业务监控数据。同时SpringBoot平台可以向RTU设备下发指令,RTU在执行完指令之后向平台发送响应数据。 问题一、SpingBoot平台发送指令给RTU设备,RTU设备能够…...
8、linux c 信号机制
一、信号概述 1. 信号概念 信号是一种在软件层次上对中断机制的模拟,是一种异步通信方式。信号的产生和处理都由操作系统内核完成,用于在进程之间传递信息或通知某些事件的发生。 2. 信号的产生 信号可以通过以下方式产生: 按键产生&…...
Set,Map,WakeSet,WakeMap
简介 Set、Map、WeakMap 和 WeakSet 是 ES6 引入的高级数据结构,它们的底层实现和特性与传统的对象和数组有显著差异 强弱引用了解: link Set Set对象 是一种用于存储 唯一值 的可迭代集合,可存储任意类型的值(原始值、对象引用等&…...
NSSCTF(MISC)—[HITCTF 2021]PNG
相应的做题地址:https://www.nssctf.cn/problem/819 import zlib from Crypto.Cipher import AES import base64 def decode(data, key, iv): cipher AES.new(key, AES.MODE_CBC, iv) decryptByts base64.b64decode(data) msg cipher.decrypt(decryptByts) msgs…...
只出现一次的数字
这个题目动了点脑筋,由于它们时无序的,所以我们如果去找的话比较费劲,可能要循环嵌套再嵌套,所以我们先利用库中自带的sort函数进行排序,把这些数从小到大以此排列,然后我们进行判断哪个数出现了一次即可。…...
【编程中的框架】
编码中常用的框架及其使用方法和好处 框架(Framework)是一种为解决特定问题而设计的软件架构,它提供了一组预定义的组件、模式和工具,帮助开发者更高效地构建应用程序。框架通常不仅仅是方法库,它们提供了一种结构化的…...
Python-常用关键字
基础值 1. False - 意义:布尔类型假值(首字母大写) - 用法示例: if condition is False: print("条件为假") 2. True - 意义:布尔类型真值(首字母大写) - 用法示例&…...
【计算机网络】DHCP工作原理
DHCP(动态主机配置协议) Dynamic Host Configuration Protocol 基于UDP协议传输 DHCP分配IP地址的过程 (1)DHCP DISCOVER客户机请求 IP 地址: 当一个 DHCP 客户机启动时,客户机还没有 IP 地址,所以客户机要通过 DHC…...
python 原型链污染学习
复现SU的时候遇到一道python原型链污染的题,借此机会学一下参考: 【原型链污染】Python与Jshttps://blog.abdulrah33m.com/prototype-pollution-in-python/pydash原型链污染 文章目录 基础知识对父类的污染命令执行对子类的污染pydash原型链污染打污染的…...
量子计算:未来计算技术的革命性突破
在当今科技飞速发展的时代,量子计算正逐渐从理论走向实践,成为计算技术领域最具潜力的革命性突破之一。与传统计算机基于二进制的计算方式不同,量子计算利用量子比特(qubit)的叠加和纠缠特性,能够在处理复杂…...
Maven:Java项目构建与依赖管理工具
Maven 是什么 Maven 将项目开发过程和管理过程抽象成一个项目对象模型(POM),本质上是一个项目管理工具。Maven 主要用于Java项目的依赖管理、编译、测试、打包和部署等操作。 Maven的核心设计围绕标准化和自动化,通过一系列约定和…...
内积相似系数——内积度量相似系数
内积与相似系数 内积(Inner Product) 内积(Inner Product),也称为点积(Dot Product)或标量积,两个向量点积的结果是一个标量(通常是实数或复数)。 内积&…...
问题:md文档转换word,html,图片,excel,csv
文章目录 问题:md文档转换word,html,图片,excel,csv,ppt**主要职责****技能要求****发展方向****学习建议****薪资水平** 方案一:AI Markdown内容转换工具打开网站md文档转换wordmd文档转换pdfm…...
GET 和 POST 有什么区别
GET 和 POST 是 HTTP 协议中两种最常见的请求方法,它们在用途、安全性、数据传递方式等方面有显著的区别。以下是它们的主要区别: 1. 用途 • GET: • 用于从服务器获取资源(数据)。 • 是一种无状态的操作…...
AI Agent 人工智能相关公开比赛汇总
参与 AI 相关比赛是提升技术能力、接触前沿算法、积累项目经验的绝佳方式。以下是全球知名的比赛,以及适合不同水平选手的竞赛分类。 1. 全球知名 AI & 计算机竞赛 (1) Kaggle 竞赛(Kaggle Competitions) 简介:全球最知名的…...
Java 多线程编程之 Object.wait 方法(工作原理、高级特性、notify 方法与 notifyAll 方法)
一、wait 方法 1、基本介绍 wait 方法是 Java 中每个对象都拥有的方法,它继承自 Object 类 wait 方法使当前线程进入等待状态,直到其他线程调用该对象的 notify 方法或 notifyAll 方法 wait 方法必须在同步代码块中使用,否则抛出 Interrup…...
python下载m3u8格式视频
一、安装 m3u8库 pip install requests pip install requests m3u8 二、编码实现 import os import re import requests import subprocess# 下载ts文件 def down_ts_file(base_url, m3u8_url, download_dir):# 从m3u8文件中获取所有ts的分片名称信息response requests.get…...
3.30 代码随想录第三十天打卡
准备:01背包理论基础(二维) 1.有n个物品每个物品只有一个 2.完全背包是有n个物品每个物品有无限多个 3.多重背包是有n个物品每种物品个数各不相同 (1)题目描述: (2)解题思路; 1…...
01 相机标定与相机模型介绍
学完本文,您将了解不同相机模型分类、内参意义,及对应的应用代码模型 标定的意义 建模三维世界点投影到二维图像平面的过程。标定输出的是相机模型。 相机模型 相机模型可以解理解为投影模型 +...
鸿蒙学习手册(HarmonyOSNext_API16)_应用开发UI设计:相对布局
概述 RelativeContainer 就像个「智能拼图板」,帮你把界面组件像拼图一样自由组合,不用一层套一层地堆叠。每个组件可以直接「贴」到其他组件旁边或容器边缘,省去多层嵌套的麻烦,让复杂界面更高效。 举个接地气的例子 dz…...
关于为什么使用redis锁,不使用zk锁的原因
实际项目中,redis一直是最为稳定、可靠的部分,你根本不用担心redis本身的问题。至于ap模型的问题,绝大多数分布式锁只是用于避免一些极端情况的,若单一数据会有那么高的并发量你还加锁,那就要考虑这个业务场景设置的合…...
string的基本使用
C基础格式 C语言语法STL。蓝桥杯选用C11的版本。 #include <bits/stdc.h> #include <iostream> using namespace std; int main() {cout<<"Hello World!"<<endl;printf("Hello World!");return 0; } 基本数据类型 #include &l…...
论文阅读笔记——PointVLA: Injecting the 3D World into Vision-Language-Action Models
PointVLA 论文 现有的 VLA 基于 2D 视觉-语言数据表现良好但缺乏 3D 几何先验导致空间推理缺陷。传统方案:1)3D->2D 投影,造成几何信息损失;2)3D 数据集少。PointVLA 保留原有 VLA,提取点云特征…...
MySQL数据库精研之旅第四期:解锁库操作高阶技能
专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、查看所有表 1.1. 语法 二、创建表 2.1. 语法 2.2. 示例 2.3. 表在磁盘上对应的⽂件 三、查看表结构 3.1. 语法 3.2. 示例 四、修改表 4.1. 语法 4.2. 示例 五、删除表 5.1. 语法 5.2.…...
自定义一个C语言字符串取整函数
一、字符串取整的主要思路 1、遍历每个字符; 2、获得0到9的字符对应的整数值; 3、把对应位置的十进制权重相乘; 4、把所有的相乘结果相加; 5、返回相加结果; 二、主要代码 // 主要是把十进制的整数字符转成十进制变量值…...
Ruby 命令行选项
Ruby 命令行选项 概述 Ruby 是一种广泛使用的编程语言,它拥有强大的命令行工具,可以帮助开发者进行各种任务。了解 Ruby 的命令行选项对于提高开发效率至关重要。本文将详细介绍 Ruby 的常用命令行选项,帮助开发者更好地利用 Ruby 的命令行功能。 Ruby 命令行选项概述 R…...
3.29:数据结构-绪论线性表-上
一、时间复杂度 1、ADT 2、定义法计算时间复杂度:统计核心语句的总执行次数 (1)例题1,与2022年的真题对比着写 此题关键在于求和公式的转化,类型为:线性循环嵌套非线性循环 2022年那道题如果考场上实在脑…...
【百日精通 JAVA | SQL篇 | 第一篇】初识数据库
一、数据库是什么? 数据库是一类软件,数据库的作用用于管理系统(这是一款成品软件,内部应用了很多数据结构)。 二、数据库分为两大类 1.关系型数据库 对于数据的要求比较严格 通常是以表格的方式来组织数据的。(和Excel差不多) 典型代表…...
yum repolist all全部禁用了 怎么办
文章目录 步骤思考解决yum仓库全部被禁用的问题步骤思考: 检查仓库状态:运行yum repolist all,查看所有仓库的启用状态。 被禁用的仓库会显示为disabled。 启用所有仓库:可以逐一启用,或者使用命令批量启用。 例如使用yum-config-manager --enable ‘*’,但需要注意是否有…...
gnvm切换node版本号
1. gnvm下载官网 GNVM - Node.js version manager on Windows by Go 2. 安装 2.1 不存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到任意文件夹,并将此文件夹加入到环境变量 Path。 2.2 存在 Node.js 环境 下载并解压缩 gnvm.exe 保存到 Node.js 所在的文件夹。 2.…...