Linux-进程信号

Linux进程信号

  • 初步认识信号
  • 信号的存储结构
  • 信号的处理方式
  • 信号的产生
  • 硬件异常产生的信号
  • 核心转储
  • sigset_t信号集
  • 信号集的操作函数
  • 对block表的操作
  • 对pending表的操作
  • 对handler表的操作
  • 信号的捕捉
    • 用户态和内核态
  • 信号的处理过程
  • 可重入函数
  • volatile关键字

初步认识信号

生活中有哪些信号?例如:红绿灯、闹钟、手势等都叫做信号,我们接收到这些信号都会采取一些措施来应对这些信号。

操作系统里面也是有信号的,进程也是可以接受信号的,接收之后进程也会采取信号所相对应的措施。

信号可能随时产生,信号的产生对于进程来讲是异步的,所以在接收到信号时,进程可能在做优先级更高的事情,不能立即处理信号,所以进程需要有保存信号的能力,在后续合适的时间去处理这个信号。

信号会保存在进程的PCB中,进程的PCB只能由OS(operating system 操作系统的意思,后面都用OS简写代替)修改,所以无论有多少种信号的产生,最终只能由OS来完成最后的发送。

系统自定义的信号列表

1-31为普通信号:普通信号被进程接收首先保存,可以等待进程执行完优先级更高的指令再来处理信号。重点讲解普通信号。因为Linux、windows、安卓等系统都是分时操作系统,用的是分时信号

31-64为实时信号:实时信号必须马上被处理,直到处理完毕。车载系统等实时操作系统会用实时信号。
在这里插入图片描述
信号的其他相关概念:

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

信号的存储结构

进程中接收信号的时候可能不会立即处理,但是不代表不会处理,所以需要先保存起来,然后等待合适的时机去处理。所以进程需要记录一个信号在进程中是否存在,0代表不存在,1代表存在,普通信号有31个,所以PCB中信号的保存就可以用位图结构,从低位开始,第一个比特位代表1号信号,依次类推。所以给进程发送信号就是直接修改特定进程的信号位图中的特定的比特位。

实时信号在操作系统中用的是队列的存储方式(了解即可)

在PCB中有三张表分别是:pending、block、handler。

pending表:位图结构。比特的位置,表示哪一个信号,从低位到高位第一个比特位表示1号信号,比特位的内容表示是否接收到该信号,1表示接收,0表示未接收。

block表:位图结构。比特的位置,表示哪一个信号,从低位到高位第一个比特位表示1号信号,比特位的内容表示该信号是否被阻塞。

handler表:函数指针数组。该数组的下标对应信号的递达动作。SIG_DEL表示默认处理,SIG_ING表示忽略,还有就是自定义方法的函数指针。用signal自定义信号递达动作时,就是往指针数组中存放函数地址。

在这里插入图片描述

信号的处理方式

当进程收到信号时,有三种处理信号的方法:

  1. 默认方式
  2. 忽略信号
  3. 用户自定义处理

​ 用户自定义处理,有一个接口sighandler_t signal(int signum, sighandler_t handler);

在这里插入图片描述

第一个参数是信号的编号,第二个参数是一个函数指针,当进程处理signum时,会执行handler函数的方法。但是只是执行了singal方法,handler方法并不会立马执行,singal方法只是改变了信号产生时对应的执行动作。只有signum信号真正产生时,才会执行handler方法。

9号信号不能被自定义。

信号的产生

当一个进程被执行的时候,分为前台进程和后台进程,前台进程是可以被ctrl+c直接中断的,本质上就是向这个前台进程发送一个2号信号,或者ctrl+\给前台进程发送一个3号信号。

那么我们从键盘输入的时候,计算机怎么知道我从键盘里输入了数据呢?

CPU的背面有很多针脚,每一个针脚连接一个硬件,每一个针脚都有自己的编号,表示中断号,当键盘被按下的时候,连接键盘的针脚会接收到键盘被按下的信号,内存中会保存一个中断向量表,向量表中存放的都是函数指针,CPU会根据中断号去中断向量表中查询对应的函数,键盘的函数就是让OS读取数据,键盘被输入的数据就会被OS发送到前台进程。

系统接口

给对应的进程发送信号。

在这里插入图片描述

