ch1_系统启动_setup.S

news2024/11/28 1:49:49

1 功能分析

大写的.s 后缀名, 是为了说明是一个16位, 实模式下的汇编语言, 小写的 s 是保护模式下的汇编语言;

1.1 使用中断,读取机器参数

setup.S 是一个操作系统的加载程序, 主要作用使用 ROM BIOS 中断读取机器系统数据, 并将这些数据保存到0X90000开始的位置, 即覆盖掉原先bootsect 程序所在地方,

所取得的参数和保留的内存位置见表 6–2 所示。

这些参数将被内核中相关程序使用,例如字符设备驱动程序集中的 console.c 和 tty_io.c程序等。

利用BIOS中断程序填下面这张表格的内容:

在这里插入图片描述
如表中,

1)保存光标的位置
2)得到扩展内存的大小
3)得到显示卡当前的显示模式
4)检测显示方式
5)读取硬盘参数表信息

1.2 setup 移动 system模块

然后, setup 程序将 system 模块从 0x10000-0x8ffff 整块向下移动到内存绝对地址 0x00000 处(当时认为内核系统模块 system 的长度不会超过此值:512KB)。

因为我们已经使用完所有的BIOS中断程序,所以由BIOS在0地址处建立的中断向量表也可以覆盖掉。

; 之前bootsect引导程序将system模块移动到(0x10000)处,
; 并把自己移动到(0x90000)处,把setup加载在它后面


; setup 程序将整个system模块移动到0x00000处,
; 即把从0x10000到0x8ffff的内存数据块整块的向内存地址低端移动了0x10000的位置

	mov	ax,#0x0000
	cld			! 'direction'=0, movs moves forward
do_move:
	mov	es,ax		! destination segment
	add	ax,#0x1000
	cmp	ax,#0x9000  ! 判断代码是否移动完成
	jz	end_move    ! 移动完成则跳转
	mov	ds,ax		! source segment
	sub	di,di
	sub	si,si
	mov cx,#0x8000  ! 循环移动,循环次数,每次循环完次数减 移动0x8000字
	rep		! 用于把内容从ds:si 复制es:di  以字节单位
	movsw           ! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中
	jmp	do_move

1.3 加载描述符表

原始的实模式下, 16位时, 寻址方式是: 段寄存器左移四位 + 偏移寄存器。
此时,加载的描述符表包含两种:

  • . 中断描述符表
  • . 全局描述符表 GDT (global descriptor table )

全局描述符表的出现, 是为了解决32位 保护模式下的寻址方式问题

GDT 表: 是使用段寄存器CS作为一个索引在一个地址表里找到32位的基地址,
然后再和偏移寄存器EIP 中的32位 数值相加,
得到最终的地址放到地址总线上去选定内存。

而为了让硬件找到这个表, GDT 表的起始地址被放在了一个GDTR 的寄存器中;


为进入保护模式做准备,

  1. 加载中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR),
  2. 开启 A20 地址线,
  3. 重新设置两个中断控制芯片 8259A,
  4. 将硬件中断号重新设置为 0x20 - 0x2f。

实模式和保护模式下的寻址方式的区别
在这里插入图片描述
a)图记住一点,段的最大长度固定为64KB;

b)图段寄存器中保存的不再是段基地址而是描述符表的索引,并且段的最大长度是可变的。

在这里插入图片描述

1.4 模式切换

setup.s 从实模式 切换到 保护模式,

设置CPU的机器状态字寄存器CR0的PE位,进入 32 位保护模式运行,并跳转到位于 system 模块最前面部分的 head.s 程序继续运行。

进入保护模式:jmpi 0,8
加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式

;进入保护模式,只是跳转到绝对地址0x00000处
; 加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式
	mov	ax,#0x0001	! protected mode (PE) bit 保护模式比特位(PE)
	lmsw	ax		! This is it!             加载状态寄存器
	;段选择符8表示请求特权0级,使用GDT第二个段描述符
	jmpi	0,8		! jmp offset 0 of segment 8 (cs)  跳转至cs段偏移地址位0处(system已经移动到0x00000处)

2 .设备的划分

