Linux学习笔记

news2024/11/26 23:33:12

IO操作

概述

根据用户态和内核态的划分,用户态的进程是不能直接访问各种硬件资源的,只能向内核发起系统调用,由内核完成一系列操作再切回用户进程。
用户进程每次想要访问硬件资源(包括读和写)就叫做一次IO。
IO共有5大类,分别是阻塞IO,非阻塞IO,IO多路复用,信号驱动IO以及异步IO,其中前四种都属于同步IO,在进行实际的IO操作时进程都会陷入阻塞状态。

文件描述符FD

在正式介绍IO之前,我们需要先清楚文件描述符这一定义。简单来说,文件描述符就是一个数字,这个数字是内存中文件表的索引,用户进程可以根据这一数字在内核中定位到具体想要访问的文件。
Linux系统中一共有三个与fd相关的表,分别是file descriptor table,system file table以及inode table。
每个用户进程都会维护着一个属于自己的file descriptor table,代表当前进程所关注的fd,他的key是文件描述符fd,value是该文件描述符在system file table中的索引。该表的前3位0,1,2分别代表标准输入,标准输出和错误输出,这是每个用户进程都会维护的。
system file table位于内核,系统会在其中为每次IO操作创建一个属于它自己的entry,即使是同一进程连续打开一个文件多次的情况,也会生成多个对应的system file table entry。该entry中保存着对应的实际文件在inode table中的地址,同时还保存着一个offset。
之所以要每次IO一个entry,是因为每次IO都有自己独立的offset。
inode table位于内核,其中保存着文件在磁盘中的具体位置,由于Linux系统万物皆文件的特性,每个硬件(包括键盘、鼠标、网卡等)都抽象为了一个对应的文件,因此该文件系统可以涵盖所有IO操作。
在这里插入图片描述
在这里插入图片描述
之所以要有文件描述符,是因为只有内核有直接操作文件系统的权限。在用户想要访问一个文件时,需要向内核发起一次系统调用,内核首先会在system file table中创建一个对应文件的entry,接着会将对应的索引,即文件描述符返回给用户。用户如果想对指定的文件进行后续操作,需要将对应的文件描述符再传给内核,内核就知道用户想要操作哪个文件了。
总之文件描述符相当于内核给用户提供的一种类似于通信信号一样的东西。

IO流程

在Linux架构中,无论哪种IO方式,其工作流程都是差不多的,下面以读文件举例。

  1. 首先由用户发起系统调用,并传入需要监控的文件描述符(对于大多数阻塞IO),系统切换至内核态。
  2. 内核根据文件描述符查找到对应的硬件,这里是磁盘,并由相应的DMA负责将数据拷贝至内核缓存
  3. 当数据拷贝完成,内核会将数据从内核缓冲区拷贝至用户缓冲区,并切换回用户态。
  4. 用户态进程读取数据。
    在这里插入图片描述
    一次简单的IO操作,至少需要两次上下文切换,以及一次DMA负责的数据拷贝和一个CPU负责的拷贝。
    在这里插入图片描述

阻塞IO

最简单也是最常见的IO操作,通常读取磁盘文件使用的都是阻塞IO,例如open,read,writer,close函数。调用这些函数的进程在函数返回之前会一直阻塞在调用处。
对于本地IO来说,阻塞IO通产是很快的,但对于网络IO来说,由于网络的不可控性,阻塞IO通常就不能直接使用了。
阻塞IO的优缺点都很明显,优点就是简单,方便,缺点也是过于简单,没有进行任何优化。
在这里插入图片描述
在这里插入图片描述

非阻塞IO

与阻塞IO相对应的就是非阻塞IO,非阻塞IO在进入内核态之后首先会判断对应资源是否准备完成,如果准备则进行对应操作并返回结果,如果没准备完成会立即返回一个EWOULDBLOCK错误。
用户进程可以在稍后再次发起IO请求,直到IO操作成功。
非阻塞IO的优点是可以让用户在数据准备完成之前先进行其他操作,但缺点也很明显,需要多次发起系统调用,而系统调用恰恰又是特别消耗CPU资源的行为。
在这里插入图片描述

IO多路复用