自己给自己发送信号

在这里插入图片描述

结束调用abort的进程。相当于raise(6)。6号信号即便自定义处理方式了,也会执行完自定义操作之后退出。

在这里插入图片描述

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数

在这里插入图片描述

硬件异常产生的信号

什么叫做硬件异常产生的信号呢?举个例子:

int a = 10;
a = a/0;

这两行代码如果在Linux下编译会被警告,但是还是会生成可执行程序,在运行的时候会报错(float point exception 8号信号)。然后程序就不会继续向下执行了。

这个进程是怎么接收到这个8号信号的呢?

进程被执行的时候,代码会从上到下依次执行,在CPU中有若干寄存 器,其中有一个状态寄存器会记录每行代码的状态,如果结果不正确,有数据溢出,状态寄存器由0置1。这个寄存器就有了硬件异常。OS就会给引起硬件异常的进程发送信号。如果这个进程自定义了8号信号的处理方式,然后没有退出,操作系统一直调度这个进程一直执行8号信号的自定义动作。

下面这个也是硬件异常产生的信号:

int* p = nullptr;
p = 100;//p是一个指针变量,内部有空间,可以被强行赋值
*p = 100;//野指针

*是一个解引用的操作,就是要对p指向的空间进行访问,p指向的是nullptr,也就是0号空间的地址,进程中想要对变量进行赋值,存放在虚拟地址空间需要通过页表(MMU)去访问对应的物理内存,MMU也是一个硬件,被集成在CPU中的,通过MMU访问物理内存如果访问失败,有两种失败原因,一个是MMU中没有该虚拟地址的映射关系,另一个就是有映射关系但是没有访问权限,无论哪一种。MMU都会产生硬件异常,然后OS给进程发送信号。

核心转储

Linux下有这样一个功能,在进程发生异常的时候,核心代码部分进行核心转储,将内存中进程的相关数据dump到磁盘里面,一般称为核心转储文件,以core命名。如果是云服务器的话默认是关闭的。

ulimit可以设置/显示用户可以使用资源的限制。

在这里插入图片描述

我们可以看到core file size是被设置为0的,所以默认不会进行核心转储,如果想打开core文件,可以使用ulimit -c 1024(文件大小)

在学习进程等待的时候,waitpid返回结果存储在status,status为整形,32个比特位,返回结果存储在高八位,退出信号存储在第0-7个比特位。第七个比特位就是core dump的标志位,如果为0说明核心转储是关闭的或者进程正常退出,如果为1,说明进程异常退出并且核心转储是打开的。

那怎么样才能进行核心转储呢?首先我们了解了,进程收到信号后默认动作是退出,但是信号退出动作有其中两种trem和core,有什么区别呢?core和上面的core文件有什么关系呢?

在这里插入图片描述

如果信号的action是Term,进程收到信号后会直接退出。如果是Core,OS会进行核心转储。

核心转储有什么用

如果一个进程异常退出了,退出原因是最重要的,而异常退出之后生成的核心转储文件可以帮我们很快的定位到是因为哪一行代码退出的,收到几号信号退出的。

在这里插入图片描述

生成的可执行程序默认是release,如果需要gdb调试,需要在g++编译的时候加-g选项。

gdb调试时,直接使用core-file命令,后面跟着core文件就可以定位到错误部分。

为什么核心转储是关闭的

因为核心转储文件一般都很大,这种有问题的可执行程序每执行一次就会生成一个核心转储文件,在公司里假如某个服务挂掉了,他就会一直重启,重启一次就会生成一个核心转储文件,很快就会把磁盘占满。

sigset_t信号集

信号集的操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);//初始化信号集
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);//把signo信号添加到set信号集中
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);//查看signo信号是否存在这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含
某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

对block表的操作

在这里插入图片描述

对pending表的操作

sigpending函数,把当前进程的pending表设置进set里面

在这里插入图片描述

对handler表的操作

上面介绍了signal这个函数可以修改handler表,还有一个函数也可以修改handler表。

NAMEsigaction - examine and change a signal action
SYNOPSIS#include <signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);struct sigaction {void     (*sa_handler)(int);void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask;int        sa_flags;void     (*sa_restorer)(void);
};
  • sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体;
  • 将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来