2.1. 磁盘

  • 软盘: 是早期的产物, 负责从计算机上搬运出数据, 现在这个基本功能使用U盘完成了。 软盘并不是装在电脑里面的,而是可移动的,一般用来存储文件和不同电脑之间进行拷贝文件,就功能上来说它和现在的U盘是一样的,只是外形、存储原理不一样,它的容量要比硬盘小的多,比如最常用的3.5英寸的软盘容量只有1.44MB。

  • 硬盘:一般都装在机箱里面,容量较大,用来存储数据。

磁盘包括软盘和硬盘

一个磁盘由多个盘片(如下图中的 0 号盘片)叠加而成。盘片的表面涂有磁性物质,这些磁性物质用来记录二进制数据。因为正反两面都可涂上磁性物质,故一个盘片可能会有两个盘面。

在这里插入图片描述

2.2 磁道

每个盘片被划分成多个圆圈, 由内向外,圆圈逐个变大, 这样每个圆圈就形成了所谓的磁道。

在对每个圈,进行分段的划分弧度, 这样一个个分段的弧, 就形成了一个个扇区。
在这里插入图片描述

由此,

  1. 不同的磁道, 容量大小不同, 因为圆圈的大小不同;
  2. 同一个磁道下的各个扇区相同, 因为是在同一个磁道下划分的。

2.3 柱面

所有的盘面中相对位置相同的磁道组成柱面, 类似于一个圆柱面,
每个盘面对应一个磁头。所有的磁头都是连在同一个磁臂上的,因此所有磁头只能“共进退”。

在这里插入图片描述

磁盘的物理地址
可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”

可根据该地址读取一个“块”,操作如下:

① 根据“柱面号”移动磁臂,让磁头指向指定柱面;

② 激活指定盘面对应的磁头;

③ 磁盘旋转的过程中,指定的扇区会从磁头下面划过,这样就完成了对指定扇区的读/写

3. 描述符表

描述符表其实就是内存中描述符项的一个阵列。
在这里插入图片描述

描述符表有两类:

  • 全局描述符表(Gobal descriptor table-GDT)和

  • 局部描述符表(Local descri ptan⁡table−LDT)。

处理器是通过:
使用GDTR寄存器来定位GDT表
LDTR寄存器来定位当前的LDT表。

这两个寄存器以线性地址的方式保存了描述符表的基地址和表的长度。

完整setup.s 代码

!
!	setup.s		(C) 1991 Linus Torvalds
!
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
; setup从BIOS中获取数据,并将这些数据保存到0x90000开始的位置处(0x90000-0x901FF覆盖了原来bootsect程序所在的地方)
; 此时setup和system已经由bootsect引导块加载到内存中
; 
! NOTE! These had better be the same as in bootsect.s!

INITSEG  = 0x9000	! we move boot here - out of the way 原来bootsect所在段
SYSSEG   = 0x1000	! system loaded at 0x10000 (65536).   system所在0x10000处
SETUPSEG = 0x9020	! this is the current segment          本程序所在段地址

.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

entry start
start:

! ok, the read went well so we get current cursor position and save it for
! posterity.
; 保存光标的位置
; 使用BIOS中断取屏幕当前光标的位置(列,行),保存到内存(0x90000)处,2个byte
; 控制台初始化程序会到此处读取该值
; BISO 中断0x10 功能号 ah = 0x30 ,读光标的位置
; 输入:bh=页号
; 返回:返回:ch = 扫描开始线,cl = 结束开始线,dh = 行号(0x00顶端),dl=列号(0x00最左边)
	mov	ax,#INITSEG ! this is done in bootsect already, but...
	mov	ds,ax
	mov	ah,#0x03	! read cursor pos 功能号 ah = 0x30 ,读光标的位置
	xor	bh,bh
	int	0x10		! save it in known place, con_init fetches
	mov	[0],dx		! it from 0x90000.  将ds设置成0x90000(INITSEG)

! Get memory size (extended mem, kB)
; 得到扩展内存的大小
; 利用BIOS中断0x15 功能号 ah= 0x88取系统所含扩展内存大小并保存到0x90002处
; 返回: ax= 0x10000(1M)处开始的扩展内存大小,若出错CF置位,ax=出错码
	mov	ah,#0x88
	int	0x15
	mov	[2],ax      !扩展内存的大小保存到0x90002处