所谓的IO多路复用本质上还是阻塞IO,只不多可以在一次阻塞中同时监控多个文件描述符,并在任意文件描述符可以操作时进行返回。
Linux为我们提供了三种IO多路复用的方式,分别是select,poll,epoll,其在使用时对应的时间复杂度分别是O(n), O(n)和O(1),其中n是需要监控的文件描述符的数量。
在需要监控少量文件描述符时,select和poll的效率会更高,因为二者时间复杂度中的常数项更低,但当需要监控大量文件描述符时,epoll的效率会更高。
下面将对这三种方式做简单介绍:

在这里插入图片描述
在这里插入图片描述

Select

select内部会一直循环遍历所有文件描述符的状态,有一个fd满足监听条件或者超时为止。内部循环的时间复杂度就是O(n)。
此外在用户态获取返回值之后,如果有fd可操作,还需要再遍历一遍所有的fd从中找出可操作的fd,这也需要O(n)的操作时间。
为了提高效率,当一次遍历没有成功之后,当前线程会进入一段时间的休眠,直到被定时器或者状态变化的fd唤醒。

Poll

Poll的算法与Select类似,但在实现上有所不同,最大的区别在于Select使用的是定长数组,而Poll使用的是链表,可以同时监听更多的fd。

Epoll

Epoll与Select和poll的算法完全不同,其原理简单来说就是为每个要监听的fd指定一个回调函数,在fd状态发生变更时将其主动推送给用户。
epoll_create函数可以创建一个负责注册监听事件的entry,再通过epoll_ctl逐个注册想要监听的fd,最后调用epoll_wait进行等待,直到有可以操作的fd退给用户。
epoll提供两类监控事件,分别是EPOLLLT(水平触发)和EPOLLET(边沿触发),水平触发指的是如果当前fd内还有数据可读,那么就会一直将其返回给用户,而边沿触发则是只在有新数据可读时才会将其返回给用户。
边沿触发让用户可以暂时先不处理一些数据量较大且不是很重要的fd,而专注于更敏感的fd,但需要注意的是在读取fd时需要将所有buffer读空。
epoll的优点:每个fd只会被注册一次,降低了用户态和内核态之前的拷贝数据。epoll_wait只会返回可以操作的文件描述符,在用户态减少一次全部遍历。在监听时只是将有事件发生的fd加入其可以返回的链表,减少遍历次数。

信号驱动IO

使用场景和相关资料都很少,暂时不研究

异步IO

异步IO的关键点在于内核的数据准备和数据拷贝工作都是在后台进行的,用户进程在向内核发起IO相关的系统调用信号后便会直接返回。
内核在收到信号后会驱动DMA将数据拷贝至内核态缓存,再将数据拷贝至用户态缓存,在一切准备就绪之后才会通知用户进程,用户进程在收到信号通知后可以直接访问已经准备好的数据。
在这里插入图片描述

内存管理

存储架构

当前Linux系统内部共有四种存储空间,分别是寄存器(Register),高速缓存(Cache),主存/内存(Memory),磁盘(Disk)。
从左到右速度和价格都依次降低,越快的肯定越贵嘛。
其中寄存器为每个CPU核独享,速度非常快,但容量也很小,64位CPU通常只有64x64bit大小。一般软件开发也不会优化到这种程度,了解即可。
Cache又分为L1,L2和L3三种,也是速度和价格依次下降。其中L1每核独享,L3每个CPU的核内共享,L2取决于具体实现,一般Intel为共享,ARM为独享。
内存我们就很熟悉了,当前用RAM实现,大型服务器通常都有几百G了。内存分为内核态和用户态,其中用户态又使用虚拟内存的方式进行寻址。
磁盘存储空间就很大了,但访问速度与内存相比低了3个数量级,一般程序都不会跑到磁盘上。但磁盘上有个特殊的分区,swap分区。由于

Cache

Cache是我们在做系统优化时一般所能考虑到的最高级存储了。由于Cache本身就有三层,每层又有着不同的同步策略,Cache和Cache之间,Cache和内存之间都有着复杂的同步关系,深究起来非常非常复杂。
这里我只介绍自己了解过的一些针对Cache的开发点,其他就一点一点积累吧。

Cache Miss

