《真象还原》读书笔记——第六章 完善内核

6.1 函数调用约定简介

接下来要使用C语言和汇编的混合编程

6.1.1 调用约定

  • 参数的传递方式
  • 参数的传递顺序
  • 是调用者保存寄存器环境还是被调用者保存环境。保存的有哪些寄存器。

我们可以将参数保存到栈中
这时候有出现问题:

  1. 由谁来负责回收。
  2. 参数多的情况下,主调函数按照什么顺序传递。

例如:

subtract(int a, int b)		//被调用者
{return a - b;
}int sub = subtract(3, 2);	//调用者

我们来模拟下栈的情况:

//调用者
push 2
push 3
call subtract// 被调用者
push ebp
mov ebp,esp
mov eax,[ebp+8]
sub eax,[ebp+12]

在这里插入图片描述

6.1.1.1 stdcall的调用约定
  1. 调用者将所以参数从右往左入栈
  2. 被调用者清理参数所占的栈空间
//主调用者
push 2
push 3
call subtract//被调用者
push ebp
mov ebp,esp
mov eax,[ebp+0x8]
add eax,[ebp+0xc]
mov esp,ebp 	//归还esp
pop ebp			//归还ebp
ret 8
6.1.1.2 cdecl 调用约定 我们要用到的
  1. 从左往右入栈
  2. 调用者清理空间
//主调用者
push 2
push 3
call subtract
add esp,8  //这里是回收[清理]栈空间//被调用者
push ebp
mov ebp,esp
mov eax,[ebp+0x8]
add eax,[ebp+0xc]
mov esp,ebp
pop ebp
ret

6.2 汇编语言和C语言混合编程

6.2.1 C库函数和系统调用

两类,
1.混合编程:单独的汇编文件+单独的C语言文件分别编译后成为目标文件后,一起链接成为可执行程序。
2. 内嵌汇编:在C语言中嵌入汇编代码,直接编译成为可执行程序。

6.2.2 汇编语言和C语言共同协作

//C_with_S_c.c
extern void asm_print(char *, int);
void c_print(char *str){int len = 0;while(str[len++]);asm_print(str, len);
}//C_with_S_S.S
section .data
str:	db "asm_print says hello world!", 0xa, 0
str_len equ $-strsection .text
extern c_print
global	_start		;导出为全局符号,为了给链接器使用
_start:
;;;;;;;;;;;;;;;;;;;调用C代码中的函数c_print;;;;;;;;;;;;;;push strcall c_printadd esp,4
;;;;;;;;;;;;;;;;;;;;;;退出程序;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mov eax,1int 0x80global asm_print
asm_print:push ebpmov ebp,espmov eax,4mov ebx,1mov ecx,[ebp+8]mov edx,[ebp+12]int 0x80pop ebpret

汇编代码和C代码相互调用

  • 在汇编代码中导出符号供外部引用是用的关键字 global,引用的外部文件的符号是用的关键字extern。
  • 在C代码中只要将符号定义为全局便可以被外部引用,引用外部符号时候使用extern声明即可。

6.3 实现自己的打印函数

6.3.1 显卡的端口控制

这里按照它们在图形线管(位于CPU和video之间)中的位置的顺序。
VGA寄存器
计算机工程师把每一个寄存器分组视为一个寄存器数组,提供一个寄存器用于指定数组的下标,再提供一个寄存器用于索引所指向的数组的元素(也就是寄存器)进行输入输出操作。
这两个寄存器就是各组中的 Address Register 和 Data Register。
在Address Register中指定寄存器的索引值,用于确定操作的寄存器是哪个,然后在Data Register寄存器中对所索引的寄存器进行读写操作。
我们使用的显卡操作只用到了 CRT Controller Registers 分组中的寄存器。

CRT Controller Registers 寄存器组中的 Address Register 和 Data Register 的端口地址并不固定,具体值取决于 Miscellaneous Output Register 寄存器中的 Input/Output Address Select 字段。
在这里插入图片描述
在这里插入图片描述
I/OAS(Input/Output Address Select)
此位用来选择 CRT controller 寄存器组的地址,这里是指 Address Register 和 Data Register 的地址。