! Get video-card data:
; 得到显示卡当前的显示模式
; 调用BIOS中断0x10,功能号 ah = 0x0f
; 返回:ah=字符列数,al=显示模式,bh=显示当前页数

	mov	ah,#0x0f
	int	0x10
	mov	[4],bx		! bh = display page
	mov	[6],ax		! al = video mode, ah = window width

! check for EGA/VGA and some config parameters
; 检测显示方式
; 调用BIOS中断0x10, 功能号 ah=0x12,bl=0x10
	mov	ah,#0x12
	mov	bl,#0x10
	int	0x10
	mov	[8],ax      ! 0x90008 =ax
	mov	[10],bx     ! 0x9000A = 安装的显示内存,0x9000B = 显示状态
	mov	[12],cx		!0X9000C = 显卡特性参数

! Get hd0 data
; 取第一个硬盘信息
; 第一个硬盘参数表的首地址是中断向量0x41的向量值
; 第二个紧跟着对应着中断向量0x46
; 下面两个程序分别复制BIOS有关硬盘参数表,
; 第一个硬盘存放在0x90080,第二个硬盘存放在0x90090
	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x41]         !取中断向量0x41对应的地址 ,hd0参数表的地址--> ds:si
	mov	ax,#INITSEG         
	mov	es,ax
	mov	di,#0x0080          !传输的目的地址(0x9000:0x0080) -->es:di
	mov	cx,#0x10            ! 循环次数,每次循环完次数减一,共传输16个字节
	rep						! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中
	movsb                   ! 用于把内容从ds:si 复制es:di  以字节单位

! Get hd1 data

	mov	ax,#0x0000
	mov	ds,ax
	lds	si,[4*0x46]         !取中断向量0x41对应的地址 ,hd0参数表的地址--> ds:si
	mov	ax,#INITSEG 
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	rep
	movsb


! Check that there IS a hd1 :-)
; 检测是否有第二个硬盘,如果没有则把第2个清零
; 利用BIOS中断调用0x13的取盘的类型,功能号 ah =0x15
	mov	ax,#0x01500
	mov	dl,#0x81         ! dl = 驱动器号(0x8X是硬盘,0x81是第一个硬盘,0x82是第二个硬盘)
	int	0x13
	jc	no_disk1         ! 第二个不存在
	cmp	ah,#3			 ! ah =类型码 指硬盘
	je	is_disk1         ! 存在

; 第二个硬盘不存在,对第二个硬盘表清零
no_disk1:
	mov	ax,#INITSEG
	mov	es,ax
	mov	di,#0x0090
	mov	cx,#0x10
	mov	ax,#0x00
	rep
	stosb
; 第二个硬盘存在,进入保护模式,从此开始不允许中段
is_disk1:

! now we want to move to protected mode ...

	cli			! no interrupts allowed !

! first we move the system to it's rightful place
; bootsect引导程序将system模块移动到(0x10000)处,
; 并把自己移动到(0x90000)处,把setup加载在它后面
; 下面这段程序将整个system模块移动到0x00000处,
; 即把从0x10000到0x8ffff的内存数据块整块的向内存地址低端移动了0x10000的位置

	mov	ax,#0x0000
	cld			! 'direction'=0, movs moves forward
do_move:
	mov	es,ax		! destination segment
	add	ax,#0x1000
	cmp	ax,#0x9000  ! 判断代码是否移动完成
	jz	end_move    ! 移动完成则跳转
	mov	ds,ax		! source segment
	sub	di,di
	sub	si,si
	mov 	cx,#0x8000   ! 循环移动,循环次数,每次循环完次数减 移动0x8000字
	rep				! 用于把内容从ds:si 复制es:di  以字节单位
	movsw           ! rep是repeat,rep配合 movw(movsb) 就是多次复制直到cx=0为止 复制的次数放在cx中
	jmp	do_move

! then we load the segment descriptors
; 加载段描述符,设置全局描述符表和中断描述表

end_move:
	mov	ax,#SETUPSEG	! right, forgot this at first. didn't work :-)
	mov	ds,ax