这应该是我们听过的跟Cache相关的最多的词了,简单来说就是CPU在想要访问一个数据时,会先在自己的三层Cache中查找其是否存在,如果存在就拿来用,此时称为Cache Hit,如果不存在就会根据地址访问内存,将其从内存加载到Cache中,再进行访问,此时称为Cache Miss。
从内存加载到Cache的过程中,CPU是无法继续执行当前指令的,如果有其他可以执行的指令CPU会切换到其他指令,否则就会陷入阻塞状态。降低Cache Miss率可以有效提升CPU效率。
由于Cache的容量是有限的,因此需要一种算法来更新Cache中的数据,一般使用的都是LRU(最近最少使用)算法,即将使用频率最低的部分从Cache中剔除。LRU算法在内存中可以通过链表加哈希表的形式实现,Cache中的实现方法未知。

降低Cache Miss率的方法

  1. 提高CPU亲和性:在当前操作系统架构中,CPU需要经常在内核态和用户态之间切换,同时还需要经常在不同用户态线程之间切换。由于每个进程做使用的数据大概率都是不一样的,因此线程切换时,大概率会发生Cache Miss。由于通常情况下CPU核的数量一定是比需要运行的线程数少的,因此这一问题无法避免。但在某些特殊情况下,例如使用Linux服务器做转发器,此时系统运行的线程数是有限的,因此可以将一个线程绑定到CPU上,可以大大降低Cache Miss率。
  2. 使用prefetch:prefetch理论上很简单,就是在CPU使用之前提前把内存中的数据主动推送到Cache中。例如我们想要遍历一个链表,就可以在遍历当前节点时提前将下一节点通过prefetch推送到Cache中,从而大大降低Cache Miss率。但是prefetch指令会带来额外的负载,同时CPU也有自己的一套prefetch逻辑,具体使用prefetch是否有优化作用还要视具体情况而论。但Linux系统从3.0版本开始已经不再提供prefetch接口了。

Cache Line对齐

如果说Cache Miss是频率最高的词,那么Cache Line应该就是第二了。所谓的Cache Line其实就是Cache中的一行,也是Cache操作的最小单元,在64位操作系统重Cache Line一般长度为64bit。在程序开发时,为了尽可能的提高效率,通常需要Cache Line对齐。

所谓的Cache Line对齐就是让一个结构体中的成员占用的内存尽量是32bit的整数倍,当一个结构体的长度接近64或者32时,可以通过在结构体中添加padding的方法将其长度填充至32或64bit,这样可以使CPU在访问下一个相同数据时不需要跨Cache Line访问。

内存屏障

由于L1 Cache和L2 Cache通常都是每核独享的,那么不可避免的会出现同步上的问题。同时,由于CPU会对执行的指令顺序做优化,导致指令实际的执行顺序与程序中预期的顺序不一致,这也称为CPU乱序。

内存屏障就是为了解决上述问题产生的。内存屏障可以分为读屏障和写屏障,根据实现方式又可以分为CPU屏障和编译器屏障。

  1. 编译器屏障:编译器屏障的作用是可以防止编译器优化导致的指令乱序问题,常用的volatile关键字本质上就是一个编译器屏障,但编译器屏障并不能避免CPU乱序造成的问题。
  2. CPU屏障:Intel为我们提供了三种CPU屏障以供调用,分别是读屏障(lfence),写屏障(sfence)以及读写屏障(mfence)
  3. sfence:调用sfence时,当前CPU会将Store Buffer中的数据立即输入Cache中,让其他核可以访问到,同时保证sfence之前的写指令都执行完毕才会继续向下执行,也就是防止乱序。
  4. lfence:调用lfence时,当前CPU会立即读取invalid queue中的数据,将Cache中的数据全部刷新成最新版,同时也保证lfence之前的读指令都执行完毕。
    可以看到内存屏障一共有两方面的作用,一是及时同步,二是防止乱序。在某些关键字段的读写时(例如数组index)需要结合具体情况添加对应的屏障,dpdk提供的rte_rmb核rte_wmb就是基于lfence和sfence实现的。

在现在的x86系统中,及时没有读写屏障也不会有同步之间的严重问题了,具体实现我也不是很清楚,但如果在某些特殊场景下,为了避免CPU乱序,还是要加写屏障的(防止Store Load乱序)。

内存

内存按照使用功能可以分为内核态和用户态,而用户态又需要区分虚拟内存和物理内存,这里重点介绍虚拟内存以及与其相关的TLB Miss。