的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。 sa_flags字段包含一些选项,本章的代码都把sa_flags设为0,sa_sigaction是实时信号的处理函数。

示例代码:

void handler(int signo){cout<<"接收到了"<<signo<<"信号"<<endl;
}
int main(){struct sigaction act,oldact;memset(&act,0,sizeof(act));memset(&oldact,0,sizeof(oldact));act.sa_handler = handler;act.sa_flags=0;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask,3);sigaddset(&act.sa_mask,4);sigaddset(&act.sa_mask,5);sigaction(2,&act,&oldact);}

信号的捕捉

当进程接收到信号的时候,信号可能不会被立即处理,因为进程在做优先级更高的事情,那么什么时候会处理呢? 答案是当进程从内核态切换到用户态的时候,进程会在OS的指导下进行信号的检测和处理。

补充:当信号之前被block,block解除后对应的信号会被立即递达;

用户态和内核态

进程被加载到内存中被执行的时候分为用户态和内核态

用户态:执行我们自己写的代码时,进程所处的状态。

内核态:执行OS的代码时,进程所处的状态。例如:进程时间片到了,需要执行进程切换逻辑代码的时候。或者调用系统接口的时候。都处于内核态。

重新理解进程地址空间

在这里插入图片描述

  • [0GB,3GB]是用户空间,每个进程的用户空间是不一样的。每个进程都有自己的用户级页表。
  • [3GB,4BG]是内核空间,每个进程的内核空间都是一样的。所有进程都用同一张内核级页表。
  • 操作系统就是在进程中运行的。
  • 调用系统接口,就和调用自己写的库函数一样,都是在进程地址空间内完成调用。
  • 进程在用户态的时候,无法访问OS的数据和代码。CPU中有一个寄存器(CR3)中比特位为3表示用户态,比特位为0表示内核态。但是用户无法直接修改寄存器的状态,我们调用系统接口的时候就需要从用户态切换成内核态。所以系统调用接口内部会帮我们做这个事情,在刚开始进入系统函数的时候没有立马进入内核态,还没有触发状态检测,系统接口会先修改CR3的寄存器状态,然后再去执行函数代码。

进程是如何被调度的?

我们都知道进程是被OS管理和调度的。那么到底如何调度的呢?

OS的本质也是软件,是一个一直死循环的软件。电脑的开机操作本质上就是把OS加载到内存。上面提到了OS在每一个进程中的内核空间中运行。在没有进程被OS调度的时候,OS也有自己的进程可以执行,在centos7中叫做systemd也就是1号进程。

Linux是一个分时系统,如果内存中有若干个进程,它会让每个进程都能被执行到,所以每个进程都有时间片。如果进程的时间片到了,就需要切换到别的进程,OS如果执行某段逻辑代码,例如死循环等,怎么知道该进程的时间片到了,需要切换进程呢?

在电脑主板上有一个时钟硬件,它是用来记录时间的。就算我们的电脑关机很久并且不联网,再开机电脑的时间也不会错误。就是依赖这个时钟硬件。这个时钟硬件会每隔很短的时间给OS发送一次硬件中断。OS就会执行对应的中断处理方法,会检查当前进程的时间片,如果超时。OS会将当前进程进行保存等一系列处理。OS会调用一个叫做schedule();的系统接口完成进程的切换调度。而这一切都是OS在当前进程的内核空间内完成的操作。

信号的处理过程

上面说了,信号只有在内核态向用户态转换的时候才会被处理,那么具体过程是什么呢?

== 当我们执行用户态的代码时,会因为系统调用等原因陷入内核态,进入内核态后完成某种任务之后,内核态要向用户态转换,转换之前会检查一下block表和pending表,如果block为0,pending表为1,就会执行对应的handler方法,有三种处理方式,其中是SIG_DEL、SIG_IGN、自定义函数。其中自定义函数在进程的用户空间内定义的,所以需要跳转到用户态去执行自定义方法,之所以不用内核态是因为防止自定义函数利用内核态权限修改OS数据或代码。执行完自定义方法之后不能直接返回到用户态的上下文中,因为自定义方法并不知道用户态到内核态的位置,需要先返回到内核态(sigreturn),然后在内核态用sys_sigreturn()系统接口返回。==