; lidt指令用于加载中断描述符表(IDT)寄存器
; 中断描述符表中每一个8个字节对应每个中断发生时所需要的中断程序地址入口
	lidt	idt_48		! load idt with 0,0   
; lgdt指令用于加载全局描述符表(GDT)寄存器
; 全局描述符表中每个描述符项(8字节)描述了保护模式下数据段和代码段的信息
	lgdt	gdt_48		! load gdt with whatever appropriate

! that was painless, now we enable A20
; 开启A20地址线,为了能够访问和使用1MB以上的物理内存
	call	empty_8042    ! 测试8042状态寄存器,等待输入缓冲器空,
	mov	al,#0xD1		  ! command write 0xD1命令码表示写数据到8042的P2端口
	out	#0x64,al

	call	empty_8042    !等待输入缓冲器空,看命令是否被接受
	mov	al,#0xDF		  ! A20 on
	out	#0x60,al
	call	empty_8042    !若此时输入缓冲器为空,则表示A20线也选通 

! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
! we put them right after the intel-reserved hardware interrupts, at
! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
! messed this up with the original PC, and they haven't been able to
! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
! which is used for the internal hardware interrupts as well. We just
! have to reprogram the 8259's, and it isn't fun.

; 重新对中断进行编程

	mov	al,#0x11		! initialization sequence
	out	#0x20,al		! send it to 8259A-1  发送到8259A主芯片
;   0x00eb直接使用机器码表示两条相对跳转指令,起延时作用
	.word	0x00eb,0x00eb		! jmp $+2, jmp $+2
	out	#0xA0,al		! and to 8259A-2   再发送到8259A从芯片
	.word	0x00eb,0x00eb
;   系统硬件中断号被设置成0x20开始
	mov	al,#0x20		! start of hardware int's (0x20)
	out	#0x21,al		!送主芯片ICW2命令字,设置起始中断,要送奇端口
	.word	0x00eb,0x00eb

	mov	al,#0x28		! start of hardware int's 2 (0x28)
	out	#0xA1,al        !送主芯片ICW2命令字,从芯片的起始中断号

	.word	0x00eb,0x00eb
	mov	al,#0x04		! 8259-1 is master
	out	#0x21,al        !ICW3
	.word	0x00eb,0x00eb
	mov	al,#0x02		! 8259-2 is slave
	out	#0xA1,al
	.word	0x00eb,0x00eb
	mov	al,#0x01		! 8086 mode for both
	out	#0x21,al
	.word	0x00eb,0x00eb
	out	#0xA1,al
	.word	0x00eb,0x00eb
	mov	al,#0xFF		! mask off all interrupts for now
	out	#0x21,al
	.word	0x00eb,0x00eb
	out	#0xA1,al

! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
! need no steenking BIOS anyway (except for the initial loading :-).
! The BIOS-routine wants lots of unnecessary data, and it's less
! "interesting" anyway. This is how REAL programmers do it.
!
! Well, now's the time to actually move into protected mode. To make
! things as simple as possible, we do no register set-up or anything,
! we let the gnu-compiled 32-bit programs do that. We just jump to
! absolute address 0x00000, in 32-bit protected mode.

; 进入保护模式,只是跳转到绝对地址0x00000处

; 加载机器状态字(控制寄存器CR0),将0位置1,CPU切换到保护模式
	mov	ax,#0x0001	! protected mode (PE) bit 保护模式比特位(PE)
	lmsw	ax		! This is it!             加载状态寄存器
	;段选择符8表示请求特权0级,使用GDT第二个段描述符
	jmpi	0,8		! jmp offset 0 of segment 8 (cs)  跳转至cs段偏移地址位0处(system已经移动到0x00000处)

! This routine checks that the keyboard command queue is empty
! No timeout is used - if this hangs there is something wrong with
! the machine, and we probably couldn't proceed anyway.

; 检差键盘命令队列是否为空
; 只有当输入缓冲器为空(键盘控制器状态寄存器位1 = 0)才可以进行写命令
empty_8042:
	.word	0x00eb,0x00eb      !延时作用
	in	al,#0x64	! 8042 status port
	test	al,#2		! is input buffer full?
	jnz	empty_8042	! yes - loop
	ret