当此位为 0 时:
CRT controller 寄存器组的端口地址被设置为 0x3Bx,结合表 6-2,Address Register 和 Data Register 的
端口地址实际值为 3B4h-3B5h。并且为了兼容 monochrome 适配器(显卡),Input Status Register 寄存器的端口地址被设置为 0x3BA。

当此位为 1 时:
CRT controller 寄存器组的端口地址被设置为 0x3Dx,结合表 6-2,Address Register 和 Data Register 的
端口地址实际值为 3D4h-3D5h。并且为了兼容 color/graphics 适配器(显卡),Input Status Register 寄存器的端口地址被设置为 0x3DA。

默认情况下,Miscellaneous Output Register 寄存器的值为 0x67,其他字段不管,咱们只关注这最重要的 I/OAS 位,其值为 1。也就是说:

  • CRT controller 寄存器组的 Address Register 的端口地址为 0x3D4,
    Data Register 的端口地址 0x3D5。
  • Input Status #1Register 寄存器的端口地址被设置为 0x3DA。
  • Feature Control register 寄存器的写端口是 0x3DA

6.3.2 实现单个字符打印

lib/stdint.h

#ifndef __LIB_STDINT_H
#define __LIB_STDINT_H
typedef signed char 			int8_t;
typedef signed short int 		int16_t;
typedef signed int 				int32_t;
typedef signed long long int 	int64_t;
typedef unsigned char 			uint8_t;
typedef unsigned short int 		uint16_t;
typedef unsigned int 			uint32_t;
typedef unsigned long long int 	uint64_t;
#endif

print.asm

TI_GDT  equ 0
RPL0    equ 0
SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0[bits 32]
section .text;----------------- put_char ------------------
;功能描述:把栈中一个字符写入光标处
;---------------------------------------------
global put_char
put_char:pushadmov ax,SELECTOR_VIDEOmov gs,ax;;;;;;;;;;获取当前光标位置;;;;;;;;;;8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5in al,dxmov ah,al;8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5in al,dx;光标放入 bxmov bx,ax;放入待打印的字符mov ecx,[esp + 36] ;因为pushad压入4*8=32字节,还要跳过4字节的返回地址。cmp cl,0xd ;回车符号jz .is_carriage_return cmp cl,0xa ;换行符号jz .is_line_feedcmp cl,0x8 ;Backspace的ascll码是0x8jz .is_backspacejmp .put_other.is_backspace:dec bxshl bx,1 ;坐标左移1,并将等待删除的字节补上0或空格mov byte [gs:bx],0x20inc bxmov byte [gs:bx],0x07shr bx,1jmp .set_cursor.put_other:shl bx,1mov [gs:bx],clinc bxmov byte [gs:bx],0x07shr bx,1inc bxcmp bx,2000jl .set_cursor.is_line_feed:          ;换行符 LF(\n)
.is_carriage_return:    ;回车符 CR(\r)xor dx,dxmov ax,bxmov si,80div sisub bx,dx.is_carriage_return_end:add bx,80cmp bx,2000
.is_line_feed_end:      ;若是LF(\n),将光标移+80即可jl .set_cursor;滚屏,把屏幕的 1-24行搬运到第 0-23;再将第 24 行用空格填充。
.roll_screen:cldmov ecx,960mov esi,0xc00b80a0;第一行首部mov edi,0xc00b8000;第零行首部rep movsd;空白填充最后一行mov ebx,3840mov ecx,80.cls:mov word [gs:ebx], 0x0720;黑底白字空格键add ebx,2loop .clsmov bx,1920.set_cursor:
;设置光标为 bx 值
;8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5mov al,bhout dx,al
;8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5mov al,blout dx,al
.put_char_done:popadret

lib/kernel/print.h

#ifndef __LIB_KERNEL_PRINT_H
#define __LIB_KERNEL_PRINT_H
#include "stdint.h"
void put_char(uint8_t char_asci);
#endif

编译处理:

nasm -f elf -o lib/kernel/print.o lib/kernel/print.asmclang -I lib/ -m32 -s -w -c -o main.o main.cld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin main.o lib/kernel/print.odd if=kernel.bin of=hd60M.img bs=512 count=200 seek=9 conv=notrunc

