实验23.硬盘分区,并编写硬盘驱动程序

news2024/11/27 6:22:34

已完成实验

已完成实验链接

简介

实验 23. 硬盘分区,并编写硬盘驱动程序

总结

  • 创建硬盘并分区

  • 加载硬盘分区

    • 实现 printk

    • 使能硬盘中断 interrupt.c

    • idel 线程,主线程阻塞后此线程保底 thread.c

    • sleep 函数 timer.c

    • 加载分区信息 ide.c

创建硬盘


fdisk /home/c/os/bochs/hd80M.img

# 1.设置柱面和磁头
m
x
m
c
162
h
16
r

# 创建主分区
n
p
1
2048
33263

# 创建拓展分区
n
e
4
33264
163295

# 创建逻辑分区
n
35312
51407

n
53456
76607

n
78656
91727

n
93776
121967

n
124016
163295

# 修改拓展分区的type
t
5
66

t
6
66

t
7
66

t
8
66

t
9
66

用 xxd 查看分区

#0x1BE=446
#0x200=512

fdisk -l /home/c/os/bochs/hd80M.img

# 查看硬盘分区表
./xxd.sh /home/c/os/bochs/hd80M.img 0x1BE 64

00 00 21 02 83 0F 3F 20 # 83 linux img1
00 08 00 00 # 起始扇区 0x0800 = 2048
F0 79 00 00 # 扇区数   0x79F0 = 31216

00 00 01 21 05 0F 3F A1 # 5 总拓展分区 img4
F0 81 00 00 # 起始扇区 0x81F0 = 33264
F0 FB 01 00 # 扇区数   0x1FBF0 = 130032

# 查看拓展分区表
# 0x81F0 * 0x200 + 0x1BE = 0x103E1BE
./xxd.sh /home/c/os/bochs/hd80M.img 0x103E1BE 64

00 00 21 23 66 0F 3F 32 # 66 逻辑分区 img5
00 08 00 00 # 起始扇区 0x0800 + 0x81F0 = 0x89F0 = 35312
E0 3E 00 00 # 扇区数   0x3EE0 = 16096

00 00 01 33 05 0F 3F 4B # 5 子拓展分区1 不显示
E0 46 00 00 # 起始扇区 0x46E0 + 0x81F0 = 0xC8D0
70 62 00 00 # 扇区数   0x6270 = 25200


# 查看子拓展分区1表
# 0xC8D0 * 0x200 + 0x1BE = 0x191A1BE
./xxd.sh /home/c/os/bochs/hd80M.img 0x191A1BE 64

00 00 21 35 66 0F 3F 4B # 66 逻辑分区 img6
00 08 00 00 # 起始扇区 0x0800 + 0xC8D0 = 0xD0D0 = 53456
70 5A 00 00 # 扇区数   0x5A70 = 23152

# 后续就是递归查找了

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

主要代码

stdio-kernel.c

// 文件: stdio-kernel.c
// 时间: 2024-08-06
// 来自: ccj
// 描述: 内核打印,与printf的区别是不用陷入中断,开销更小

#include "stdio-kernel.h"
#include "print.h"
#include "stdio.h"
#include "console.h"
#include "global.h"

#define va_start(args, first_fix) args = (va_list)(&first_fix)
#define va_end(args)              args = NULL

/// @brief 供内核使用的格式化输出函数
/// @param format 格式
/// @param  参数
void printk(const char* format, ...) {
    va_list args;
    va_start(args, format);
    char buf[1024] = {0};
    vsprintf(buf, format, args);
    va_end(args);
    console_put_str(buf);
}

interrupt.c

在这里插入图片描述

thread.c

在这里插入图片描述

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

timer.c

在这里插入图片描述

ide.h

// 文件: ide.h
// 时间: 2024-08-05
// 来自: ccj
// 描述: 硬盘相关数据结构


#ifndef __DEVICE_IDE_H
#define __DEVICE_IDE_H

#include "stdint.h"
#include "sync.h"
#include "list.h"
#include "bitmap.h"

/// @brief 分区结构
struct partition {
    uint32_t start_lba;          // 起始扇区
    uint32_t sec_cnt;            // 扇区数
    struct disk* my_disk;        // 分区所属的硬盘
    struct list_elem part_tag;   // 用于队列中的标记
    char name[8];                // 分区名称
    struct super_block* sb;      // 本分区的超级块
    struct bitmap block_bitmap;  // 块位图
    struct bitmap inode_bitmap;  // i结点位图
    struct list open_inodes;     // 本分区打开的i结点队列
};