内核对信号进行处理之前会先把pending表中的bit位置为0。

可重入函数

在这里插入图片描述

main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。

像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。

我们学习的大多数函数都是不可重入的。

volatile关键字

我们先看一个现象 然后解释这个关键字

int quit=0;
void handler(int signo)
{cout<<"quit from zero to one"<<endl;quit=1;cout<<quit<<endl;
}
int main(){signal(2,handler);while(!quit);cout<<"main formal quit"<<endl;return 0;}

上面这段代码正常应该是接收到2号信号 然后修改quit退出死循环 然后正常退出。看看结果:

在这里插入图片描述

实际上确实是这样,没什么不对,但是Linux下gcc的优化级别分为O0 O1 O2 O3,O3的优化级别是最高的,O0是默认编译方式,不做优化,不优化就不会有什么问题,如果把优化级别改成O1,就不一样了。

g++ -o $@ $^ -std=c++11 -O1

再进行编译运行

在这里插入图片描述

无论发送多少2号信号都不会退出,为什么?解释一下:

quit这个变量是在内存中存放的,cpu想要执行这段代码,对这个循环进行逻辑判断,需要先把quit的数据从内存中获取到cpu再进行运算,但是如果编译级别优化之后,cpu对这个quit变量使用频率非常高并且发现main函数里面只是对quit变量进行取反再判断,并没有进行修改。所以把变量存放在寄存器内部,每次判断从寄存器读取数据即可,即使变量在内存中被修改cpu也不会重新读取,这就叫做内存位置不可见了。解决方法加上volatile关键字,就是告诉cpu每次要从内存中获取数据,保证内存可见性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【日常聊聊】新年新征程:迎接学习的挑战

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 随着新的一年的到来&#xff0c;程序员们站在了全新的起点。这是一个充满机遇和挑战的时刻&#xff0…

【Java程序设计】【C00257】基于Springboot的校园二手书交易平台(有论文)

基于Springboot的校园二手书交易平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的乐校园二手书交易管理系统 本系统分为系统功能模块、管理员功能模块、卖家用户功能模块以及用户功能模块。 系统功能模块&…

Flaurm实现中文搜索

目录 摘要需求本文涉及环境情况如下解决方案最终效果文章其他链接&#xff1a; 摘要 Flarum本身对中文支持并不理想&#xff0c;但随着版本更新&#xff0c;逐渐加强了对中文的优化。然而在1.8.5版本&#xff0c;却还是不支持中文搜索网站文章内容。作者在检索了全网教程&#…

MyBatis中的XML实现和动态SQL实现

文章目录 一、XML实现1.1增1.2删1.3查1.4改 二、XML方式实现动态SQL2.1if标签2.2trim标签2.3where标签2.4set标签2.5foreach标签2.6include标签和sql标签 一、XML实现 先在新建的XML文件中写入如下内容&#xff1a; <?xml version"1.0" encoding"UTF-8&qu…

腾讯云4核8G服务器能支持多少人访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

[论文总结] 深度学习在农业领域应用论文笔记12

文章目录 1. 3D-ZeF: A 3D Zebrafish Tracking Benchmark Dataset (CVPR, 2020)摘要背景相关研究所提出的数据集方法和结果个人总结 2. Automated flower classification over a large number of classes (Computer Vision, Graphics & Image Processing, 2008)摘要背景分割…

高中数学:不等式

一、性质 1、同向可加性 2、同向同正可乘 3、正数乘方开方&#xff08;n∈Z&#xff0c;n≥2&#xff09; 常见题型 1、比较大小 分式比较大小&#xff0c;先去分母作差法比较大小带根号的无理数比较大小&#xff0c;直接两边开方因式分解&#xff08;较难&#xff09; 2、…

Matplotlib Figure与Axes速成:核心技能一网打尽

Matplotlib Figure与Axes速成&#xff1a;核心技能一网打尽 &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333; 一、Figure&#xff08;图形&#xff09;&#x1f333;&#x1f341;1. 创建Figure&#x1f341;&#x1f341;2. 添加Axes&#…

配备Apple T2 安全芯片的 Mac 机型及T2芯片mac电脑U盘装系统教程