虚拟内存

由于用户态的所有进程共用一块物理内存,因此如何对物理内存就行分配就是首先需要考虑的问题。经过多次架构演变,Linux系统最终使用虚拟内存的方式来为各个进程分配物理内存。
虚拟内存相当于是进程和物理内存之间的一个中间层,每个进程都会维护着一个属于自己的页表,一个页表就相当于一个目录,其key为虚拟内存地址,value为物理内存地址,提供了一个从虚拟内存到物理内存的映射。
对于当前常见的64位操作系统,其所使用的寻址空间为48位,已经能够提供256T的寻址能力。Linux系统默认的一页大小为4K,所对应的页偏移(即虚拟内存和物理内存相同的地址空间)为12位,因此还需要36位的大小的页表提供查询。
为了尽可能降低页表的大小,Linux使用多级页表,一般为4级,每级提供9位的寻址能力。在使用时,会从高到低地逐次查询页表,最终定位到需要的物理内存地址。
这样做的问题也显而易见,页表默认都保存在内存中,如果四级页表都没有在Cache中,那么CPU仅仅是想简单的查询一个数据就需要访问5次内存(四次定位物理地址,一次读取数据),这样会大大降低效率。
为了解决这一问题,TLB(Translation Lookaside Buffer)应运而生。TLB相当于是一个专门为虚拟地址向物理地址映射提供服务的寄存器,其效率相当快,且每核独享。但与寄存器一样,其造价很贵,因此空间很小,如何高效利用这些空间也是系统优化中需要经常考虑的问题。

TLB Miss

与Cache Miss类似,CPU在想要获取一个虚拟地址对应的物理地址时,会先查询其TLB中的数据,如果其中没有对应的虚拟地址,也就称为TLB Miss。此时从Cache和内存中的页表查询对应地址,并将其添加到TLB中。
由于TLB大小有限,因此也要制定对应的更新算法,一般也是LRU。

TLB Miss优化

  1. 提高CPU亲和性:和Cache Miss一样,如果CPU经常切换线程,其使用的内存地址也会经常变换,TLB Miss率自然就高。
  2. 使用大页内存(Hugepage):最根本的方法还是降低页的数量,常见的大页内存有4M,1G,2G等,分别可以使页的数量降低512倍和50万倍,理论上其对应的TLB Miss率也会等比例的下降。大页内存在原理上也就是提高了页偏移,即提高了虚拟内存和物理内存的共用地址位。

Swap区间

关于虚拟内存还有一个问题,那就是每个进程都有自己的一份虚拟内存,总的虚拟内存大小是远大于实际的物理内存大小的,那么当各个进程同时使用的虚拟内存大于实际物理内存时该怎么办呢,Swap区间就是为了解决这一问题。
Swap是磁盘上的一个特殊区间,在安装系统时就已经指定了。当物理内存不足时,系统会根据更新算法将最少用到的物理内存地址暂时放到磁盘的Swap区间上,并将空闲下来的物理内存给新的程序使用。
除此之外,在系统休眠时,会将全部的内存数据存入Swap区间中,等到下次系统被唤醒时会重新从Swap区间加载全量内存数据。

用户态和内核态

概念

用户态(User Space)和内核态(Kernel Space)可以说是所有Linux开发人员都绕不开的话题,那么他们究竟是什么?有什么区别?有什么关系?又为什么这么设计?
首先需要理解什么是内核,所谓内核可以简单理解为一个特殊的软件,它支撑起了整个操作系统,有着最高的管理权限,并且可以直接与各种设备通信,可以执行ring 0级别的CPU指令集。
而内核态,就是系统为内核专门划分的一块物理内存,除内核外的软件对该内存都没有直接访问的权限,一般是物理内存的高位。
在这里插入图片描述
与之对应的用户态就是其他所有软件共享的一块物理内存,共享通过虚拟内存实现。

联系

用户态可以通过三种方式与内核进行通信:

  1. 库函数或者Shell脚本,由内核态的程序主动发起,通过预定义的接口完成通信
  2. 异常:当用户态程序发生不可预期的异常时,会被动切换到内核态
  3. 中断:当CPU收到中断信号时,也会从用户态切换到内核态
    在这里插入图片描述