/// @brief 硬盘结构
struct disk {
    char name[8];                    // 本硬盘的名称,如sda等
    struct ide_channel* my_channel;  // 此块硬盘归属于哪个ide通道
    uint8_t dev_no;                  // 本硬盘是主0还是从1
    struct partition prim_parts[4];  // 主分区顶多是4个
    struct partition logic_parts[8];  // 逻辑分区数量无限,但总得有个支持的上限,那就支持8个
};

/// @brief ata通道结构
/// ide = ata ,硬盘接口类型,如sata
struct ide_channel {
    char name[8];        // 本ata通道名称, 如ata0,也被叫做ide0.
    uint16_t port_base;  // 本通道的起始端口号
    uint8_t irq_no;      // 本通道所用的中断号
    struct lock lock;
    bool expecting_intr;  // 向硬盘发完命令后等待来自硬盘的中断
    struct semaphore disk_done;  // 硬盘处理完成.阻塞自己,由硬盘完成后产生的中断将线程唤醒
    struct disk devices[2];  // 一个通道上连接两个硬盘,一主一从
};

void intr_hd_handler(uint8_t irq_no);
void ide_init(void);
extern uint8_t channel_cnt;
extern struct ide_channel channels[];
extern struct list partition_list;
void ide_read(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt);
void ide_write(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt);

#endif

ide.c

// 文件: ide.c
// 时间: 2024-08-06
// 来自: ccj
// 描述: 从盘初始化
//          1.读取硬盘分区

#include "ide.h"
#include "sync.h"
#include "io.h"
#include "stdio.h"
#include "stdio-kernel.h"
#include "interrupt.h"
#include "memory.h"
#include "debug.h"
#include "console.h"
#include "timer.h"
#include "string.h"
#include "list.h"

// 定义硬盘各寄存器的端口号
#define reg_data(channel)       (channel->port_base + 0)
#define reg_error(channel)      (channel->port_base + 1)
#define reg_sect_cnt(channel)   (channel->port_base + 2)
#define reg_lba_l(channel)      (channel->port_base + 3)
#define reg_lba_m(channel)      (channel->port_base + 4)
#define reg_lba_h(channel)      (channel->port_base + 5)
#define reg_dev(channel)        (channel->port_base + 6)
#define reg_status(channel)     (channel->port_base + 7)
#define reg_cmd(channel)        (reg_status(channel))
#define reg_alt_status(channel) (channel->port_base + 0x206)
#define reg_ctl(channel)        reg_alt_status(channel)

// reg_status寄存器的一些关键位
#define BIT_STAT_BSY  0x80  // 硬盘忙
#define BIT_STAT_DRDY 0x40  // 驱动器准备好
#define BIT_STAT_DRQ  0x8   // 数据传输准备好了

// device寄存器的一些关键位
#define BIT_DEV_MBS 0xa0  // 第7位和第5位固定为1
#define BIT_DEV_LBA 0x40
#define BIT_DEV_DEV 0x10

// 一些硬盘操作的指令
#define CMD_IDENTIFY     0xec  // identify指令
#define CMD_READ_SECTOR  0x20  // 读扇区指令
#define CMD_WRITE_SECTOR 0x30  // 写扇区指令

// 定义可读写的最大扇区数
#define max_lba ((80 * 1024 * 1024 / 512) - 1)  // 只支持80MB硬盘

uint8_t channel_cnt;             // 按硬盘数计算的通道数
struct ide_channel channels[2];  // 有两个ide通道

// 用于记录总扩展分区的起始lba,初始为0,partition_scan时以此为标记
int32_t ext_lba_base = 0;

uint8_t p_no = 0, l_no = 0;  // 用来记录硬盘主分区和逻辑分区的下标

struct list partition_list;  // 分区队列

/// @brief 构建一个16字节大小的结构体,用来存分区表项
struct partition_table_entry {
    uint8_t bootable;    // 是否可引导
    uint8_t start_head;  // 起始磁头号
    uint8_t start_sec;   // 起始扇区号
    uint8_t start_chs;   // 起始柱面号
    uint8_t fs_type;     // 分区类型
    uint8_t end_head;    // 结束磁头号
    uint8_t end_sec;     // 结束扇区号
    uint8_t end_chs;     // 结束柱面号

