Linux0.12内核源码解读(2)-Bootsect.S
作者:小牛呼噜噜 | https://xiaoniuhululu.com
计算机内功、源码解析、科技故事、项目实战、面试八股等更多硬核文章,首发于公众号「小牛呼噜噜」
文章目录
- 回顾计算机启动过程
- 8086、80x86是什么意思?
- 寄存器初始化CS:IP
- CPU是如何和ROM相连的?
- 加载MBR到内存中
- bootsect.S具体干了什么?
- 设置段基址 & 内存分段机制
- bootsect的"再次搬家"到0x90000
- 加载setup.s到内存0x90200
- 加载system到内存0x10000
- 尾语
大家好,我是呼噜噜,在上一篇文章聊聊x86计算机启动发生的事?我们了解了x86计算机启动过程,MBR、0x7c00是什么?其中当bios引导结束后,操作系统接过计算机的控制权后,发生了哪些事?本文将揭开迷雾的序章-Bootsect.S
回顾计算机启动过程
我们先来回顾一下,上古时期计算机按下电源键的启动过程,这里以8086架构为例:
8086、80x86是什么意思?
有许多人不知道 经常遇到的8086、80x86是什么意思?我们简单科普一下:
- 8086是Intel公司推出的最早,也是最流行的面向个人电脑的CPU型号
- x86泛指一系列基于Intel 8086且向后兼容的中央处理器指令集架构,由于以“86”作为结尾,因此其架构被称为"x86"
- 80x86也就是在8086基础上的增强版,包括80286,80386,80486,其后面就是我们所熟悉的奔腾、酷睿、i5、i7等等
寄存器初始化CS:IP
相比于上一篇文章聊聊x86计算机启动发生的事,我们这里再讲细致点,当计算机一按下电源后,8086CPU就处于实模式的状态,此时会将CPU的寄存器初始化为CS=0xFFFF;IP=0x0000
,也就是实际物理地址0xFFFF0
(CS左移4位+IP)
CS : 代码段寄存器;IP : 指令指针寄存器。CS:IP指向的内容 会被CPU当做计算机指令去执行
那么从地址0xFFFF0
中取出来的指令是什么?我们知道当电路通电后,内存是一片空白的,内存断电后 数据是无法保存的,所以BIOS程序需要事先被刷入只读存储器ROM中。物理地址0xFFFF0
就是指向这样一段BIOS ROM
CPU是如何和ROM相连的?
那么问题又来了,CPU是如何和ROM相连的?CPU 不仅和ROM相连,还和RAM(俗称内存),IO接口等设备相连,他们是通过总线相连。还好当时笔者将计算机组成原理好好复习了一遍,不然这部分真挺难理解的。
总线是贯穿整个系统的是一组电子管道,是连接各个部件的信息传输线,是各个部件共享的传输介质,称作总线,它携带信息字节并负责在各个计算机部件间传递。
总线按系统总线传输信息内容的不同,又可以分为3 种:数据总线、地址总线和控制总线。我们这里用到的就是地址总线,把 0xFFFF0 作为 CPU 的地址总线信号传输出去,去这个地址总线对应的位置处找
由于计算机有多个设备,必然会存在多个设备同时竞争总线控制权的问题,这时候就需要**总线仲裁,**让某个设备优先获得总线控制权,获得了总线控制权的设备,才能开始传送数据。未获胜的设备只能等待获胜的设备处理完成后才能执行。
我们简单总结一下:当总线仲裁器仲裁通过后,CPU可以依靠地址总线寻址,找到对应设备ROM上地址0xFFFF0
处的内容。
拓展可见:什么是计算机中的高速公路-总线?
加载MBR到内存中
当BIOS自检完成,设置启动顺序后,利用 BIOS 的输入功能将启动磁盘的启动扇区MBR(也叫第一扇区,主引导记录)的内容原封不动地搬到内存的0x7C00
地址处,并设置CPU寄存器CS=0x07C0,IP=0x0000
。到这一步,计算机的控制权将交到操作系统手中!
为什么是0x7C00这个地址?如何得出?别再问了,本文不再解释了,具体看笔者的上一篇文章聊聊x86计算机启动发生的事
对于Linux0.12来说,第一个程序Bootsect.S 编译成二进制后,需要事先放到主引导记录MBR中,MBR大小就是一个扇区的大小512字节,如果这512字节的最后两个字节是0x55AA
,表明这个设备可以用于启动。只有这样我们BIOS才能识别它,才能把bootsect.S加载到内存中。
如果不是0x55和0xAA,表明设备不能用于启动,控制权于是被转交给"启动顺序"中的下一个设备。如果到最后还是没找到符合条件的,直接报出一个无启动区的error。
下面我们看下操作系统编译后,存放在储存设备(硬盘)的模块分布:
先简单介绍一下,不必深究,后续文章会娓娓道来:
- bootsect.s的主要作用就是加载操作系统,把操作系统从硬盘中,加载到内存里去
- setup.s的主要作用:首先获得光标,内存,显卡,磁盘等硬件参数存放在内存空间中,方便后续程序使用;临时建立gdt、idt表,并且从实模式进入到了保护模式
- 在linux0.12源码,boot目录下还有一个head.s,在上图中被归于system模块,属于操作系统主体文件,主要是进行进入保护模式之后的初始化工作
- system模块:就是操作系统的主体,比如文件系统,IO,进程等模块。 Linux0.12 内核 system 模块大约占随后的 260 个扇区。
更多精彩文章在公众号「小牛呼噜噜」
bootsect.S具体干了什么?
bootsect的主要作用就是加载操作系统,把操作系统从硬盘中,加载到内存里去,我们下面结合bootsect.s的源码一起来看看bootsect.S具体干了什么?
呼噜噜这里整个过程先汇成了图,大家配合图去阅读下文,对照起来,更容易理解
设置段基址 & 内存分段机制
要想bootsect启动,需要让BIOS将bootsect.s 从硬盘的MBR中搬到 内存位置0x7c00
处,大小512个字节。当bootsect被BIOS加载到内存后,计算机的控制权就到操作系统bootsect的手上了。
entry start ! 告知链接程序,程序入口是从start 标号开始执行的
start:mov ax,#BOOTSEG !BOOTSEG=0x7c0 , 将 ds 段寄存器置为 0x7C0mov ds,ax !再将 ax 段寄存器里的值复制到 ds 段寄存器里mov ax,#INITSEG !SETUPSEG=0x9000,将 es 段寄存器置为 0x9000mov es,ax !再将 ax 段寄存器里的值复制到 es 段寄存器里mov cx,#256sub si,sisub di,direpmovw jmpi go,INITSEG
我们可以看到CPU实际执行第一句的代码 mov ax,#BOOTSEG !BOOTSEG=0x7c0
,这是汇编写的,其实这里的0x7c0
对应的就是我们上文的地址0x7C00
0x7c0
是段地址,0x7C00
是其实际的物理地址,0x7c0
左移四位就是0x7c00
,这就是内存寻址-分段机制
那么大家一定会有疑问内存为什么分段?
计算机内存究竟是什么?其实它就像数组一样,咦有人不懂数组是什么,那么我们可以再头脑风暴一下,内存其实就像纸带一样,我们来看下上古时期的计算机:
穿孔纸带,图片来源于网络
纸带上有一个个孔,这样大家可能还看不明白,我们再来看一张图:
这些孔排列组合其实就是二进制数,纸带其实就是储存数据的介质,那么内存就是足够长的“纸带”
在现代计算机中,内存它使用的是DRAM芯片,也叫动态随机存取存储器,即只需给出地址,就能直接访问指定地址的数据,这一点特别像数组,所以许多材料都是用数组来画内存图
那么CPU访问内存明明可以直接通过地址访问内存,为什么还要分段?其实这又是一个历史因素导致的,让我们回到"分段"首次出现的时候:"分段"是从Intel 8086芯片开始的,8086又是你…
由于8086那个时代CPU、内存都很昂贵, CPU 和寄存器等宽度都是 16 位的,其可寻址2的16次方字节,也就是64kb,然而8086有20根地址线,可寻址的最大内存空间是1MB。CPU和寄存器的寻址能力远远不能满足使用,于是机智的祖师爷们,采用了分段技术
分段,为解决这个问题,8086引入段寄存器,如CS、DS、ES、SS
。通过段基址+段内偏移地址的方式生成20位的地址,扩大寻址能力,从而实现对1MB内存空间的寻址。由于这样程序中指令了只用到16位地址,缩短了指令长度,也变相地提高了程序执行速度。
- CS:代码段寄存器,存放代码段的段基址
- DS是数据段寄存器,存放数据段的段基址
- ES是扩展段寄存器,存放当前程序使用附加数据段的段基址,该段是串操作指令中目的串所在的段
- SS是堆栈段寄存器,存放堆栈段的段基址
- 80836还新增2个寄存器,FS标志段寄存器、GS全局段寄存器。
使用段地址还有一个好处是 程序可以重定位,那个时候的计算机可没有虚拟地址之说,只有物理地址,访问任何存储单元都直接给出物理地址。这就带来一个问题: 如果此时计算机多道程序并发运行,程序中的地址都是实际物理地址,这些程序编译出来的程序运行地址是相同的,计算机只能运行一个程序。
重定向: 将程序中指令的地址改成另一个地址,但该地址处的内容还是原内存地址处的内容。这样程序指令虽然还是物理地址,但程序能够并发运行了。
1982年处理器80286,首次提出保护模式概念,为了保持兼容性,所以同样支持内存分段管理,将8086这种称为实模式,最大的区别是物理内存地址不能直接被程序访问,这块非常重要,篇幅也较长,笔者先挖坑,后续系列文章再单独出一篇。
咳咳,拓展的有点多了,赶紧让我们回到bootsect源码处
mov ds,ax
这句话代码的意思就是:将 ax 段寄存器里的值复制到 ds 段寄存器里。ds在上文我们提到,8086特地为采用内存分段机制,引入的段寄存器。ds具体表示 数据段寄存器,存放数据段的段基址
换句话说,就是将段基址设为0x07c0
,那么后续数据段程序中只需写段内偏移地址,就能访问实际物理地址了。比如后续程序中出现mov ax,0x01
,0x01
其实是[ds:0x01]
,那么ax的实际物理地址= 0x07c0 <<4 + 0x01
。将ds寄存器段基址设置好后,其实就是方便之后程序访问内存,访问的数据的内存地址都先默认加上 0x7c00
,然后再去内存中寻址。
如果实际编程时,代码段的起始地址一般放到 CS寄存器,虽然CPU没有强制规定代码段、数据段等分离。
mov ax,#INITSEG
,mov es,ax
将 ax 段寄存器里的值0x9000
复制到 es 段寄存器里,和ds赋值同理,不再赘述。需要注意的是8086无法直接给段寄存器进行赋值,需要使用通用寄存器来当中介(一般使用ax)
bootsect的"再次搬家"到0x90000
接着bootsect自己把自己从内存位置0x7c00
处,搬到0x90000
处,这次可没BIOS帮忙了,得自食其力
start:mov ax,#BOOTSEG mov ds,ax mov ax,#INITSEG mov es,ax mov cx,#256 ! 设置移动计数值=256 字(512 字节);sub si,si ! si寄存器 清零sub di,di ! di寄存器 清零rep ! 重复执行并递减 cx 的值,直到 cx = 0 为止。movw ! 即 movs 指令。从内存[si]处移动 cx 个字到[di]处。//一次移动两个字节,256B*2=512B
mov cx,#256
将cx 寄存器的值赋值为 256,单位是字(Word), 1 word=2Byte
sub si,si
是si寄存器 清零操作,sub
是汇编语言中的一种运算指令,它用来执行减法运算,并将结果存储到被减数(前者)上去。比如sub a,b
就是a = a-b
。再结合前面的ds,es,那么此时si的段地址ds:si = 0x07C0:0x0000
,同理di的段地址es:di = 0x9000:0x0000
rep
就是重复执行后一条指令,movw
就是复制的意思。rep movw
就是重复多次搬运
我们可以知道这段的总体意思就是:循环256次,反复将段地址0x07C0:0x0000
的内容一个字一个字的复制到段地址0x9000:0x0000
处,直到寄存器cx为0。这样就实现了bootsect的"自我搬运",把实际物理内存地址0x7c00处
512个字节的内容全部复制到实际物理内存地址0x90000处
。
那为啥bootsect还要"多此一举" 将自己从0x7c00
,搬到0x90000
处?
- 操作系统system后续最终是要从物理内存起始位置处
地址0
开始存放,好处是让system代码中的地址对应上实际的物理地址。- 一般要留
512KB
的内存空间放操作系统system,会覆盖0x7c00地址的内容,所以需要把bootsect代码搬到内存更高处。
加载setup.s到内存0x90200
当上面bootsect完成自我搬运后,紧接着执行jmpi go,INITSEG
,jmpi有段间跳转的作用。这里 INITSEG 指出跳转到的段地址0x9000
,标号 go 是段内偏移地址。
其实就是执行完jmpi go,INITSEG
后,CPU已经移动到内存0x90000+go
位置处的代码中 执行。为啥要加go?其实此时bootsect编译后的二进制内容,已经搬运到内存0x90000
处,但是我们不能再从头执行start: mov ax,#BOOTSEG
操作,而是从go: mov ax,cs
处代码继续执行下去。
jmpi go,INITSEG ! 段间跳转。这里 INITSEG 指出跳转到的段地址,标号 go 是段内偏移地址。go: mov ax,cs mov dx,#0xfef4 ! arbitrary value >>512 - disk parm sizemov ds,axmov es,axpush ax ! 临时保存段值(0x9000)mov ss,ax ! put stack at 0x9ff00 - 12.mov sp,dxpush #0 ! 置段寄存器 fs = 0。pop fs ! fs:bx 指向存有软驱参数表地址处(指针的指针)mov bx,#0x78 ! fs:bx is parameter table addressseg fslgs si,(bx) ! gs:si is sourcemov di,dx ! es:di is destinationmov cx,#6 ! copy 12 bytescldrep ! 复制 12 字节的软驱参数表到 0x9000:0xfef4 处。seg gsmovwmov di,dxmovb 4(di),*18 ! patch sector countseg fs ! 让中断向量 0x1E 的值指向新表。mov (bx),diseg fsmov 2(bx),espop axmov fs,axmov gs,axxor ah,ah ! reset FDC 让中断向量 0x1E 的值指向新表。xor dl,dlint 0x13
上述主要是将 寄存器DS、ES 和SS 重新设置为CPU移动后,代码所在的段处0×9000
,设置SP栈寄存器0xfef4
栈指针要远大于512字节偏移(即 0x90200 )处都可以,一般setup程序大概占用4个扇区,这样栈顶段地址ss:sp
和现有的代码足够远 ,防止后续栈操作覆盖掉已有的代码。
还有BIOS 设置的中断 0x1e 的中断向量值等操作。这边和主干操作不太相干,简略过一下,主要就是把这些寄存器重新设置好值,方便后续使用。
更多精彩文章在公众号「小牛呼噜噜」
接下来紧接着将setup.s 加载到内存0x90200
处
load_setup:xor dx, dx ! 驱动器drive 0, 磁头head 0mov cx,#0x0002 ! 扇区sector 2, 磁道号track 0,从第二个扇区开始读mov bx,#0x0200 ! 偏移address = 512, in INITSEG ,表示读到0x90200mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors ,SETUPLEN是 4个扇区int 0x13 ! read itjnc ok_load_setup ! ok,就跳到ok_load_setuppush ax ! dump error codecall print_nl ! 屏幕光标回车mov bp, spcall print_hex ! 显示十六进制值pop ax xor dl, dl ! reset FDCxor ah, ahint 0x13j load_setup ! j 即 jmp 指令,失败就再跳转到load_setup,重复执行
那怎么简单高效将磁盘里的内容加载到内存中呢?linus这里用的是bios的中断程序,因为此时bios还在内存中,可以为我们所用,0x13
号中断 在BIOS中是可以访问软盘、IDE、ROM、远程磁盘服务的作用。
这里0x13 和C语言中的函数调用是很像的,不过需要注意的是它的参数只能通过寄存器去传参,而C语言函数调用不仅可以寄存器传参,还可以栈传参。所以0x13的参数就是其前面的dx,cx,bx,ax寄存器的值,另外磁盘只认磁头磁道扇区,如果给个地址,磁盘是不识别的,磁盘一副不太聪明的样子。
另外xor
对两个操作数进行逻辑(按位)异或操作,并将结果存放在目标操作数,xor dx,dx
也是一个置零操作,指定驱动和磁头
那么我们连起来,这段主要是让bios 0x13号中断处理程序 从磁盘的第2扇区开始读,接连读4个扇区的内容到内存0x90200
处中。成功就跳转到ok_load_setup
,没成功就回到load_setup
,重复执行上述操作。
加载system到内存0x10000
当bootsect成功将setup.s搬到内存0x90200
处后,CPU从ok_load_setup
处继续执行指令。接下来就是需要将整个操作系统system(head.s+其他文件,大约260个扇区)的内容加载到内存0x10000
处,下面我们就具体看下代码是如何实现的:
ok_load_setup:! Get disk drive parameters, specifically nr of sectors/track
!提示这面段代码功能是:利用BIOSINT 0x13 中断,来来取磁盘的一些参数,比如是取每磁道扇区数,并保存在
位置 sectors 处xor dl,dlmov ah,#0x08 ! AH=8 is get drive parametersint 0x13xor ch,chseg cs !表示下一条语句的操作数在 cs 段寄存器所指的段中。它只影响其下一条语句mov sectors,cxmov ax,#INITSEGmov es,ax !取磁盘参数中断改了es寄存器的值,这里重置es的值! Print some inane message 提示下面这段功能是:打印一些消息mov ah,#0x03 ! read cursor pos 读取当前光标的地址xor bh,bhint 0x10 ! bios 0x10中断,其作用:在屏幕上显示字符和字符串mov cx,#9mov bx,#0x0007 ! page 0, attribute 7 (normal)mov bp,#msg1 ! msg1的内容是: .byte 13,10(换行+回车) .ascii "Loading"mov ax,#0x1301 ! write string, move cursorint 0x10! ok, we've written the message, now
! we want to load the system (at 0x10000) 加载system到内存0x10000mov ax,#SYSSEGmov es,ax ! segment of 0x010000call read_it ! 读磁盘上 system 模块call kill_motor ! 关闭驱动器马达call print_nl ! 光标回车换行... 省略非主干代码...! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:jmpi 0,SETUPSEG !bootsect程序到这里就结束了,跳转到0x9020,同时setup获得控制权
这里int 0x10
号中断,其作用是 在屏幕上显示字符和字符串,由于操作系统比较大,加载需要时间,这时在屏幕上显示提示信息"Loading"
这里将操作系统加载到内存中,是通过子程序read_it
来实现的,read_it就不具体展开了,比较复杂。我们需要知道由于操作系统比较大,一个磁道是远远放不下的,另外磁盘是不认地址的,在搬运过程中,需要进行磁道、扇区和磁头的计算,特别是一个段的大小是64k,如果放不下,需要更换段地址。如果不更换段地址,会从该段地址0字节开始重新写,这样会覆盖之前的内容。
那为什么一个段的大小是64KB呢?
我们知道在8086CPU中,其内存地址是表示为段基址+段内偏移地址,其中偏移地址使用一个16位的二进制数表示,表示范围0000~FFFF
,所以总共有2^16(2的16次方)=64K个不同的地址,一个内存最小单元是字节Byte,所以一个段大小为64KB
jmpi 0,SETUPSEG
,bootsect程序到这里就结束了,跳转到内存地址0x90200
,同时setup获得控制权
为了帮助大家理解,呼噜噜这里又把本篇文章全部串起来,大家可以根据下面这张图重新回顾一下bootsect整个工作流程:
额外补充一下:
boot_flag: .word 0xAA55
最后2个字节是0xAA55
,由于bootsect是采用AT&T汇编,小端显示的,实际上就是0x55AA
与前文MBR那边前后呼应
这也说明了操作系统在开始加载到内存的程序中,得与内存地址一一对应, 不能多一个字节,也不能少一个字节!!!
尾语
本文主要讲解了bootsect.S的主要工作流程,Linux0.12虽然和如今的Linux6.x内核相比显得过于简陋,但麻雀虽小五脏俱全,它是我们打开操作系统大门的钥匙,后面让我们看看setup.s获得计算机的控制权后,会发生什么?
最近实在太忙了,后面随缘更新,留言可催更(bushi)~~
参考资料:
《Linux内核完全注释5.0》
《操作系统真象还原》
https://elixir.bootlin.com/linux/0.12/source/boot/bootsect.S
https://files.embeddedts.com//old/saved-downloads-manuals/EBIOS-UM.PDF
本篇文章到这里就结束啦,如果我的文章对你有所帮助的话,还请点个免费的赞,你的支持会激励我输出更高质量的文章,感谢!
相关文章:
Linux0.12内核源码解读(2)-Bootsect.S
作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功、源码解析、科技故事、项目实战、面试八股等更多硬核文章,首发于公众号「小牛呼噜噜」 文章目录 回顾计算机启动过程8086、80x86是什么意思?寄存器初始化CS:IPCPU是如何和ROM相连的?加载MBR到…...
虚拟环境搭建、后台项目创建及目录调整、封装logger、封装全局异常、封装Response、后台数据库创建
1 虚拟环境搭建 #1 虚拟环境作用多个项目,自己有自己的环境,装的模块属于自己的# 2 使用pycharm创建-一般放在项目路径下:venv文件夹-lib文件夹---》site-package--》虚拟环境装的模块,都会放在这里-scripts--》python࿰…...
每日一练 | 网络工程师软考真题Day39
1、Windows Server 2003操作系统中,IIS6.0不提供以下 效劳。 A.web B.smtp C.pop3 D.ftp 2、Windows Server 2003操作系统中, 提供了远程桌面访问。 A.ftp B.email C…...
Android Studio编写xml布局不提示控件的部分属性问题的解决
最近突然发现Android Studio编写xml,发现有一部分控件的属性没有了代码提示,主要体现为id,margin等属性不再有代码提示,如下图。 但是手动输入仍然有效。然后删掉Android Sdk重新回来还是发现有问题,导一个之前的旧项目进来&#…...
BUGKU-simple_SSTI_1漏洞注入
SSTI漏洞注入 SSTI全称Server side template injection.服务端模板注入这节课主要讲flask的模板注入.flask会把类似于 的变量当做参数来渲染并填充到web页面,如果该参数可控并被后台解析则有可能被注入恶意代码导致注入漏洞请注意 模板注入只会存在于二次渲染中,无二次渲染不会…...
Python:dict
一些关于dict的奇妙观察 实验一 首先,创建两个一模一样的字典: dict1 {a: 1} dict2 {a: 1}然后,进行各种各样的相等判断: print(dict1 dict2) print(dict1.keys() dict2.keys()) print(dict1.values() dict2.values()) p…...
git和svn 的国内的下载地址
CNPM Binaries Mirror 下面是svn的地址 TortoiseSVN 64位下载-TortoiseSVN客户端官方版下载-华军软件园...
matplotlib制图进阶版
需求:两个产品销量的可视化折线图 1、使用pandas读取数据 2、生成销售数量的折线图...
【Java 进阶篇】HTML介绍与软件架构相关知识详解
HTML(Hypertext Markup Language)是一种用于创建网页的标记语言。它是互联网上信息传递和展示的基础,无论是在浏览器中查看网页还是在移动设备上浏览应用程序,HTML都扮演着关键角色。本文将向您介绍HTML的基础知识,并探…...
Python数据攻略-Pandas与机器学习数据准备
在机器学习项目中,大部分时间都花在了数据准备上。你可能听说过“数据是机器学习的燃料”的说法,这是因为高质量的数据是构建出色模型的关键。 在这篇文章中将使用Pandas库来进行数据准备。为了让内容更贴近实际将使用《三国志》游戏中的角色数据作为样本。 文章目录 数据编…...
阿里云/亚马逊云代理:aws账号购买:aws亚马逊云账号的优势
AWS 可以用多少付多少,无预付费用,无需签订长期使用合约。我们能够构建和管理大规模的全球基础设施,aws账号购买并以降低价格的形式将节约成本的优势传递给您。借助我们在规模和专业知识方面的效益,过去四年来,我们已在…...
JSON的MIME媒体类型是application/json
JSON(全称 JavaScript Object Notation)即JavaScript对象表示法,通知使用application/json媒体类型。 目录 1、JSON介绍 2、JSON语法 3、实践总结 运行环境: Windows-7-Ultimate-x64、Windows-10-BusinessEditions-21h2-x64 1…...
C++ 之如何将数组传递给函数?
在本文中,您将学习将数组传递给C 中的函数。您将学习如何传递一维和多维数组。 数组可以作为参数传递给函数。也可以从函数返回数组。考虑以下示例,将一维数组传递给函数: 示例1:将一维数组传递给函数 C 程序通过将一维数组传递…...
1.7 计算机网络体系结构
思维导图: 1.7.1 计算机网络的体系结构的形成 **1.7 计算机网络体系结构** 计算机网络体系结构中,分层的思想为核心。该方法使得复杂的网络设计变得更为简单和可管理。 **1.7.1 计算机网络体系结构的形成** - **计算机网络的复杂性**: 即使是简单的文…...
boost在不同平台下的编译(win、arm)
首先下载boost源码 下载完成之后解压 前提需要自行安装gcc等工具 window ./bootstrap.sh ./b2 ./b2 installarm (linux) sudo ./bootstrap.sh sudo ./b2 cxxflags-fPIC cflags-fPIC linkstatic -a threadingmulti sudo ./b2 installx86 (linux) su…...
计算机网络(第8版)第一章概述笔记
6 性能指标 带宽: 在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。 7 分层结构、协议、接口、服务 1、实体:第n层的活动元素称为n层实体。同一层的实体叫对等实体。 2、协议:为进行网络中的对等实体数据交换而建立的规则、…...
Linux 部署项目
部署 Linux 部署项目1. 宝塔部署1.1 前端部署1.2 后端部署 2. docker 部署2.1 后端部署2.2 前端部署 3. 跨域问题3.1 Nginx 代理(推荐)3.2 修改后端服务3.3 添加 web 全局请求拦截器 4. 域名解析DNSPod添加域名 Linux 部署项目 1. 宝塔部署 准备工作&am…...
MySQL 基础
MySQL 基础 顾名思义,关系型数据库(RDB,Relational Database)就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。 关系型数据库中&#…...
VR模拟鸡胚培养接种实验,打造沉浸式的学习环境
在医学教育领域,传统的鸡胚接种实验一直是教学的重要组成部分。然而,这种实验方法存在一定的局限性,如操作难度大、成本高、安全隐患等。为了解决这些问题,越来越多的教育机构开始尝试引入虚拟现实(VR)技术,以模拟鸡胚…...
基于ModbusTCP与西门子PLC通讯项目案例
目录 一、西门子PLC仿真环境搭建 【1.1】创建PLC项目 【1.2】编写PLC程序 二、C#代码编写 【2.1】窗口制作 【2.2】效果演示 【2.3】读取源码 【2.4】FrmSiemensSet源码 【2.5】Variable源码 一、西门子PLC仿真环境搭建 【1.1】创建PLC项目 搭建PLCSIM-Advacend模拟仿…...
Oralce数据库管理 -操作系统cpu 内存 io指标分析查询
1 前35个cpu消耗较大的进程 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 3|head -35 1 前35个内存消耗较大的进程 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head -35...
my_print_defaults 及perror
参考文档: https://mysql.net.cn/doc/refman/8.0/en/my-print-defaults.html https://mysql.net.cn/doc/refman/8.0/en/perror.html -- my.cnf的内容 [rootredhat762100 mysql3306]# more my.cnf [mysqld] datadir/mysql/mysql3306/data #socket/tmp/mysql3306.so…...
视频转GIF:快速生成有趣的动态图片
随着社交媒体的快速发展,GIF动态图片已经成为了人们表达情感、分享生活片段的重要方式。将视频片段转换成GIF动态图片,可以让人们更好地分享和表达自己的情感,也可以让一些有趣的瞬间变得更加生动有趣。本文将介绍如何将视频快速转换成GIF动态…...
vue3 vscode no tsconfig与找不到名称“ref”。ts(2304)
如题,这两个问题都与tsconfig的配置有关,先看下问题表现: 解决方法,应当正确配置如下,之后保存或重启vscode:...
Docker基本操作【一篇学会项目部署】
文章目录 一、Docker简介二、Docker安装三、配置镜像加速四、Docker部署五、Docker基础操作1. 常见命令2. 操作演示3. 数据卷①nginx的html目录挂载②分析匿名数据卷③MySQL的本地目录挂载 4. 自定义镜像①Dockerfile②构建镜像 5. 网络①常见命令②自定义网络 六、DockerCompo…...
目标识别项目实战:基于Yolov7-LPRNet的动态车牌目标识别算法模型(二)
前言 目标识别如今以及迭代了这么多年,普遍受大家认可和欢迎的目标识别框架就是YOLO了。按照官方描述,YOLOv8 是一个 SOTA 模型,它建立在以前 YOLO 版本的成功基础上,并引入了新的功能和改进,以进一步提升性能和灵活性…...
Ceph入门到精通-sysctl.conf 配置
sysctl.conf Ubuntu server out of box is not optimized to make full use of available hardware. This means “out-of-box” setup might fail under high load. So we need to tweak system configuration for maximum concurrancy. Sysctl Tweaks Open vim /etc/sys…...
Cesium 展示——实体点击的相关属性,进行增删改
文章目录 需求分析1. 实体创建2. 相关属性需求 点击已加载的实体,获取该实体的所有属性,从而对实体进行增删改 分析 1. 实体创建 var viewer = new Cesium.Viewer(cesiumContainer, {terrainProvider: Cesium....
【算法小课堂】二分查找算法
简单思路: 当我们要从一个序列中查找一个元素的时候,最快想到的方法就是顺序查找法(即:从前到后依次查找)。但这种方法过于无脑,就是暴力的把每个元素都排查一遍。元素个数少的时候还行,一旦元…...
git修改提交历史中的author信息
全局设置 git config --global user.name "作者名" 局部设置(本项目) git config user.name "作者名" git修改提交作者和邮箱-CSDN博客 git修改提交作者和邮箱-CSDN博客...
中韩双语网站制作价格/长沙网站策划
【赛迪网讯】《varbusiness》杂志发表评论指出,随着开放资源软件的发展,Linux 大旗的挥舞者之一红帽(Red Hat)向外界公布了自己强劲的第二季度财政报告,在整个企业软件市场显现出低迷状态的趋势下,红帽逆潮…...
做木箱的网站/今日国内新闻大事
解构赋值(destructuring assignment)语法是一个Javascript表达式,这种语法能够更方便的提取出 Object 或者 Array 中的数据。这种语法可以在接受提取的数据的地方使用,比如一个表达式的左边。有明确的语法模式来告诉我们如何使用这…...
wordpress seo什么意思/百度投放广告收费标准
vector的数据安排和操作方式,和array很相似。但是vector优点是空间的灵活性。array是静态空间,如果我们想要多增加元素,必须手动申请,而vector会自动扩充空间。再也不用担心空间不足申请很大的array. vector实现关键在于对大小的控…...
浙江省网站建设与管理试卷/百度导航2023年最新版
No1: 程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊的执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上市在类结构确定下来时就已知的,因此这几个区域的内存分配和回收都具…...
网站流量数据查询/网站媒体推广
在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足: nums1[i] == nums2[j] 且绘制的直线不与任何其他连线(非水平线)相交。 请注意,连线即使在端点也不能相交:每个…...
wordpress调用文章标题/比百度好用的搜索软件手机版
HTML4.01 参考手册(共89个)排序按字母顺序,h1-h6算一个,除去不赞成使用的11个还有78个。 1 <!--...--> 定义注释。 2 <!DOCTYPE> 定义文档类型。 3 <a> 定义锚。 4 <abbr> 定义缩写。 5 <acronym> 定义只…...