原因

那操作系统为什么要这么设计呢?
主要有两点,首先为了给内核划分一片专用内存,保证在任何情况下操作系统都能跑,其次是因为有许多高度敏感的CPU指令集如果不当使用的后果是灾难性的,所以只能由提前设计好的内核调用,对外只提供对应的库函数。
这一过程被称为系统调用。每次系统调用需要两次上下文切换,分别是调用的用户态进程切换至内核态,等内核处理完成再从内核态切换至用户态。

问题

根据用户态和内核态对内存进行分区带来了许多优点,但传统架构中用户态的进程如果想要访问文件资源(磁盘和网口)需要多次的上下文切换和CPU参与的拷贝工作,这大大限制了CPU效率,为此催生了一系列的零拷贝技术。

进程和线程

进程和线程算是计算机系统里老生常谈的问题了,特别是在面试中,面试官很有可能问出的一个问题就是,说一下进程和线程的区别?
这篇文章就从概念上简单阐述一个现阶段我对这一问题的理解。
想要完整的回答这个问题,可以从是什么,为什么,怎么做的角度入手,对应的具体问题就是,进程和线程分别是什么?为什么要这么设计?以及Linux系统是怎么实现的?

概念

进程和线程本质上都是一种对程序的抽象,每个人都可能有自己的一套理解,并且各个操作系统的具体实现也会有所区别,最常见的一个定义就是,进程是资源分配的最小单位,线程是系统调用的最小单位。
对于计算机来说,资源无非就是内存和CPU,上面的定义如果说的更通俗一点就是操作系统以进程为单位为程序分配内存,以线程为单位为程序分配CPU时间。

进程的资源

在Linux系统中,内存被分为用户态和内核态,为了充分利用用户态的空间,Linux系统会为每个程序分配独立的虚拟内存,同时有一个对应的页表保存虚拟地址和物理地址之间的映射关系,在程序运行时不断的通过缺页中断来为其分配实际的物理地址。
这一虚拟地址空间,按照功能的不同又会被划分成如下几个区域:

  1. 用户自行分配和释放的堆区
  2. 系统负责分配和释放的栈区
  3. 存储全局变量和静态变量的静态区
  4. 存储常量的常量区
  5. 存储二进制代码的代码区
    其中与我们关系比较密切的是堆和栈,这也是面试一大考点
    栈是由操作系统维护的,栈中会保存函数的入参以及局部变量,线程的栈大小是有限的,可以通过配置进行修改,当前最常用的设置是2M,当创建的局部变量占用内存超过这一值时就会发生栈溢出错误。
    堆是由用户自身进行维护的,与栈相比,堆通常可以视为无限大的,用户可以在其中申请到大量内存,这些内存使用完毕之后不会被系统释放,需要由用户手动释放,否则就会引发内存泄露。
    栈内存的优点是使用方便,快捷,缺点就是空间有限,无法存储较大的数据。堆的优点是空间大,缺点是维护较为复杂,速度较慢,且容易产生内存碎片,降低内存的使用效率。

线程的资源

操作系统通常会按照线程为单位为程序分配时间片,时间片的大小取决于线程的优先级,优先级越高的线程分配的时间片大小就越大。Linux系统中通常会为IO操作密集的线程分配更高的优先级,这是因为用户对有IO操作的线程通常更为敏感。
对于用户线程来说,其分配的时间片大小可从5ms~800ms,具体取决于调度算法。

Linux系统的实现

在Linux系统中,进程和线程会统一使用task_struct进行表示,并不会做明显的区分。我们可以将一个task_struct视为一个线程,共用同一个虚拟空间的多个线程视为一个进程。
如果一个虚拟空间只有一个对应的线程,那么此线程就是一个进程,如果一个虚拟空间对应着多个task_struct,那么就可以将这一线程组视为一个进程。

进程和线程的切换

