驱动程序与操作系统之间的关系是怎样的?

本文详细介绍了驱动程序如何与操作系统交互,包括设备与驱动的关系、中断处理流程,以及通过自定义命令行终端展示驱动与内核的连接。文章重点讨论了设备初始化、驱动程序表的构建和初始化过程,以及中断回调函数在处理键盘事件中的应用。
摘要由CSDN通过智能技术生成

驱动程序与操作系统概览

设备与驱动关系

在这里插入图片描述

中断处理流程: 键盘事件

在这里插入图片描述

设备与驱动概念

设备

举个例子,储存设备,其实不管它是机械硬盘,还是 TF 卡,或者是一个设备控制代码模块,它向操作系统内核表明它是储存设备,但它完全有可能分配一块内存空间来储存数据,不必访问真正的储存设备

所以,操作系统内核所感知的设备,并不需要和物理设备对应,这取决于设备控制代码自身的行为

驱动

操作系统内核和应用程序都不会主动建立设备,那么谁来建立设备呢?当然是控制设备的代码,也就是我们常说的驱动程序

案例: 自定义命令行终端

应用端代码

现在以一个自定义命令行终端,揭示驱动与操作系统之间的关系

/**
 * @brief 自定义命令终端
 *  1. 创建句柄
 *  2. 通过句柄读取信息
 *  3. 通过句柄写入信息
 */
void shell()
{
    hand_t hand = -1;
    char_t charbuff[2] = {0,0};
    u16_t kbcode; 
    hand = open_keyboard();
    memset((void*)&shellkbuff, 0, sizeof(shellkbbuff_t));

    printf("Cosmos shell is init!\n");

    printf("Cosmos@IVANQI:>");
    for(;;) {
        kbcode = read_keyboard_code(hand);
        if (kbcode == 0) return;

        if (32 <= kbcode && 127 >= kbcode) {
            charbuff[0] = (char_t)kbcode;
            skb_buff_write(&shellkbuff, charbuff[0]);
            skb_buff_disp(&shellkbuff);
            continue;
        }

        if (0x103 == kbcode) {
            skb_buff_enter(&shellkbuff);
            skb_buff_clear(&shellkbuff);
            continue;
        }

        if (0x104 == kbcode) {
            skb_buff_backspace(&shellkbuff);        
            continue;
        }
    }

    close_keyboard(hand);
    return;
}

设备驱动预先初始化

设备初始化

void init_krldevice()
{
    // 初始化系统全局设备表
    devtable_t_init(&osdevtable);
    return;
}

/**
 * @brief 初始化设备
 *  1. 初始化对应的设备分类
 * 
 * @param initp 
 */
void devtable_t_init(devtable_t *initp)
{
    list_init(&initp->devt_list);
    krlspinlock_init(&initp->devt_lock);
    list_init(&initp->devt_devlist);
    list_init(&initp->devt_drvlist);
    initp->devt_devnr = 0;
    initp->devt_drvnr = 0;

    // 初始化设备链表。 DEVICE_MAX 为设备分类上限
    for (uint_t t = 0; t < DEVICE_MAX; t++) {
        devtlst_t_init(&initp->devt_devclsl[t], t);
    }
    return;
}

初始化所有设备,并赋值到devtable_t,进行设备按类别管理,同时

devtlst_t_init(&initp->devt_devclsl[t], t);

初始化不同类别下的设备资源

最后保存在osdevtable全局变量中,待后续应用端代码使用

驱动初始化

// 驱动程序表,然后再init_krldriver中运行
KRL_DEFGLOB_VARIABLE(drventyexit_t, osdrvetytabl)[] = {
    systick_entry,  // 定时器驱动
    rfs_entry,      // 文件驱动
    uart_entry,     // 键盘驱动
    NULL
};

/**
 * @brief 驱动程序的初始化
 *  1. 根据驱动程序表初始化程序入口
 */
void init_krldriver()
{
    // 遍历驱动程序表中的每个驱动程序入口函数
    for (uint_t ei = 0; osdrvetytabl[ei] != NULL; ei++) {
        // 运行一个驱动程序入口
        if (krlrun_driverentry(osdrvetytabl[ei]) == DFCERRSTUS) {
            hal_sysdie("init driver err");
        }
    }

    kprint("设备驱动初始化成功\n");
    return;
}

对键盘驱动进行驱动初始化

/**
 * @brief 驱动程序入口函数
 *  1. 建立driver_t实例变量
 *  2. 运行驱动程序入口函数
 *  3. 把驱动程序加入系统
 *  4. 把驱动加入挂载到devtable_t
 * 
 * @param drventry 
 * @return drvstus_t 
 */
drvstus_t krlrun_driverentry(drventyexit_t drventry)
{
    driver_t *drvp = new_driver_dsc();              // 建立driver_t实例变量
    if (drvp == NULL) {
        return DFCERRSTUS;
    }

    if (drventry(drvp, 0, NULL) == DFCERRSTUS) {    // 运行驱动程序入口函数
        return DFCERRSTUS;
    }

    // 把驱动加入挂载到devtable_t
    if (krldriver_add_system(drvp) == DFCERRSTUS) { // 把驱动程序加入系统
        return DFCERRSTUS;
    }

    return DFCOKSTUS;
}

比如初始化的是键盘驱动,就会跳转键盘驱动的入口处理函数中

/**
 * @brief 键盘驱动程序入口
 *  1. 新建设备
 *  2. 设置驱动默认函数
 *  3. 把设备加入到驱动的设备链表中
 *  4. 安装中断回调函数接口.用于后续的中断处理
 * 
 * @param drvp 驱动结构体
 * @param val 
 * @param p 
 * @return drvstus_t 
 */
