【操作系统】设备驱动

本文为学习李治军老师《操作系统原理、实现与实践》第八章的总结,主要讲述显示器、键盘设备驱动。

参考资料:

  1. 哈工大李治军老师操作系统mooc视频
  2. 实践项目解析

第八章 设备驱动

设备驱动的基本原理

  • 外设工作原理

    • 从CPU开始:CPU发送命令(给外设中的寄存器),最终表现为执行指令“out ax, 端口号”;

    • 从外设开始:外设在工作完成后或出现状态变化时产生中断 ,CPU 通过中断处理程序完成后续工作;

  • 文件视图

    • 不论什么样的外设,操作系统都将其统一抽象成一个文件,程序员通过文件接口open, read, write, close来使用外设;

    • 不同的设备对应不同的设备文件:/dev/xxx;

    • 上层用户使用外设的基本结构:

      int fd = open(“/dev/xxx”);//打开外设文件
      for (int i = 0; i < 10; i++) {
      write(fd,i,sizeof(int));//向外设文件写入
      }
      close(fd);//关闭外设文件
      
  • 补充:设备文件(参考IO设备

    • 操作系统把一切外设都映射为文件,被称作设备文件(如磁盘文件),常见的设备文件又分为三种:
      1.字符设备 如键盘,鼠标,串口等(以字节为单位顺序访问);

      2.块设备 如磁盘驱动器,磁带驱动器,光驱等(均匀的数据库访问);

      3.网络设备 如以太网,无线,蓝牙等(格式化报文交换);

显示器的驱动

  • 显示器和键盘构成了终端设备 dev/tty,显示器只写,键盘只读;

  • 以printf为例:

    • printf()调用系统调用write(1, buf, count)

    • write 的内核实现是 sys_write:找到所写文件的属性,根据设备文件中存放的设备属性信息分支到相应的操作命令;

      • 分支1:根据文件属性(是否是字符设备);

        //在linux/fs/read_write.c中
        int sys_write(unsigned int fd, char *buf,int cnt)
        { 
        	inode = file->f_inode;
        	if(S_ISCHR(inode->i_mode))
        	return rw_char(WRITE,inode->i_zone[0], buf,cnt); 
        	...
        
      • 分支2:在rw_char中根据不同的读写函数:

        //在linux/fs/char_dev.c中
        int rw_char(int rw, int dev, char *buf, int cnt)
        { 
        //以主设备号MAJOR(dev)为索引从函数表crw_table中找到和终端设备对应的读写函数rw_ttyx并调用
        	crw_ptr call_addr=crw_table[MAJOR(dev)];
        	call_addr(rw, dev, buf, cnt); 
        	...
        }
        
      • 分支3:根据读/写操作:

        //根据是设备读操作还是设备写操作调用相应的函数
        static int rw_ttyx(int rw, unsigned minor, char *buf, int count)
        { 
        	return ((rw==READ)? tty_read(minor,buf): tty_write(minor,buf));
        }
        
    • printf是输出所以调用tty_write(minor,buf)实现输出:先将内容写到缓冲区里,由操作系统统一将队列中的内容输出到显示器上,如果缓冲区已满,就睡眠等待;

      在linux/kernel/tty_io.c中
      int tty_write(unsigned channel,char *buf,int nr)
      { 
      	struct tty_struct *tty;tty=channel+tty_table;
      	sleep_if_full(&tty->write_q); //输出就是放入队列
      	...
      }
      
    • 如果输出完成或写队列满

      int tty_write(unsigned channel,char *buf,int nr)
      { 
          ...
      	tty->write(tty);//用tty中write函数指针来进行真实的显示器输出
      }
      
    • 最终调用con_write向终端写数据,核心代码为:

      mov c, al
      mov attr, ah
      mov ax, [pos] ;显存和内存独立编址用out,显存和内存混合编址用mov
      

      其中pos为开机后当前光标所在的显存地址;

  • printf()输出函数的完整文件视图路线

    • printf -> write -> sys_write -> rw_char -> rw_ttyx ->tty_write -> write_q -> con_write -> “mov ax, [pos]

键盘的驱动

  • 从键盘中断开始

    • 设置键盘中断号,按下键盘会产生 0x21 号中断,执行keyboard_interrupt中的inb $0x60,%al

      void con_init(void) //应为键盘也是console的一部分
      { set_trap_gate(0x21, &keyboard_interrupt); }
      
      //在kernel/chr_drv/keyboard.S中
      .globl _keyboard_interrupt
      _keyboard_interrupt:
      inb $0x60,%al //从端口0x60读扫描码
      call key_table(,%eax,4) //根据扫描码调用不同处理函数key_table()来处理各个按键
      ... 
      push $0 
      call _do_tty_interrupt
      
      • key_table是一个函数数组,在其中调用do_self()函数处理显示字符,其他特殊按键由func 等其他函数来处理;
    • do_self函数

      • 从键盘对应的ASCII码表(key_map中以当前案件的扫描码为索引找到当前案件的ASCII码);
      • 找到 tty 结构体中的 read_q 队列,键盘和显示器使用同一个 tty 结构体 tty_table[0],只是键盘使用的读队列,而显示器使用的写队列;
      • 将 ASCII 码放到缓冲队列 read_q 中;
    • 字符回显,先调用 do_tty_interrupt 返回文件视图(与显示器输出中将字符先放在缓冲队列 write_q 中再显示到屏幕上同理)

      void do_tty_interrupt(int tty) {
      	copy_to_cooked(tty_table + tty);
      }
      void copy_to_cooked(struct tty_struct *tty) {
          GETCH(tty->read_q, c);
          PUTCH(c, tty->secondary);
          ...
          wake_up(...);
      }
      
    • copy_to_cookedread_q中取出字符并放在tty->secondary队列中,唤醒等待在该队列中的进程;

    • 对于键盘,用户发起的文件操作是scanfscanf调用sys_read,根据printf类似的分支,最终调用tty_read

    • tty_read:一旦tty->secondary有内容了,scanf会将队列中的字符诸葛取出来并复制到用户内存,从设备开始的路线最后回到了文件操作scanf;

  • 键盘操作的完整文件视图:

    • keyboard_interrupt -> inb 0x60, al -> do_self -> read_q -> copy_to_cooked -> secondary -> wake_up -> tty_read -> rw_char ->sys_read ->read ->scanf

实践项目7

终端设备字符显示的控制

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值