运行结果
在这里插入图片描述

6.3.3 实现字符串打印

在C语言中,会在字符串末位添加 ‘\0’ ,在这里我们也这么做。
我们会通过判断结尾的 0 来判断字符串是否结束。

lib/kernel/print.asm
添加了 put_str 部分

TI_GDT  equ 0
RPL0    equ 0
SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0[bits 32]
section .text;------------- put_str ----------------------
global put_str
put_str:push ebxpush ecxxor ecx,ecxmov ebx, [esp+12]
.goon:mov cl,[ebx]cmp cl,0jz .str_overpush ecxcall put_charadd esp,4inc ebxjmp .goon
.str_over:pop ecxpop ebxret;----------------- put_char ------------------
;功能描述:把栈中一个字符写入光标处
;---------------------------------------------
global put_char
put_char:pushadmov ax,SELECTOR_VIDEOmov gs,ax;;;;;;;;;;获取当前光标位置;;;;;;;;;;8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5in al,dxmov ah,al;8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5in al,dx;光标放入 bxmov bx,ax;放入待打印的字符mov ecx,[esp + 36] ;因为pushad压入4*8=32字节,还要跳过4字节的返回地址。cmp cl,0xd ;回车符号jz .is_carriage_return cmp cl,0xa ;换行符号jz .is_line_feedcmp cl,0x8 ;Backspace的ascll码是0x8jz .is_backspacejmp .put_other.is_backspace:dec bxshl bx,1 ;坐标左移1,并将等待删除的字节补上0或空格mov byte [gs:bx],0x20inc bxmov byte [gs:bx],0x07shr bx,1jmp .set_cursor.put_other:shl bx,1mov [gs:bx],clinc bxmov byte [gs:bx],0x07shr bx,1inc bxcmp bx,2000jl .set_cursor.is_line_feed:          ;换行符 LF(\n)
.is_carriage_return:    ;回车符 CR(\r)xor dx,dxmov ax,bxmov si,80div sisub bx,dx.is_carriage_return_end:add bx,80cmp bx,2000
.is_line_feed_end:      ;若是LF(\n),将光标移+80即可jl .set_cursor;滚屏,把屏幕的 1-24行搬运到第 0-23;再将第 24 行用空格填充。
.roll_screen:cldmov ecx,960mov esi,0xc00b80a0;第一行首部mov edi,0xc00b8000;第零行首部rep movsd;空白填充最后一行mov ebx,3840mov ecx,80.cls:mov word [gs:ebx], 0x0720;黑底白字空格键add ebx,2loop .clsmov bx,1920.set_cursor:
;设置光标为 bx 值
;8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5mov al,bhout dx,al
;8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5mov al,blout dx,al
.put_char_done:popadret

补充 print.h 中的put_str

#ifndef __LIB_KERNEL_PRINT_H
#define __LIB_KERNEL_PRINT_H
#include "stdint.h"
void put_char(uint8_t char_asci);
void put_str(char *message);
#endif

Makefile

loader.bin:loader.asmnasm -I inc/ -o loader.bin loader.asmmbr.bin:mbr.asmnasm -I inc/ -o mbr.bin mbr.asmkernel.bin:main.cnasm -I lib/kernel -f elf -o lib/kernel/print.o lib/kernel/print.asmclang -I lib/kernel -m32 -s -w -c -o main.o main.cld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin main.o lib/kernel/print.odd: dd_mbr dd_loader dd_kernel
dd_mbr:dd if=mbr.bin of=hd60M.img bs=512 count=1 conv=notruncdd_loader:dd if=loader.bin of=hd60M.img bs=512 count=4 seek=2 conv=notruncdd_kernel:dd if=kernel.bin of=hd60M.img bs=512 count=200 seek=9 conv=notrunc

main.c

#include "print.h"
int main(void){put_char('k');put_char('e');put_char('r');put_char('n');put_char('e');put_char('l');put_char('\n');put_char('1');put_char('2');put_char('\b');put_char('3');put_char('\n');put_str("I am kernel\n");put_str("put_str(\"I am kernel\n\");");while(1);return 0;
}