我们都知道进程和线程切换时需要进行上下文的切换,这也是频繁切换进程或线程时的主要性能损耗原因,那么进程和线程的上下文都有什么呢?
对于进程来说,其上下文的切换包含两方面,一是空间地址的切换,需要切换到新的虚拟地址空间,也就是切换到新的页表。二是处理器状态的切换,又叫硬件上下文切换,也就是保存当前的寄存器状态,并切换到下一个将要执行的进程的寄存器状态。
对于线程来说,如果切换前和切换后的线程属于同一个进程,那就说明他们有着相同的虚拟空间,也就只需要进行处理器状态的切换了。这也是多线程程序比多进程程序快的原因。
在Linux系统中,切换虚拟地址空间就是改变task_struct结构中一级页表的物理地址对应的字段,这一切换是很快的,但是在虚拟地址空间切换之后,TLB中的所有字段都需要失效,不然会让CPU访问到错误的物理地址。而这会已发大量的TLB Miss,这才是进程切换的最大开销。为了应对这一情况,处理器引入了ASID机制,大概就是为TLB中的表项打上一个进程的标记,这样就可以保留一部分进程对应的表项,利用这一机制可以降低TLB Miss发生的概率。

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

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

相关文章

梦开始的地方——C语言文件操作详解

文章目录C语言文件操作1. 什么是文件?2.文件指针3.文件的打开和关闭4.文件的顺序读写fgetc&fputcfgets&fputsfread&fwritefscanf&fprintfscanf/fscanf/sscanf 对比 printf/fprintf/sprintf5.文件的随机读写(fseek&ftell &rewind)6. 文件结束…

兄弟机床联网

一、设备信息确认 1、确认型号 看面板颜色: 面板如果是彩色屏幕,大概率是可以做联网采集的。如果是黑白屏则需要进一步确认设备名牌。 看名牌: 名牌一般在设备后面,可以看到数控系统的品牌,一般C00和B00都是可以直接…

怎么写综述类论文? - 易智编译EaseEditing

一、确定综述的主题 每篇综述都应该有一个观点,即想要表达的事物。一篇综述不是简单的对相关发现的罗列。综述的真正功能是迈出下一步。已有的研究告诉了我们什么?以及我们下一步要怎么做? 确定综述的主题在撰写过程中是最重要的一步。这会…

快速上手几个Linux命令

Linux操作系统有很多功能,我们有很多方式可以使用这些功能,其中最简单和直接的方式就是命令行(Command Line) 用户与密码 当我们打开一个新系统的时候,第一件要做的事就是登录。系统默认有一个 Administrator 用户&a…

Vue学习:Vue中的数据代理

<!-- 准备容器 --><div idroot> <h2>学校名称&#xff1a;{{name}}</h2><h2>学校地址&#xff1a;{{adress}}</h2></div><script>const vm new Vue({ el: #root,data: {name:Jhon,adress:street 10},});</script> vm上…

8.javase_数组2

一 . 二维数组 (1)二维数组 元素为一维数组的数组 (2)定义格式&#xff1a; 数据类型[][] 变量名; int[][] arr; 数据类型 变量名[][]; int arr[][]; 数据类型[] 变量名[]; int[] arr[]; 二.二维数组初始化 (1)静态初始化 格式&#xff1a;数据类型[][] 变量名 new 数据类型…

PNG怎么转成PDF格式?这两种方法一定要尝试一下

图片文件是我们经常使用到的一种文件类型&#xff0c;但是我们通常会有很多的图片需要同时进行发送&#xff0c;这时候发送给别人就不是很便利了&#xff0c;我们一般会需要通过微信进行发送&#xff0c;但是大家都知道&#xff0c;微信一次只能发送九张图片&#xff0c;有时候…

【CDC跨时钟域信号处理】快时钟域到慢时钟域-单bit

快时钟域到慢时钟域分两种情况&#xff1a; 1、允许采样丢失&#xff1a;直接采用同步器即可。 2、不允许采样丢失&#xff1a;原理是保证快时钟域的信号宽度满足一定的条件&#xff0c;使得慢时钟域有足够的时间采样到。 对于情况2有两种方法解决&#xff1a;①信号展宽边沿检…

接口管理测试繁琐复杂?何不试试Eolink

一、前言 作为一名测试从业者&#xff0c;深刻的明白接口测试在项目过程中是多么重要的一个环节。通过页面进行的UI测试会因为界面不稳定而导致用例维护非常困难。另外&#xff0c;在检查系统的安全性、稳定性上面也是尤为重要的环节&#xff0c;这些也是无法通过前端测试的&a…

react-native webstorm 无法启动 Android 模拟器