    // 更需要关注的是下面这两项
    uint32_t start_lba;     // 本分区起始扇区的lba地址
    uint32_t sec_cnt;       // 本分区的扇区数目
} __attribute__((packed));  // 保证此结构是16字节大小


/// @brief 引导扇区,mbr或ebr所在的扇区
//  512字节 = 引导代码[446] + 分区表[64] + 魔数[2]
struct boot_sector {
    uint8_t other[446];                               // 引导代码
    struct partition_table_entry partition_table[4];  // 分区表中有4项,共64字节
    uint16_t signature;                               // 启动扇区的结束标志是0x55,0xaa,
} __attribute__((packed));


/// @brief 发送选择硬盘指令
/// 向硬盘的端口写入指令
/// @param hd
static void select_disk(struct disk* hd) {
    uint8_t reg_device = BIT_DEV_MBS | BIT_DEV_LBA;
    if (hd->dev_no == 1) {  // 若是从盘就置DEV位为1
        reg_device |= BIT_DEV_DEV;
    }
    outb(reg_dev(hd->my_channel), reg_device);
}


/// @brief 发送选择扇区起始和扇区数命令
/// @param hd 硬盘
/// @param lba 扇区索引
/// @param sec_cnt 扇区数
static void select_sector(struct disk* hd, uint32_t lba, uint8_t sec_cnt) {
    ASSERT(lba <= max_lba);
    struct ide_channel* channel = hd->my_channel;

    // 写入要读写的扇区数
    outb(reg_sect_cnt(channel), sec_cnt);  // 如果sec_cnt为0,则表示写入256个扇区

    // 写入lba地址(即扇区号)
    // lba地址的低8位,不用单独取出低8位.outb函数中的汇编指令outb %b0, %w1会只用al。
    outb(reg_lba_l(channel), lba);
    outb(reg_lba_m(channel), lba >> 8);   // lba地址的8~15位
    outb(reg_lba_h(channel), lba >> 16);  // lba地址的16~23位

    // 因为lba地址的24~27位要存储在device寄存器的0~3位
    // 无法单独写入这4位,所以在此处把device寄存器再重新写入一次
    outb(reg_dev(channel),
         BIT_DEV_MBS | BIT_DEV_LBA | (hd->dev_no == 1 ? BIT_DEV_DEV : 0) | lba >> 24);
}

/// @brief 向通道channel发命令cmd,并标记有额外操作
/// @param channel ide通道索引
/// @param cmd 命令
static void cmd_out(struct ide_channel* channel, uint8_t cmd) {
    channel->expecting_intr = true;  // 表示下次中断有额外操作
    outb(reg_cmd(channel), cmd);
}

/// @brief 从硬盘读入sec_cnt个扇区的数据到buf
/// @param hd 硬盘
/// @param buf 数据
/// @param sec_cnt 扇区数
static void read_from_sector(struct disk* hd, void* buf, uint8_t sec_cnt) {
    uint32_t size_in_byte;
    if (sec_cnt == 0) {
        // 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0
        size_in_byte = 256 * 512;
    } else {
        size_in_byte = sec_cnt * 512;
    }
    // 从数据端口读出 size_in_byte个比特
    insw(reg_data(hd->my_channel), buf, size_in_byte / 2);
}

/// @brief 将buf中sec_cnt扇区的数据写入硬盘
/// @param hd 硬盘
/// @param buf 数据
/// @param sec_cnt 扇区数
static void write2sector(struct disk* hd, void* buf, uint8_t sec_cnt) {
    uint32_t size_in_byte;
    if (sec_cnt == 0) {
        /* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */
        size_in_byte = 256 * 512;
    } else {
        size_in_byte = sec_cnt * 512;
    }
    outsw(reg_data(hd->my_channel), buf, size_in_byte / 2);
}

/// @brief 等待30秒
/// @param hd 硬盘指针
/// @return 超时返回false
static bool busy_wait(struct disk* hd) {
    struct ide_channel* channel = hd->my_channel;

    uint16_t time_limit = 30 * 1000;  // 可以等待30秒
    while (time_limit -= 10 >= 0) {
        if (!(inb(reg_status(channel)) & BIT_STAT_BSY)) {
            return (inb(reg_status(channel)) & BIT_STAT_DRQ);
        } else {
            mtime_sleep(10);  // 睡眠10毫秒
        }
    }
    return false;
}