T2 芯片为 Mac 提供了一系列功能&#xff0c;例如加密储存和安全启动功能、增强的图像信号处理功能&#xff0c;以及适用于触控 ID 数据的安全保护功能。哪些电脑配备了 T2 安全芯片呢&#xff0c;T2芯片mac电脑又如何重装系统呢&#xff1f;跟随小编一起来看看吧&#xff01; …

陪护系统|陪护小程序提升长者护理服务质量的关键

在如今逐渐老龄化的社会中&#xff0c;老年人对更好的护理服务需求不断增加。科技的进步使得陪护小程序系统源码成为提供优质服务的重要途径之一。本文将从运营角度探讨如何优化陪护小程序系统源码&#xff0c;提升长者护理服务的质量。 首先&#xff0c;我们需要对软件的设计和…

scIMC:scRNA-seq插补方法基准

在scRNA-seq中一个主要的挑战即为“dropout”事件&#xff0c;它扭曲了基因表达&#xff0c;显著影响了单细胞转录组的下游分析。为了解决这个问题&#xff0c;已经做了很多努力&#xff0c;并开发了几种基于模型和基于深度学习的scRNA-seq插补方法。但是&#xff0c;目前还缺乏…

ES6 ~ ES11 学习笔记

课程地址 ES6 let let 不能重复声明变量&#xff08;var 可以&#xff09; let a; let b, c, d; let e 100; let f 521, g "atguigu", h [];let 具有块级作用域&#xff0c;内层变量外层无法访问 let 不存在变量提升&#xff08;运行前收集变量和函数&#…

Python爬虫之Ajax数据爬取基本原理

前言 有时候我们在用 requests 抓取页面的时候&#xff0c;得到的结果可能和在浏览器中看到的不一样&#xff1a;在浏览器中可以看到正常显示的页面数据&#xff0c;但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档&#xff0c;而浏览器中…

【数据结构】哈希桶封装出map和set

利用之前的哈希桶封装出unordered_map和unordered_set。 这个封装并不简单&#xff0c;迭代器的使用&#xff0c;模板参数的繁多&#xff0c;需要我们一层一层封装。 map是一个k - v类型&#xff0c;set是k类型&#xff0c;那么就明确了如果需要封装&#xff0c;底层的tables…

pm2启动的node项目访问不了,npm start却可以访问

netstat -ntlp输入该命令&#xff0c;查看启动的服务端口是否有被监听到&#xff0c;如3001&#xff0c;4000之类的&#xff0c;是node项目启动时候自己配的那个&#xff0c; 若没有&#xff0c;则执行 pm2 delete [app-id/app-name] 先删除启动的这个项目 例如pm2 delete my…

LeetCode Python - 15.三数之和

目录 题目答案运行结果 题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可…

C语言—字符数组(3)

可能不是那么的完整&#xff0c;先凑合看吧&#xff0c;如果我学会如何修改以后&#xff0c;我慢慢回来修改的 1.编写程序实现对两个字符串的连接功能&#xff1b; 法一:不使用strcat函数,写程序直接实现&#xff0c;记得添加结束符&#xff0c;不然程序访问数组时候将变得不…

ZooKeeper安装及配置(Windows版)

步骤&#xff1a; 1.官网下载二进制版本ZooKeeper安装包。 2.解压到你要安装的目录下 3.配置 3.1进入目录 D:\Install\apache-zookeeper-3.9.1-bin 新增两个文件夹&#xff1a;data和log 3.2 进入目录D:\Install\apache-zookeeper-3.9.1-bin\conf 复制zoo_sample.cfg文件&a…

python中库的安装和卸载

1 安装库的基本方法 pip install 库名 2 批量下载(pa.txt放入库的名称和版本要求&#xff09;格式如图所示&#xff1a; pip install -r pa.txt 3 官网下载安装库 https://pypi.org/project/ 搜索后点击Download files python -m pip install C:\Users\ZHUHUA\Downloads\…

elasticsearch下载及可视化工具下载使用

elasticsearch下载及配置、启动 一、下载 Download Elasticsearch | Elastic 二、启动 双击bat即可。 出现如下说明启动成功&#xff1a; 访问测试&#xff1a; 三、注意 &#xff08;1&#xff09;因为es启动默认端口是&#xff1a;9200,所以需要检查此端口是否被占用。…