运行结果:
在这里插入图片描述

6.3.4 实现整数打印

lib/kernel/print.asm 中添加 put_int

TI_GDT  equ 0
RPL0    equ 0
SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0
[bits 32]section .data 
put_int_buffer dq   0
section .text;------------- put_str ----------------------
global put_str
put_str:push ebxpush ecxxor ecx,ecxmov ebx, [esp+12]
.goon:mov cl,[ebx]cmp cl,0jz .str_overpush ecxcall put_charadd esp,4inc ebxjmp .goon
.str_over:pop ecxpop ebxret;----------------- put_char ------------------
;功能描述:把栈中一个字符写入光标处
;---------------------------------------------
global put_char
put_char:pushadmov ax,SELECTOR_VIDEOmov gs,ax;;;;;;;;;;获取当前光标位置;;;;;;;;;;高8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5in al,dxmov ah,al;低8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5in al,dx;光标放入 bxmov bx,ax;放入待打印的字符mov ecx,[esp + 36] ;因为pushad压入4*8=32字节,还要跳过4字节的返回地址。cmp cl,0xd ;回车符号jz .is_carriage_return cmp cl,0xa ;换行符号jz .is_line_feedcmp cl,0x8 ;Backspace的ascll码是0x8jz .is_backspacejmp .put_other.is_backspace:dec bxshl bx,1 ;坐标左移1位,并将等待删除的字节补上0或空格mov byte [gs:bx],0x20inc bxmov byte [gs:bx],0x07shr bx,1jmp .set_cursor.put_other:shl bx,1mov [gs:bx],clinc bxmov byte [gs:bx],0x07shr bx,1inc bxcmp bx,2000jl .set_cursor.is_line_feed:          ;换行符 LF(\n)
.is_carriage_return:    ;回车符 CR(\r)xor dx,dxmov ax,bxmov si,80div sisub bx,dx.is_carriage_return_end:add bx,80cmp bx,2000
.is_line_feed_end:      ;若是LF(\n),将光标移+80即可jl .set_cursor;滚屏,把屏幕的 1-24行搬运到第 0-23 行
;再将第 24 行用空格填充。
.roll_screen:cldmov ecx,960mov esi,0xc00b80a0;第一行首部mov edi,0xc00b8000;第零行首部rep movsd;空白填充最后一行mov ebx,3840mov ecx,80.cls:mov word [gs:ebx], 0x0720;黑底白字空格键add ebx,2loop .clsmov bx,1920.set_cursor:
;设置光标为 bx 值
;高8位mov dx,0x03d4mov al,0x0eout dx,almov dx,0x03d5mov al,bhout dx,al
;低8位mov dx,0x03d4mov al,0x0fout dx,almov dx,0x03d5mov al,blout dx,al
.put_char_done:popadret;------------------ 将小端字节序数字编程对于的ASCII ,倒置------
;输入:栈中参数为待打印的数字
;输出:在屏幕打印16进制数字,并不会打印前缀 0x
;例如 打印 15(10) 时候 打印的为 f
;------------------------------------------------------------
global put_int
put_int:pushadmov ebp,espmov eax,[ebp+4*9]mov edx,eaxmov edi,7mov ecx,8mov ebx,put_int_buffer
;将32位数字按照16进制的形式从低到高位逐个处理
;共处理8个十六进制数字
.16based_4bits:and edx,0x0000000Fcmp edx,9jg .is_A2Fadd edx,'0'jmp .store 
.is_A2F:sub edx,10add edx,'A'
.store:mov [ebx+edi],dl dec edi shr eax,4 mov edx,eax loop .16based_4bits
.ready_to_print:inc edi 
.skip_prefix_0:cmp edi,8je .full0
.go_on_skip:mov cl,[put_int_buffer+edi]inc edi cmp cl,'0'je .skip_prefix_0;判断继续下一个字符是否是字符0dec edi jmp .put_each_num .full0:mov cl,'0'
.put_each_num:push ecxcall put_char add esp,4 inc edi mov cl,[put_int_buffer+edi]cmp edi,8jl .put_each_numpopad ret

补全print.h