/// @brief 从硬盘读取sec_cnt个扇区到buf
/// @param hd 硬盘
/// @param lba 地址
/// @param buf 数据
/// @param sec_cnt 扇区数
void ide_read(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt) {
    ASSERT(lba <= max_lba);
    ASSERT(sec_cnt > 0);
    lock_acquire(&hd->my_channel->lock);

    // 1 先选择操作的硬盘
    select_disk(hd);

    uint32_t secs_op;        // 每次操作的要读取的扇区数
    uint32_t secs_done = 0;  // 每次操作的要读取的扇区索引
    while (secs_done < sec_cnt) {
        if ((secs_done + 256) <= sec_cnt) {
            secs_op = 256;
        } else {
            secs_op = sec_cnt - secs_done;
        }

        // 2 写入待读入的扇区数和起始扇区号
        select_sector(hd, lba + secs_done, secs_op);

        // 执行的命令写入reg_cmd寄存器
        cmd_out(hd->my_channel, CMD_READ_SECTOR);  // 准备开始读数据

        // 阻塞自己,等待硬盘完成读操作后通过中断处理程序唤醒自己
        sema_down(&hd->my_channel->disk_done);

        // 4  被唤醒,检测硬盘状态是否可读
        if (!busy_wait(hd)) {  // 若失败
            char error[64];
            sprintf(error, "%s read sector %d failed!!!!!!\n", hd->name, lba);
            PANIC(error);
        }

        // 5 把数据从硬盘的缓冲区中读出
        read_from_sector(hd, (void*)((uint32_t)buf + secs_done * 512), secs_op);
        secs_done += secs_op;
    }

    lock_release(&hd->my_channel->lock);
}

/// @brief 将buf中sec_cnt扇区数据写入硬盘
/// @param hd 硬盘
/// @param lba 数据
/// @param buf
/// @param sec_cnt 扇区数
void ide_write(struct disk* hd, uint32_t lba, void* buf, uint32_t sec_cnt) {
    ASSERT(lba <= max_lba);
    ASSERT(sec_cnt > 0);
    lock_acquire(&hd->my_channel->lock);

    /* 1 先选择操作的硬盘 */
    select_disk(hd);

    uint32_t secs_op;        // 每次操作的扇区数
    uint32_t secs_done = 0;  // 已完成的扇区数
    while (secs_done < sec_cnt) {
        if ((secs_done + 256) <= sec_cnt) {
            secs_op = 256;
        } else {
            secs_op = sec_cnt - secs_done;
        }

        /* 2 写入待写入的扇区数和起始扇区号 */
        select_sector(hd, lba + secs_done,
                      secs_op);  // 先将待读的块号lba地址和待读入的扇区数写入lba寄存器

        /* 3 执行的命令写入reg_cmd寄存器 */
        cmd_out(hd->my_channel, CMD_WRITE_SECTOR);  // 准备开始写数据

        /* 4 检测硬盘状态是否可读 */
        if (!busy_wait(hd)) {  // 若失败
            char error[64];
            sprintf(error, "%s write sector %d failed!!!!!!\n", hd->name, lba);
            PANIC(error);
        }

        /* 5 将数据写入硬盘 */
        write2sector(hd, (void*)((uint32_t)buf + secs_done * 512), secs_op);

        /* 在硬盘响应期间阻塞自己 */
        sema_down(&hd->my_channel->disk_done);
        secs_done += secs_op;
    }
    /* 醒来后开始释放锁*/
    lock_release(&hd->my_channel->lock);
}


/// @brief 将dst中len个相邻字节交换位置后存入buf
/// @param dst
/// @param buf
/// @param len
static void swap_pairs_bytes(const char* dst, char* buf, uint32_t len) {
    uint8_t idx;
    for (idx = 0; idx < len; idx += 2) {
        // buf中存储dst中两相邻元素交换位置后的字符串
        buf[idx + 1] = *dst++;
        buf[idx] = *dst++;
    }
    buf[idx] = '\0';
}