drvstus_t uart_entry(driver_t *drvp, uint_t val, void *p)
{
    if (drvp == NULL) {
        return DFCERRSTUS;
    }

    device_t *devp = new_device_dsc();
    if (devp == NULL) {
        return DFCERRSTUS;
    }

    // 省略以下设备和驱动的初始化信息

    // 把设备加入到驱动的设备链表中
    if (krldev_add_driver(devp, drvp) == DFCERRSTUS) {
        if (del_device_dsc(devp) == DFCERRSTUS) { //注意释放资源。
            return DFCERRSTUS;
        }
        return DFCERRSTUS;
    }

    // 向内核(设备表)注册设备
    if (krlnew_device(devp) == DFCERRSTUS) {
        if (del_device_dsc(devp) == DFCERRSTUS) { //注意释放资源
            return DFCERRSTUS;
        }
        return DFCERRSTUS;
    }

    // 安装中断回调函数接口
    if (krlnew_devhandle(devp, uart_handle, 0x21) == DFCERRSTUS) {
        return DFCERRSTUS; // 注意释放资源。
    }

    ... 
    return DFCOKSTUS;
}

最终设备和驱动数据结构简陋示例图

在这里插入图片描述

中断处理流程: 键盘事件

在这里插入图片描述

中断事件注册

初始化段描述符和TSS描述符

/**
 * @brief 初始化段描述符和TSS描述符
 * 
 * @return void 
 */
PUBLIC LKINIT void init_descriptor()
{
 set_idt_desc(INT_VECTOR_IRQ0 + 1, DA_386IGate, hxi_hwint01, PRIVILEGE_KRNL);
}

汇编定义

; 硬件1~7号中断. 定义了各种硬件中断编号
hxi_hwint01:
    HARWINT (INT_VECTOR_IRQ0+1)

; 硬件中断,硬件分发器函数 hal_hwint_allocator
%macro  HARWINT 1
    SAVEALL

    mov r14w, 0x10
    mov ds, r14w
    mov es, r14w
    mov fs, r14w
    mov gs, r14w

    mov rdi, %1
    mov rsi, rsp
    call hal_hwint_allocator

    RESTOREALL
%endmacro

硬件中断分发器函数

 * @brief 硬件中断分发器函数
 *  1. 调用异常处理函数hal_do_hwint
 * 
 * @param intnumb 第一个参数为中断编号,在rdi
 * @param krnlsframp 第二个参数为中断发生时的栈指针,在rsi
 */
void hal_hwint_allocator(uint_t intnumb, void *krnlsframp)
{
    cpuflg_t cpuflg;
    hal_cpuflag_sti(&cpuflg);

    hal_hwint_eoi();
    hal_do_hwint(intnumb, krnlsframp);
    krlsched_chkneed_pmptsched();

    hal_cpuflag_cli(&cpuflg);
    //kprint("暂时无法向任何服务进程发送中断消息,直接丢弃......\n");
    return;
}

/**
 * @brief 中断处理函数
 *  1. 加锁
 *  2. 调用中断回调函数hal_run_intflthandle
 *  3. 释放锁
 * 
 * @param intnumb 中断码
 * @param krnlsframp 
 */
void hal_do_hwint(uint_t intnumb, void *krnlsframp)
{
  ...
  // 运行中断处理的回调函数
  hal_run_intflthandle(intnumb, krnlsframp);
  ...
}

最后调用中断处理的回调函数

/**
 * @brief 负责调用中断处理的回调函数
 *  1. 先获取中断异常表machintflt
 *  2. 然后调用i_serlist 链表上所有挂载intserdsc_t 结构中的中断处理的回调函数,是否处理由函数自己判断
 * 
 * @param ifdnr 中断码
 * @param sframe 
 */
void hal_run_intflthandle(uint_t ifdnr, void *sframe)
{
    intserdsc_t *isdscp;
    list_h_t *lst;
    // 根据中断号获取中断异常描述符地址
    intfltdsc_t *ifdscp = hal_retn_intfltdsc(ifdnr);

    if (ifdscp == NULL) {
        hal_sysdie("hal_run_intfdsc err");
        return;
    }

    // 遍历i_serlist链表
    list_for_each(lst, &ifdscp->i_serlist) {
        // 获取i_serlist链表上对象即intserdsc_t结构
        isdscp = list_entry(lst, intserdsc_t, s_list);
        // 调用中断处理回调函数
        isdscp->s_handle(ifdnr, isdscp->s_device, sframe);
    }

    return;
}

通过isdscp->s_handle(ifdnr, isdscp->s_device, sframe);就会访问到对应的回调函数,比如 键盘驱动的就是

/**
 * @brief 键盘驱动回调函数
 * 
 * @param ift_nr 
 * @param devp 
 * @param sframe 
 * @return drvstus_t 
 */
drvstus_t uart_handle(uint_t ift_nr, void *devp, void *sframe)
{
    device_t* idev = (device_t*)devp;
    kboard_t* ikbd = (kboard_t*)idev->dev_extdata;
    u8_t scan;
    if (NULL == idev || NULL == ikbd) {
        return DFCERRSTUS;
    }

    scan = (u8_t)read_keybd();
    kbd_write_scan(ikbd, scan);

    krlsem_up(&ikbd->kb_sem);
    return DFCOKSTUS;
}

最终呈现结果

在这里插入图片描述

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值