react-native webstorm 无法启动 Android 模拟器 一、问题描述 在 安装完 Android Studio 和 模拟器之后&#xff0c;WebStorm 启动 react-native 项目时提示如下&#xff1a; No emulators found as an output of emulator -list-avds.二、解决办法 官方环境安装说明&#x…

汽车OTA技术门槛提升,具备软硬一体化能力的Tier1优势凸显

在软件定义汽车的大背景下&#xff0c;无论是传统车企还是造车新势力都在加大OTA的布局力度&#xff0c;整车OTA的普及应用已经成为必然趋势。 高工智能汽车研究院监测数据显示&#xff0c;2022年1-6月中国市场&#xff08;不含进出口&#xff09;乘用车标配搭载OTA上险量为40…

移动硬盘raw怎么办?一招教你解决RAW格式的文件

RAW文件格式是一种特殊的文件格式。RAW表示未处理&#xff0c;因此RAW也指未格式化的磁盘。移动硬盘里有 RAW格式的文件。这是什么原因造成的&#xff1f;以及我们该如何把移动硬盘raw里面的文件给恢复回来&#xff1f;来看看下面的解说&#xff0c;一起寻找解决方法吧&#xf…

阿里云国际站云计算-负载均衡SLB介绍-unirech

阿里云国际站的负载均衡SLB&#xff08;Server Load Balancer&#xff09;是一种对流量进行按需分发的服务&#xff0c;通过将流量分发到不同的后端服务器来扩展应用系统的吞吐能力&#xff0c;并且可以消除系统中的单点故障&#xff0c;提升应用系统的可用性。 阿里云国际站的…

研究研究 ES_OEMCONVERT 标志

ES_OEMCONVERT 这个标志&#xff0c;主要是用在 16 位 Windows 系统上。下面是一篇 MSDN 上的文章中对它的一段描述&#xff1a; ES_OEMCONVERT 会导致输入到编辑控件中的文本从 ANSI 转换为 OEM&#xff0c;然后再转换回 ANSI。这可确保在应用程序调用 AnsiToOem 函数将编辑控…

【在Vue脚手架项目中使用axios】

目录 1. 安装axios 2. 在main.js中添加配置 1. 安装axios 首先&#xff0c;需要安装axios&#xff0c;则在终端窗口中&#xff0c;在当前项目文件夹下&#xff0c;执行安装命令&#xff1a; 如果没有权限进入C盘找到cmd的执行软件&#xff0c;用管理员启动&#xff0c;进入目…

作为外贸业务员,为什么我经常随机轻松 就“捡“到精准潜在客户

心里夹杂着很多情绪和想法&#xff0c;沉浸在客户背调里面走不出来&#xff0c;但我还是决定不得不暂停下得心应手的google背调&#xff0c;记录一下此时此刻的想法。 01 我曾好多次在文章里表露出做外贸业务背调是非常关键的一环&#xff0c;而在背调里一些细微的关键信息非常…

自适应滤波器更新算法-EP3

文章目录1、PNLMS和IPNLMS算法1.1 算法原理2.2 算法分析2、一种改进的时变参数的比例自适应滤波算法2.1 算法原理2.2 算法分析2.3 算法性能评价标准2.3.1 均方误差(Mean Square Error, MSE)2.3.2 失调(Misalignment, MIS)2.3.3 回声衰减系数(Echo Return Loss Enhancement, ERL…

【音视频开发】为什么无损音频会有44.1Khz这样的奇葩采样率?

文章目录一、 问题&#xff1a;为什么无损音频会有44.1Khz这样的奇葩采样率&#xff1f;二 、PCM流程2.1 PCM流程2.2 PCM量化方式2.2 量化位数2.3 比特率三、答疑解惑3.1 使用采样定理来解释3.2 以影片磁带录音&#xff1f;硬件限制而来的 44.1kHz3.3 关于44100和质数的关系四、…

【关于检查请求参数的基本有效性】

目录 检查请求参数的基本有效性 检查请求参数的基本有效性 在服务器端项目中&#xff0c;可以通过spring-boot-starter-validation对请求参数进行检查。 在客户端项目中&#xff0c;Element UI的示例表单中都有对各控件&#xff08;例如输入框、选择框等&#xff09;的检查。…

[附源码]Python计算机毕业设计Django校园生活服务平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…