/// @brief 获得硬盘参数信息
/// @param hd 硬盘索引
static void identify_disk(struct disk* hd) {
    select_disk(hd);                        // 选择硬盘端口
    cmd_out(hd->my_channel, CMD_IDENTIFY);  // 向端口写入识别指令

    // 重点 阻塞,等待硬件完成识别
    sema_down(&hd->my_channel->disk_done);

    // 醒来后开始执行下面代码
    if (!busy_wait(hd)) {  //  若失败
        char error[64];
        sprintf(error, "%s identify failed!!!!!!\n", hd->name);
        PANIC(error);
    }

    // 读取1个扇区到id_info
    char id_info[512];
    read_from_sector(hd, id_info, 1);

    char buf[64];
    uint8_t sn_start = 10 * 2;
    uint8_t sn_len = 20;
    swap_pairs_bytes(&id_info[sn_start], buf, sn_len);
    printk("   disk %s info:\n      SN: %s\n", hd->name, buf);

    memset(buf, 0, sizeof(buf));
    uint8_t md_start = 27 * 2;
    uint8_t md_len = 40;
    swap_pairs_bytes(&id_info[md_start], buf, md_len);
    printk("      MODULE: %s\n", buf);

    uint32_t sectors = *(uint32_t*)&id_info[60 * 2];
    printk("      SECTORS: %d\n", sectors);
    printk("      CAPACITY: %dMB\n", sectors * 512 / 1024 / 1024);
}

/// @brief 扫描硬盘hd中地址为ext_lba的扇区中的所有分区
/// @param hd 硬盘
/// @param ext_lba 起始索引
static void partition_scan(struct disk* hd, uint32_t ext_lba) {
    struct boot_sector* bs = sys_malloc(sizeof(struct boot_sector));
    ide_read(hd, ext_lba, bs, 1);

    // 遍历分区表4个分区表项
    struct partition_table_entry* p = bs->partition_table;


    uint8_t part_idx = 0;
    while (part_idx++ < 4) {
        if (p->fs_type == 0x5) {  // 若为扩展分区
            if (ext_lba_base != 0) {
                // 子扩展分区的start_lba是相对于主引导扇区中的总扩展分区地址
                partition_scan(hd, p->start_lba + ext_lba_base);
            } else {  // ext_lba_base为0表示是第一次读取引导块,也就是主引导记录所在的扇区
                // 记录下扩展分区的起始lba地址,后面所有的扩展分区地址都相对于此
                ext_lba_base = p->start_lba;
                partition_scan(hd, p->start_lba);
            }
        } else if (p->fs_type != 0) {  // 若是有效的分区类型
            if (ext_lba == 0) {        // 此时全是主分区
                hd->prim_parts[p_no].start_lba = ext_lba + p->start_lba;
                hd->prim_parts[p_no].sec_cnt = p->sec_cnt;
                hd->prim_parts[p_no].my_disk = hd;
                list_append(&partition_list, &hd->prim_parts[p_no].part_tag);
                sprintf(hd->prim_parts[p_no].name, "%s%d", hd->name, p_no + 1);  // 主分区数字是1~4

                p_no++;
                ASSERT(p_no < 4);  // 0,1,2,3
            } else {
                hd->logic_parts[l_no].start_lba = ext_lba + p->start_lba;
                hd->logic_parts[l_no].sec_cnt = p->sec_cnt;
                hd->logic_parts[l_no].my_disk = hd;
                list_append(&partition_list, &hd->logic_parts[l_no].part_tag);
                // 逻辑分区数字是从5开始
                sprintf(hd->logic_parts[l_no].name, "%s%d", hd->name, l_no + 5);

                l_no++;
                if (l_no >= 8)  // 只支持8个逻辑分区,避免数组越界
                    return;
            }
        }
        p++;
    }

    sys_free(bs);
}

/// @brief 打印分区信息
/// @param pelem
/// @param UNUSED
/// @return
static bool partition_info(struct list_elem* pelem, int arg UNUSED) {
    struct partition* part = elem2entry(struct partition, part_tag, pelem);
    printk("   %s start_lba:0x%x, sec_cnt:0x%x\n", part->name, part->start_lba, part->sec_cnt);

    // 在此处return false与函数本身功能无关,
    //  只是为了让主调函数list_traversal继续向下遍历元素
    return false;
}