#ifndef __LIB_KERNEL_PRINT_H
#define __LIB_KERNEL_PRINT_H
#include "stdint.h"
void put_char(uint8_t char_asci);
void put_str(char *message);
void put_int(uint32_t num);
#endif

main.c

#include "print.h"
int main(void){put_str("I am kernel\n");put_int(0); put_char('\n'); put_int(9); put_char('\n'); put_int(0x00021a3f); put_char('\n'); put_int(0x12345678); put_char('\n'); put_int(0x00000000);while(1);return 0;
}

运行结果:
在这里插入图片描述

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

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

相关文章

【SpringCloudAlibaba系列--nacos配置中心】

Nacos做注册中心以及使用docker部署nacos集群的博客在这&#xff1a; 容器化部署Nacos&#xff1a;从环境准备到启动 容器化nacos部署并实现服务发现(gradle) 使用docker部署nacos分布式集群 下面介绍如何使用nacos做配置中心 首先要进行nacos-config的引入&#xff0c;引入…

线性代数:向量、张量、矩阵和标量

线性代数&#xff1a;向量、张量、矩阵和标量 背景 在线性代数中&#xff0c;向量、张量、矩阵和标量都属于基础概念&#xff0c;特别是最近AI的爆火&#xff0c;向量和张量的概念也越来越普及&#xff0c;本文将介绍下这些基本概念。 1. 标量&#xff08;Scalar&#xff0…

会声会影2024视频编辑软件电脑版本下载

一、功能特点 会声会影是一款功能强大的视频编辑软件&#xff0c;它集合了视频剪辑、特效添加、音频处理、字幕制作等多种功能于一身。具体来说&#xff0c;其特点包括&#xff1a; 会声会影2024安装包下载如下: https://wm.makeding.com/iclk/?zoneid55677 直观易用的操作…

WordPress前端如何使用跟后台一样的Dashicons图标字体?

很多站长都喜欢在站点菜单或其他地方添加一些图标字体&#xff0c;常用的就是添加Font Awesome 图标和阿里巴巴矢量库图标iconfont。其实我们使用的 WordPress 本身就有一套管理员使用的官方图标字体 Dashicons&#xff0c;登录我们站点后台就能看到这些图标字体。那么有没有可…

Dynamo批量将房间名称转换为模型文字

今天呢&#xff0c;我们简单聊聊如何把房间名称&#xff0c;变成模型文字&#xff0c;好在三维中能够看到房间名称。 本来吧&#xff0c;我觉得批量创建模型文字应该是个很简单的事&#xff0c;但是我在Dynamo中搜了下ModelText&#xff0c;发现只有一个在族环境中创建模型文字…

新版Java面试专题视频教程——虚拟机篇②

新版Java面试专题视频教程——虚拟机篇② 3 垃圾收回3.1 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff09;3.2 对象什么时候可以被垃圾器回收3.2.1 引用计数法3.2.2 可达性分析算法 3.3 JVM 垃圾回收算法有哪些&#xff1f;——4种3.3…

EasyRecovery2024永久免费版手机数据恢复软件功能全面介绍

一、功能概述 EasyRecovery手机数据恢复软件是一款专为移动设备设计的数据恢复工具。它能够有效地从智能手机、平板电脑等移动设备中恢复因各种原因丢失的数据&#xff0c;包括但不限于误删除、格式化、系统崩溃、病毒感染等。 EasyRecovery-mac最新版本下载:https://wm.maked…

IT廉连看——C语言——循环语句

IT廉连看——C语言——循环语句 循环语句分为三种&#xff1a; while for do while 一、while循环 我们已经掌握了&#xff0c;if语句&#xff1a; if(条件)语句; 当条件满足的情况下&#xff0c;if语句后的语句执行&#xff0c;否则不执行。 但是这个语句只会执行一次…

源代码管理——码云Gitee

目录 Git安装 Gitee配置SSH 源代码管理常规操作 1.idea配置git 2.常规操作 Git安装 安装Git是进行源代码管理的基本步骤之一。以下是在本地安装Git的通用步骤&#xff0c;适用于Windows系统&#xff1a; 下载Git安装程序: 访问Git官网的下载页面&#xff1a;Git官网下载地…

选择适合你的编程语言