; GDT全局描述符表开始处,描述符表由多个8字节长的描述符项组成,
; 3个描述符项
; 第一项没有作用,但是必须存在
; 第二项是系统代码段描述符
; 第三项是系统数据段描述符
gdt:
	.word	0,0,0,0		! dummy  第一个描述符 不用

; 在GDT表这里的偏移量是0x80,它是内核代码段选择符的值
	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9A00		! code read/exec
	.word	0x00C0		! granularity=4096, 386

; 在GDT表这里的偏移量是0x10,它是内核数据段选择符的值
	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
	.word	0x0000		! base address=0
	.word	0x9200		! data read/write
	.word	0x00C0		! granularity=4096, 386

; 加载中断描述符表寄存器(idtr)
; 这里设置一个长度为0的空表
idt_48:
	.word	0			! idt limit=0
	.word	0,0			! idt base=0L

; 加载全局描述符表寄存器(gdtr)
; GDT表长度为2kb
gdt_48:
	.word	0x800		! gdt limit=2048, 256 GDT entries
	.word	512+gdt,0x9	! gdt base = 0X9xxxx
	
.text
endtext:
.data
enddata:
.bss
endbss:


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

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

相关文章

我为什么拒绝了一个5年测开经验的候选人

某互联网大厂的测试开发岗位招聘时,收到一位 5 年测试开发经验的候选人,是南京大学软件学院的硕士,毕业后一直在国内的互联网巨头公司从事测试框架和工具平台的开发工作。 他简历中参与开发过的测试框架和工具和当时该公司在做的项目很匹配&…

一. 编程规则

命名风格 1.不能以下划线或美元符号开头或结尾,不许使用中英文混合的模式命名. 2.必须使用驼峰命名,DO/BO/DTP/Vo/AO例外 3.常量名全部大写,单词用下划线隔开 4.抽象命名使用Abstract或Base开头,异常命名使用Exception结尾,测试类以Test结尾,枚举类名带上Enum后缀,枚举成员…

认真学习MySQL的事务日志-Redo日志

事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢? 事务的隔离性由锁机制执行。事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证。 redo log称为重做日志,提供再写入操作&#x…

考研数据结构大题整合_组一(ZYL组)_做题版

考研数据结构大题整合 目录考研数据结构大题整合一、ZYL组ZYL组一ZYL组二ZYL组三ZYL组四ZYL组五ZYL组六ZYL组七ZYL组八一、ZYL组 ZYL组一 1.一棵树有度为i的结点ni 个(i1,2,3,…m), 求叶结点的个数.(10分) ∑i1m(ni∗i)1−∑i1m(ni)\sum_{i1}^m(n_i *i…

C++单例模板:使用宏函数实现

C单例模板:使用宏函数实现 在我们日常开发中,无可避免需要使用单例模式进行设计类对象,那么实际上我们写单例格式基本都是一样的,那么每次都要写几乎一模一样的代码来实现我们需要的单例对象是不是会觉得很累?下面博主…

新体验经济@2022: 世界杯、啤酒与供应链

【潮汐商业评论/原创】 四年后,世界杯再一次刷屏,绿茵场上挥洒着汗水与泪水,而绿茵场下的观众们也在对诸如“馄饨皮”、“卡塔尔小王子”和球队输赢等话题展开着热烈地讨论。 这其中当然也包括Hans,Hans是一名忠实的足球球迷&am…

mapper-reducer编程搭建

一.虚拟机安装CentOS7并配置共享文件夹 二.CentOS 7 上hadoop伪分布式搭建全流程完整教程 三.本机使用python操作hdfs搭建及常见问题 四.mapreduce搭建 五.mapper-reducer编程搭建 mapper-reducer编程搭建一、打开hadoop二、创建mapper.py、reducer.py及参数文件1.创建 mapper.…

Android Framework 如何学习,如何从应用深入到Framework?

1、为什么要学Android Framework呢? 一方面,面试考察相关内容的可能性高。随着Android开发者越来越多,企业对Android程序员的筛选也有了更高的要求, 考察对Android底层逻辑的理解和思考就是很重要的一个方面,尤其是An…

遥感测深方法综述(一)遥感测深方兴未艾

众所周知,当前世界上许多沿海近岸区域的海图数据更新滞后,甚至直接空白,大约有50%的沿海国家不具备国家级海图制图能力,特别是发展中国家常缺乏足够的水深数据或海图,总体上的浅海测深和制图能力处于从“贫乏”到“具备…

智慧社区管理系统08(维修和柱状图显现)

目录 后端代码 实体类 mapper层 mappers sql语句 Service层 接口 实现类 Controller层 前端部分 列表显示 后端代码 实体类 package com.woniu.community.entity;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data…

跨境电商:圣诞前的狂欢,超级星期六营销白皮书

“黑五”和“网一”的促销大戏终于告一段落了。Nox聚星获悉,Adobe Analytics的初步统计显示,美国购物者在今年黑色星期五的线上支出达到创纪录的91.2亿美元,增长2.3%。今年的市场表现比预期的要好,对跨境卖家而言,是个…

01-Maven高级

回顾 知识目标 第一单元、父子工程 1、为什么使用父子工程? 1.1、痛点1:提高了代码的重用性 父控制器的存在就是为了去除重复,一个项目存在多个模块,可能由多个人开发,比如abc3个模块,3个模块都是基于…

如何设计分布式系统-分布式事务-XA?

以下为个人观点,如有纰漏敬请指正。 如何设计分布式系统-CAP和BASE理论?_技术分子的博客-CSDN博客​​​​​​ 什么是事务? 处理问题整个过程中同时具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)&am…