/// @brief 硬盘中断处理程序
/// @param irq_no 中断号
void intr_hd_handler(uint8_t irq_no) {
    ASSERT(irq_no == 0x2e || irq_no == 0x2f);

    uint8_t ch_no = irq_no - 0x2e;
    struct ide_channel* channel = &channels[ch_no];
    ASSERT(channel->irq_no == irq_no);

    // 不必担心此中断是否对应的是这一次的expecting_intr,
    //  每次读写硬盘时会申请锁,从而保证了同步一致性
    if (channel->expecting_intr) {
        channel->expecting_intr = false;

        sema_up(&channel->disk_done);  // 唤醒

        // 读取状态寄存器使硬盘控制器认为此次的中断已被处理
        inb(reg_status(channel));
    }
}

/// @brief 硬盘数据结构初始化
void ide_init() {
    printk("[ide] ide_init start\n");

    list_init(&partition_list);


    uint8_t hd_cnt = *((uint8_t*)(0x475));  // 获取硬盘的数量  2个硬盘
    ASSERT(hd_cnt > 0);
    channel_cnt = DIV_ROUND_UP(hd_cnt, 2);  // 一个ide通道上有两个硬盘 1个ide通道


    // 处理每个ide通道的两个硬盘
    struct ide_channel* channel;
    uint8_t channel_no = 0;
    while (channel_no < channel_cnt) {
        channel = &channels[channel_no];

        lock_init(&channel->lock);                    // 初始化锁
        sprintf(channel->name, "ide%d", channel_no);  // 设置ide通道名字

        // 为每个ide通道设置端口号、中断向量、并注册中断号处理
        switch (channel_no) {
            case 0:
                channel->port_base = 0x1f0;  // ide0通道的起始端口号是0x1f0
                channel->irq_no = 0x20 + 14;
                break;
            case 1:
                channel->port_base = 0x170;  // ide1通道的起始端口号是0x170
                channel->irq_no = 0x20 + 15;
                break;
        }
        register_handler(channel->irq_no, intr_hd_handler);

        sema_init(&channel->disk_done, 0);
        channel->expecting_intr = false;

        // 分别获取两个硬盘的参数及分区信息
        uint8_t dev_no = 0;
        while (dev_no < 2) {
            struct disk* hd = &channel->devices[dev_no];
            hd->my_channel = channel;                                  // 设置硬盘的ide通道
            hd->dev_no = dev_no;                                       // 设置硬盘索引
            sprintf(hd->name, "sd%c", 'a' + channel_no * 2 + dev_no);  // 设置硬盘名 sda和sdb

            // 重点 阻塞
            identify_disk(hd);  // 获取硬盘参数

            // 扫描该硬盘上的分区
            if (dev_no != 0) {  // 内核本身的裸硬盘(hd60M.img)不处理
                partition_scan(hd, 0);
            }

            p_no = 0, l_no = 0;
            dev_no++;
        }

        channel_no++;
    }

    printk("\n   all partition info\n");
    //   打印所有分区信息
    list_traversal(&partition_list, partition_info, (int)NULL);

    printk("[ide] ide_init done\n");
}

init.c

 // 文件: init.c
 // 时间: 2024-07-22
 // 来自: ccj
 // 描述: 内核所有初始化操作

 #include "init.h"
 #include "print.h"
 #include "interrupt.h"
 #include "timer.h"
 #include "memory.h"
 #include "thread.h"
 #include "keyboard.h"
 #include "console.h"
 #include "tss.h"
 #include "syscall-init.h"
+#include "ide.h"

 /// @brief 内核所有初始化
 void init_all() {
     put_str("init all\n");

     idt_init();       // 初始化中断
     timer_init();     // 调快时钟、注册时钟中断来调度线程
     mem_init();       // 初始化内存管理系统
     thread_init();    // 初始化线程
     console_init();   // 控制台初始化最好放在开中断之前
     keyboard_init();  // 键盘初始化
     tss_init();       // tss初始化
     syscall_init();   // 初始化系统调用
+    intr_enable();    // 打开中断
+    ide_init();       // 初始化硬盘
 }

main.c

// 文件: main.c
// 时间: 2024-07-19
// 来自: ccj
// 描述: 内核从此处开始

#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall.h"
#include "syscall-init.h"
#include "stdio.h"
#include "memory.h"

int main(void) {
    put_str("I am kernel\n");

    init_all();
    while (1);

    return 0;
}

