64位操作系统-Loader引导加载程序
Loader需要做的事
检测硬件信息
处理器模式切换
向内核传递数据
Loader详解基础数据定义和头文件引用123456789101112[org 0x10000]jmp LabelStart%include "boot/fat12.inc"BaseOfKernelFile equ 0x00OffsetOfKernelFile equ 0x100000BaseTmpOfKernelAddr equ 0x00OffsetTmpOfKernelFile equ 0x7e00MemoryStructBufferAddr equ 0x7e00
fat12.inc为FAT12文件系统结构。
定义内核程序的起始地址为0x100000(1MB),因为1MB以下的内存并不是全部可用的空间。这段内存被划分为若干个子空间段,可以是内存空间、非内存空间以及地址空间。内核程序很可能超过1MB,所以让内核跳过这段复杂的空间。
0x7e00是内核程序的临时转存空间,由于内核读取是通过BIOS中断INT 0x13调用,BIOS在实模式下只支持1MB的物理地址空间寻址,所以需要先存储到临时空 ...
64位操作系统-BOOT加载LOADER
FAT12文件系统数据1234567891011121314151617181920212223242526272829303132333435363738[org 0x7c00]; 设置栈指针的位置BaseOfStack equ 0x7c00; 加载Loader到内存里的位置(20位)BaseOfLoader equ 0x1000OffsetOfLoader equ 0x00; 根目录占用的扇区数:(BPB_RootEntCnt * 32 + BPB_BytesPerSec - 1) / BPB_BytesPerSec; (224 * 32 + 512 - 1) / 512 = 14RootDirSectors equ 14; 根目录的起始扇区号 = 保留扇区数(BPB_RsvdSecCnt) + FAT表扇区数(BPB_FATSz16) + FAT表份数(BPB_NumFATs) = 1 + 9 * 2 = 19SectorNumOfRootDirStart equ 19; FAT表的起始扇区号,引导扇区的扇区号是0SectorNumOfFAT1Start equ 1; 用于平衡文 ...
64位操作系统-FAT12文件系统
FAT12是DOS时代就开始使用的文件系统,直到现在仍然在软盘上使用,FAT12软盘的被格式化后为:有两个磁头,每个磁头80个柱面(磁道),每个柱面有18个扇区,每个扇区512个字节空间。所以标准软盘的总空间为:
2 * 80 *18 * 512=1474560B=1440K=1.44M
下面是FAT12的结构图:
引导扇区操作系统之所以认识FAT12格式的磁盘,其秘密就在于逻辑0扇区这512B上。如果这512字节的最后两个字节的内容分别是55和AA(0xAA55低字节在前,高字节在后)的话,BIOS在启动时会将这个扇区读取到0:7C00h-0:7DFFh处,然后跳转到0:7C00h处继续执行指令,操作系统即用此来达到引导系统的目的,而这个磁盘就称为引导磁盘。
操作系统标识FAT12文件系统是因为在逻辑0扇区(即引导扇区)处还存储着一个特定的数据结构,此结构有固定的格式,在操作系统将此磁盘格式化时自动生成,具体数据结构如下表所示:
BS_jmpBoot:是跳转指令,偏移0处的跳转指令必须是合法的可执行的基于x86的CPU指令,如:jmp start,这样可以生成3字节长的指令,(加 ...
64位操作系统-写一个BOOT引导程序
写一个BOOT引导程序先上代码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849[org 0x7c00]BaseOfStack equ 0x7c00; init regmov ax, csmov ds, axmov es, axmov ss, axmov sp, BaseOfStack; clear screenmov ax, 0x600mov bx, 0x700mov cx, 0x0mov dx, 0x184fint 0x10; set focusmov ax, 0x200mov bx, 0x0mov dx, 0x0int 0x10; display on screenmov ax, 0x1301mov bx, 0xfmov dx, 0x0mov cx, 15push axmov ax, dsmov es, axpop axmov bp, StartBootMessageint 0x10; reset floppyxor ah, ahxor dl, dli ...
ELF文件详解
在Linux系统中,一个ELF文件主要用来表示3种类型的文件:
可执行文件。操作系统的加载器从硬盘上读取,载入内存中执行;
目标文件(*.o)。链接器读取,用来生成一个可执行文件或者共享库文件;
共享文件(*.so)。在动态链接的时候,由ld-linux.so来读取;
每个ELF文件可以拆成四部分:
123456789---------------------ELF Header---------------------Program Header Table---------------------Sections---------------------Section Header Table---------------------
链接器只关心ELF header、Sections以及Section Header Table这三部分;
加载器只关心 ELF header、Program header table 和 Segment 这三部分;中间部分的Sections,叫做Segments,本质上是一样的。
一个Segment可能包含一个或者多个Sections
...
buddy和slab算法
buddy内核使用struct page结构体描述每个物理页,也叫页框。
内核在很多情况下,需要申请连续的页框,而且数量不定,比如4个、5个、9个等。
Linux把所有空闲的页框分组为11个块链表,每个链表上的页框是固定的。在第i条链表中每个页框都包含2的i次方个连续页。
系统每个页框块的第一个页框的物理地址是该块大小的整数倍。例如大小为16个页框的块,其起始地址是10*2^12的倍数
页框操作1234// 分配2^order个连续的物理页,并返回一个指针,指向第一个页的page结构体static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);// 返回page页面所映射的虚拟地址void *page_address(const struct page *page);
Buddy提供了以page为单位的内存分配接口,这对内核来说颗粒度还太大了,所以需要一种新的机制,将page拆分为更小的单位来管理。
slabslab实现了内存的分配和管理slab层把不同的对象划分为高速缓存组(cache),其中 ...
spinlock自旋锁详解
自旋锁spinlock是一种死等的锁机制,一次只能有一个执行单元获取锁并进入临界区,其他执行单元不断循环死等。spinlock可以在中断上下文执行。由于不睡眠,因此spinlock可以在中断上下文中使用。
spinlock函数void spin_lock(spinlock_t *lock)。进程和进程之间同步
void spin_lock_bh(spinlock_t *lock)。涉及到和本地软中断之间的同步
void spin_lock_irq(spinlock_t *lock)。涉及到和本地硬中断之间的同步
void spin_lock_irqsave(spinlock_t *lock)。涉及到和本地硬中断之间的同步并保存本地中断状态
int spin_trylock(spinlock_t *lock)。尝试获取锁,如果成功返回非零,否则返回零
使用1234567spinlock_t lock;// 初始化spin_lock_init(&lock);// 获取锁spin_lock(&lock);// 释放锁spin_unlock(&lock);
sp ...
semaphore信号量详解
信号量信号量是一个计数器,用来保护一个或多个共享资源的访问。如果信号量计数器大于0,则允许进程访问,信号量-1,如果信号量等于0,信号量会将线程置入休眠直到信号量大于0。
信号量能够保证对临界区的访问是原子的。
信号量用于进程之间的同步,进程在信号量保护的临界区代码里是可以睡眠的,这是和自旋锁最大的区别。
信号量基本使用1234567struct semaphore sema;// 初始化sema_init(&sema, 1);// 进入临界区 信号量-1down(sema);// 退出临界区 信号量+1up(sema);
信号量Kernel实现信号量结构体定义结构体定义在include/linux/semaphore.h中
信号量结构体定义了自旋锁,信号量数以及等待队列。
1234567891011struct list_head { struct list_head *next, *prev;};/* Please don't access any members of this structure directly */struct sem ...
Atomic原子变量详解
原子变量适用于针对int变量进行同步的场景
Atomic使用12345678atomic_t ac = ATOMIC_INIT(1);// 将原子变量-1,如果结果为0,返回true,否则返回false// 原子操作atomic_dec_and_test(&ac);// 原子变量+1// 原子操作atomic_inc(&ac)
Atomic实现atomic_t定义定义在include/linux/types.h中
123typedef struct { int counter;} atomic_t;
初始化不同体系下实现不同,x86实现如下:
1234567/* * Atomic operations that C can't guarantee us. Useful for * resource counting etc.. */#define ATOMIC_INIT(i) { (i) }
嵌入式汇编代码语法1234asm("汇编语言" :输出寄存器 :输入寄存器 :会被修改的 ...
Redis数据结构-跳跃表
跳跃表如果一个有序集合包含的元素数量比较多,又或者有序集合中的元素成员是比较长的自复制,Redis会使用跳跃表来做有序集合的底层实现。
跳跃表的实现
左边是zskiplist结构:
header:指向跳跃表的头结点
tail: 指向跳跃表的尾节点
level: 记录目前跳跃表内,层数最大的那个节点层数(表头节点不计算在内)
length: 记录跳跃表的长度,跳跃表目前包含的节点数量(表头节点不计算在内)
右边为四个zskiplistNode结构:
层(level):节点中用L1、L2、L3等字样标记节点的各层,L1表示第一层,L2表示第二层,以此类推。每个层有带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向的节点和当前节点的距离。
后退(backward)指针:节点中用BW字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。
分值(score):各个节点中的1.0、2.0和3.0是节点保存的分值。在跳跃表中,节点按各自所保存的分值从大到小排序
成员对象(obj): 各个节点中的o1、o2、 ...