Fucoidan-PLGA 岩藻多糖-聚乳酸-羟基乙酸共聚物 PLGA-PEG-Fucoidan

Fucoidan-PLGA 岩藻多糖-聚乳酸-羟基乙酸共聚物 PLGA-PEG-Fucoidan 中文名称:岩藻多糖-聚乳酸-羟基乙酸共聚物 英文名称:Fucoidan-PLGA 别称:PLGA修饰岩藻多糖,PLGA-岩藻多糖 岩藻多糖修饰的壳聚糖聚乳酸-羟基乙酸共聚物[poly…

软件测试面试技巧:如何提高面试通过率?这3点一定要做到

对于想要进入到软件测试岗的新手人员来说,面试这一关是非常重要的,它直接关系着你的去留,也关系后续的期待遇问题,那么,有没有什么技巧可以帮忙提高面试通过率呢? 关于这一问题答案,我从这几个方…

云计算模式的优势

PaaS是一种云计算形式,它提供了一个平台,允许开发人员和程序员开发、运行和管理自己的应用,而无需构建和维护相关的基础架构或平台。它就像是一个地基,由专门的平台服务提供商进行搭建和运营,并将该平台以服务的方式提…

D-022 SD/TF卡硬件电路设计

SD/TF卡硬件电路设计1 简介2 SD卡和Micro接口定义3 硬件设计要点4 硬件电路实战1 简介 SD卡按尺寸可以分为:标准SD卡、MiniSD卡、MicroSD卡。SD存储卡是一种基于半导体快闪记忆器的忆器设备。它具有体积小、传输速度快、支持热插拔等优点,在便携式装置领…

无延时直播/超低延时直播画面同步性测试(实测组图)

阿酷TONY / 原创 2022-11-30 / 长沙 / 超多组图 无延时直播/超低延时直播,主要只测试延时情况,没有涉及直播产品的功能、使用操作界面,有兴趣的朋友可以加联系我实际测试哦~~~ 1.无延时直播应用场景 无延时直播/超低延时常见应用场景&a…

用字典统计序列中键和值的数量collections.Counter()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 用字典统计序列中键和值的数量 collections.Counter() 选择题 以下python代码结果错误的一项是? from collections import Counter myList[a,b,a] print(【显示】myList,myList) myDictCoun…

全波形反演的深度学习方法: 第 3 章 常规反演

本章介绍反演的基础知识, 以及工程中的常规反演. 仅供内部培训. 3.1 地震数据采集 地震勘探中常使用人工激发的振动进行数据采集. 相应装置包括: 激发器是产生震动的装置, 如炸药, 地震车 (撞击地面). 在城市道路等具有车辆会产生振动的地方, 也可以不安装这类装置;地震检波…