运行

在这里插入图片描述

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

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

相关文章

[Web服务器] 简易静态Web服务器的搭建

什么是Web服务器 可以为发出请求的浏览器提供静态文档的程序。 平时我们浏览百度新闻数据的时候&#xff0c;每天的新闻数据都会发生变化&#xff0c;那访问的这个页面就是动态的&#xff0c;而我们开发的是静态的&#xff0c;每天访问我们自己的静态web服务器&#xff0c;页面…

宠物托运网站如何搭建?5步帮你吸引70%顾客!

引言 随着人们生活水平的提高和宠物文化的普及&#xff0c;越来越多的宠物主人开始选择带着自己的宠物一同旅行或搬迁至新的城市。这一趋势催生了宠物托运服务行业的快速发展&#xff0c;而一个专业、高效的宠物托运网站则成为了连接宠物主人与托运服务提供商之间不可或缺的桥…

七夕情人节送什么礼物?四款好物分享,情侣必看!

在浪漫的七夕情人节&#xff0c;为心爱的人挑选一份特别的礼物是每对情侣表达爱意的重要方式。市场上琳琅满目的选择让人眼花缭乱&#xff0c;究竟什么样的礼物能触动TA的心弦&#xff1f;本篇分享将为您精选四款既实用又充满情意的好物&#xff0c;无论是甜蜜的开始还是长久的…

【区块链+医疗健康】医链 - 区块链医疗信息管理系统 | FISCO BCOS应用案例

根据《“十四五”规划和 2035 远景目标纲要》&#xff0c;我国在“十四五”时期将全面推进医疗信息化建设。工信部等部 门联合发布《关于加快推动区块链技术应用和产业发展的指导意见》&#xff0c;促进区块链在医疗健康等公共服务领域开 展应用&#xff0c;促进业务协同办理。…

1. dact-admin 中如何发布一个表并添加到导航栏

1.页面脚手架中根据自己需求选择生成的文件例如模型、控制器、翻译文件、数据库迁移文件等 访问 http://【你的域名】/admin/helpers/scaffold 例如&#xff1a; http://localhost:8000/admin/helpers/scaffold 2. 新建好之后来到路由文件 路径随便写&#xff0c;当然按照惯…

[C++] 模板进阶:特化与编译链接全解析

文章目录 非类型模板类型形参非类型模板参数代码示例 **模板的特化**为什么要有模板的特化函数模板特化使用场景与示例函数模板特化的实现细节 类模板特化全特化示例 偏特化部分优化通过进一步限制模板参数进行特化偏特化为指针类型示例&#xff1a;偏特化为引用类型示例&#…

红酒与午后:悠闲时光的惬意选择

阳光透过窗棂&#xff0c;轻轻洒在木质的茶几上&#xff0c;斑驳的光影交织出一幅静谧而温暖的画面。在这宁静的午后&#xff0c;一瓶洒派红酒&#xff08;Bold & Generous&#xff09;静静地摆放在那里&#xff0c;仿佛正等待着与你一同开启一段悠闲的品酒时光。 一、午后…

【往届均已完成EI、SCOPUS检索】第四届电气工程与计算机技术国际学术会议(ICEECT 2024,9月27-29)

第四届电气工程与计算机技术国际学术会议&#xff08;ICEECT2024&#xff09;将于9月27日-29日在哈尔滨举办。 会议主要围绕"电路与系统"、“电气工程材料”、“计算机视觉”、“计算机技术”等专业研究领域展开讨论。旨在为气工程、计算机技术等领域的专家学者及企业…

Figma汉化教程

Figma汉化教程&#xff0c;需要 5 步 第一步&#xff1a;我们打开一个Figma中文社区网站 https://www.figma.cool/cn。我们点击左上角的Figma汉化&#xff0c;进入Figma汉化安装的页面。 第二步&#xff1a;在Figma 软件汉化页面中&#xff0c;选择谷歌浏览器汉化&#xff0c;点…

Litestar GET function blocks OpenAI

题意&#xff1a;Litestar GET 函数阻塞 OpenAI 问题背景&#xff1a; When I transfer function to litestar, it suddenly stops OpenAI from returning a completion. I can print to console every declared variable except answer: 当我将函数传递给 litestar 时&#…