引言 在当今瞬息万变的技术领域中&#xff0c;选择一门合适的编程语言对于个人职业发展和技术成长至关重要。每种语言都拥有独特的设计哲学、应用场景和市场需求&#xff0c;因此&#xff0c;在决定投入时间和精力去学习哪种编程语言时&#xff0c;我们需要综合分析多个因素&a…

Redis(十四)双写一致性工程案例

文章目录 问题概述canal功能安装部署mysql配置canal服务端canal客户端&#xff08;Java程序&#xff09; 问题概述 canal https://github.com/alibaba/canal 功能 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据…

爬取数位观察城市数据知识总结

# 抓取数位观察中城市的GDP,公交车数量&#xff0c;户籍人口 # url "https://www.swguancha.com/home/query-city-page" # 1.找数据 # 1.1如果数据在页面源代码里&#xff0c;则访问&#xff0c;在本案例中并没有在源代码中 # 1.2如果数据不在页面源代码里&#xff…

zookeeper快速上手

文章目录 1. docker安装zookeeper2. zookeeper基本使用&#xff08;Linux&#xff09;新增结点查看命令修改命令删除命令其他命令 3. zookeeper ACL(Linux)ACL 命令行ACL 构成ACL 特性world授权模式ip授权模式auth模式digest授权模式 4. IDEA操作Zookeeper连接zookeeper创建结点…

QT Widget自定义菜单

此文以设置QListWidget的自定义菜单为例&#xff0c;其他继承于QWidget的类也都可以按类似的方法去实现。 1、ui文件设置contextMenuPolicy属性为CustomContextMenu 2、添加槽函数 /*** brief onCustomContextMenuRequested 右键弹出菜单* param pos 右键的坐标*/void onCusto…

C++的文件操作详解

目录 1.文本文件 1.写文件 2.读文件 2.二进制文件 1.写文件 2.读文件 1.文本文件 1.写文件 #include<bits/stdc.h> #include<fstream> using namespace std;int main() {ofstream ofs;ofs.open("text.txt",ios::out);ofs << "abc&qu…

Curator基本使用

文章目录 1. 基本操作1.1 建立连接1.2 创建结点1.3 查询结点查询数据查询子结点查看结点信息 1.4 修改结点普通修改带乐观锁的修改 1.5 删除删除单个结点删除带子结点的结点必须成功的删除带回调函数的删除 2. 监听器事件2.1 NodeCache单一结点连续监听2.2 PathChildrenCache监…

“从根到叶:深入理解排序数据结构“

一.排序的概念及引用 1.1排序的概念 排序是指将一组数据按照一定的规则重新排列的过程。排序的目的是为了使数据具有有序性&#xff0c;便于查找、插入、删除等操作&#xff0c;提高数据的组织和管理效率。 稳定性是指如果序列中存在相等元素&#xff0c;在排序完成后&#…

300分钟吃透分布式缓存-12讲:为何MC能长期维持高性能读写?

内存管理 slab 机制 讲完淘汰策略&#xff0c;我们接下来学习内存管理 slab 机制。 Mc 内存分配采用 slab 机制&#xff0c;slab 机制可以规避内存碎片&#xff0c;是 Mc 能持续高性能进行数据读写的关键。 slabclass Mc 的 slab 机制是通过 slabclass 来进行运作的&#x…

程序媛的mac修炼手册-- 小白入门Java篇

最近因为要用CiteSpace做文献综述&#xff0c;间接接触Java了。所以&#xff0c;继Python、C之后&#xff0c;又要涉猎Java了。刺激&#xff01;&#xff01; 由于CiteSpace与Java要求版本高度匹配&#xff0c;有个匹配详情明天为大家讲解。总之&#xff0c;我的Java之旅开始于…

冲击大厂算法面试=>链表专题【链表删除】

冲击大厂算法面试>链表专题【链表删除】 本文学习目标或者巩固的知识点 学习如何删除链表中的某个节点 如何删除valk的节点如何删除倒数第n个节点 学习如何删除链表中的某些节点 涉及头节点问题如何解决 提前说明&#xff1a;算法题目来自力扣、牛客等等途径 &#x1f7e…