解析蚂蚁T21 190T 算力与能效的新突破

蚂蚁T21 190T 的参数如下&#xff1a; ● 产生币种&#xff1a;B & T & C ● 额定算力&#xff1a;190T ● 额定功耗&#xff1a;3610W ● 功耗比&#xff1a;19.0J/T ● 额定电压&#xff1a;380~415V ● 芯片参数&#xff1a;采用全新的5nm芯片技术&#xff08…

Linux笔记 --- Linux内核链表

Linux 内核链表 经过上一小节的分析&#xff0c;我们知道了传统链表的先天缺陷&#xff1a;没有将具体的数据从组织这些数据的逻辑结构中剥离&#xff0c;而Linux内核链表的思路&#xff0c;正是从一方面着手&#xff0c;追根溯源直抵病灶&#xff0c;彻底颠覆了传统链表&…

Linux内网环境部署thingsboard(离线部署)

先说明一下内网部署的环境,我这里是安装的thingsboard3.6.4 下面所有环境包的版本都是基于这个版本 我们需要安装jdk11,postgres数据库&#xff0c;这里注意下jdk必须使用rpm方式安装&#xff0c;要不后面安装Thingsboard会提示你没有检测到jdk. 下面我们就一步一步来 1.先下…

同城货运软件开发货运搬家系统源码基于Java开发的货运平台

一.管理端配置及操作 1.服务配置 添加:服务,给服务添加车型和车厢,以及收费金额 2.用户中心 分为普通用户 师傅用户 和推广员用户; 推广员用户的一二级分佣不为0,可给推广员设置一二级佣金 3.车厢/车型管理 给服务添加车厢 和车型,选择 服务后,只能选择该服务关联的车型和…

Tomato靶机~文件包含日志

寻找网站上传点并把 php 恶意代码文件改成 jpg 上传到网站上在本地包含引入恶意代码&#xff0c;当文件被引入后代码就被执行&#xff1b; 0x01信息收集 # 环境准备&#xff1a; Target IP&#xff1a;192.168.66.143 Attack IP&#xff1a;192.168.66.84 靶机目标&#xff1…

无缝协作的艺术:Codigger 视频会议(Meeting)的用户体验

在当今数字化的时代&#xff0c;远程协作已经成为工作和学习中不可或缺的一部分。然而&#xff0c;远程协作也面临着诸多挑战&#xff0c;如沟通不畅、信息同步不及时、协作工具的复杂性等。而 Codigger 视频会议&#xff08;Meeting&#xff09;作为一款创新的工具&#xff0c…

伦敦银和伦敦金的关系是怎么样的?

在贵金属投资市场中&#xff0c;有两个品种是经常被投资者讨论的&#xff0c;一个是伦敦银&#xff0c;而另外一个是伦敦金&#xff0c;他们的名字很相似&#xff0c;那实际上他们有何关系呢&#xff1f;下面我们就来简单地讨论一下。 伦敦银其实也叫国际现货白银&#xff0c;是…

探索3D视觉中的Transformer架构:通用Backbone与自适应采样策略 !

1 Introduction 计算机视觉中的一个基本问题是在三维空间中理解和识别场景与物体。它允许以紧凑的方式表达关系&#xff0c;并提供在现实世界中导航和操作的能力。3D视觉在各个领域都发挥着重要作用&#xff0c;包括自动驾驶、机器人技术、遥感、医疗、增强现实、设计行业等众…

【Nuxt】初识 Nuxt 和目录说明

初识 Nuxt Nuxt3 支持 Vue3 及其周边生态&#xff0c;提供前后端功能&#xff0c;支持 CSR(SPA)&#xff0c;SSR&#xff0c;SSG 渲染模式的应用。 Nuxt3 特点&#xff1a; Vue技术栈 Nuxt3是基于Vue3Vue RouterVite等技术栈&#xff0c;全程Vue3Vite开发体验(Fast)。 自动导…

6大类果蔬食物百科大全ACCESS数据库

其实今天这个数据库早些时候就已经搞到了&#xff0c;但是鉴于它的多表结构不太喜欢就一直没有整理&#xff0c;然而现在仔细看起来&#xff0c;又觉得这种安排好处还是很好的&#xff0c;清晰明了。发上来看看有没有朋友喜欢&#xff0c;包含了水果类、蔬菜类、坚果类、肉类、…