22.IO与显示器

2023-05-16

【README】

1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;

2.显示器是输入型外设;
3.本章主要内容是讲 显示器是如何被驱动的;或操作系统是如何让用户来使用显示器的;
4.Printf是如何把字符打在显示器上的;


【1】cpu操作外设背景

1)终端设备管理

  • 包括 键盘和显示器;

【图解】
本章主要讲中断 IO设备 ,包括键盘和显示器;

2)如何让外设工作起来 ;

【图解】
1)外设工作原理:

  • 步骤1:Cpu向外设控制器发出指令(如写命令),向外设控制器中的寄存器(或存储器)读写数据;如显卡控制器;
  • 步骤2:写入完成后,外设控制器发出中断请求通知cpu写入完成; 随后,外设控制器会根据寄存器(或存储器)的内容实际来操控外设;如显存;
  • 步骤3:cpu在处理外设控制器中断时,可能会读数据到内存,当然还会进行其他后续处理;

补充: cpu向外设控制器发送的指令示例。

  • out xx, al  , 其中xx是外设控制器端口,即cpu向外设端口发送命令;  

2)总结:cpu让外设工作起来做3件事情

  • 事情1: 向外设控制器发送out指令;
  • 事情2: 外设控制器执行指令完成后,发出中断请求,cpu处理中断;
  • 事情3: 为了让使用外设简单,操作系统要提供一种统一的文件视图(因为各个厂商外设控制器设计不一样,如接口不一样,数据格式不一样,所以要提供统一的文件视图,或统一接口),即把外设看做一个文件

 


【2】CPU操作外设

【2.1】操作外设的程序

printf 函数: 就是操作外设的程序;

【图解】

1)一段操作外设的程序

// 一段操纵外设的程序 
// 打开外设对应的文件(dev,device设备) 
int fd = open("/dev/xx"); 
for (int i=0; i<10; i++) 
{
	write(fd, i, sizeof(int)); 
}
close(fd); 

 2)操作系统为用户提供统一接口:

  • 无论什么设备都是,操作接口都是 open, read, write, close ;

3)根据设备文件找到控制器的地址,内容格式等;

  • 不同设备对应不同的设备文件 (/dev/xxx)

3)统一的文件视图


操作系统无论操作哪种设备,都需要调用统一的文件接口(文件视图),包括open,read,write,close ;


【2.2】输出到显示器

1)Printf 函数:

  • Printf不是真相,printf底层调用了 系统调用write 把数据写入到显存,
  • 而write系统调用或接口 最终会形成类似 out xx,al 指令发送给显存控制器 ;

 【图解】
1)系统调用 write

// 在 linux/fs/read_write.c 中 
// fd等于1,则写到显存; fd等于2,则写到打印机(举例)
// buf 对应内存缓冲区,要写出的数据先缓存到缓冲区,然后再写出到外设,如显存;
// count 写入字节数 
int sys_write(unsigned int fd, char *buf, int count) 
{
	struct file* file; 
	// fd等于1,打开显示器(显存)的文件 
	// current 指的是当前进程pcb 
	file = current->filp[fd]; 
	// 获取文件信息 
	inode = file -> f_inode; 
}

补充: inode  

  • inode (index node)是指在许多“类Unix文件系统”中的一种数据结构,用于描述文件系统对象(包括文件、目录、设备文件、socket、管道等)。

2)fd=1的filp(文件指针)从哪里来 ?

【图解】
file = current->filp[fd];

其中fd=1;即fd=1对应到设备dev/tty0 ,而tty就是中断设备;
Open方法也是一个系统调用 sys_open ;

 

 小结:

  • write(1, buf, ...) 中的1 是 open(“dev/tty0”) 产生的;
  • 所以就形成了 父进程pcb文件指针 指向 dev/tty0的inode的链条;
  • 核心就是把 dev/tty0的设备信息放入(通过open系统调用)inode,并把inode读入内存;  
  • 根据inode把数据写入外设文件;

补充:inode (index node)是一种结构体用于描述文件信息 或用于描述文件系统对象(包括文件、目录、设备文件、socket、管道等)。

3)向显存(屏幕)写出数据

【图解】
Write系统调用 

// write 系统调用 
在 linux/fs/read_write.c 中 
int sys_write(unsigned int fd, char *buf, int cnt) 
{
	// 获取外设  /dev/tty0 的inode 
	inode = file -> f_inode;
	// 判断是否字符设备; 计算机设备分为字符设备或块设备;  
	if (S_ISCHR( inode -> i_mode )) 
	{
		// 读写字符,WIRTE 是写;
		// inode->i_zone[0]表示字符设备列表中的第几个设备的设备号(本例取4) ; 
		return wr_char(WRITE, INODE->I_ZONE[0],buf, cnt) ; 
	}
} 
// rw_char 
// 在 linux/fs/char_dev.c 中 
int rw_char(int rw, int dev, char *buf, int cnt) 
{
	// crw_table 表示字符设备操作的处理函数列表,其中 call_addr是函数指针; 
	crw_ptr call_addr = crw_table[MAJOR(dev)]; 
	call_addr(rw, dev, buf, cnt); ... 
}

4)字符设备处理函数列表 crw_table  (第4个字符设备处理函数是 rw_ttyx )

【图解】

  • 补充:终端设备指的是键盘和显示器; 其中键盘是读的,显示器是写的;
  • tty_write:写出数据到字符设备的核心函数; 其中 tty->write_q 是把数据写入到缓冲区(队列write_q);

4) tty_write(unsigned channel, char* buf, int nr) 写出数据的核心函数

 

【图解】
缓冲区写完之后,tty->wirte(tty) 把缓冲区数据刷新到显存(显卡)

5)tty->write(*write) 把缓冲区数据刷新到显存

【图解】

  • con_write  == console_write ;指的是显示器写函数(显存写入函数,属于显存驱动程序);
  •   从write_q队列或缓冲区中取出字符,并写入到显存;
  • Movb _attr, %%ah, 把属性值 送入 ax寄存器高位;
  • Movw %%ax, %1 把 ax 寄存器内容 写入到 外设控制器的存储空间(%1 指向pos指针);其中al存储了缓冲区字符,pos指针指向了显存基址(显存与内存统一编址,在同一个内存空间中);
  • con_write函数每次只能写出一个字符到显存,多个字符循环调用 con_write 即可;

补充1:

  • 有些外设控制器的寄存器地址(或存储器地址)可以与内存地址空间统一编址;寻址用mov;
  • 如果外设控制器的寄存器地址独立编址,寻址用out;
  • 即 mov 和 out 是一样的,没有本质区别;
  • 又显存特别大,通常是与内存统一编址,所以使用的是 mov汇编指令;

补充2:如何写设备驱动程序?

  • 步骤1:写出核心的out指令;
  • 步骤2:然后将相应函数注册到这些表上;
  • 步骤3:创建一个dev/xxx 外设文件;这个文件和你注册表上的设备名对应上;

【小结】
操作系统中最核心的kernel内核还是cpu进程管理与内存管理; 外围的设备驱动与文件系统很简单;

6)mov pos
把缓存区的数据写出到显存;

 

【图解】

  • 显存的初始地址pos 是由 con_init() 函数初始化的,pos从0xA0000 开始 ;

6)修改 pos 显存

  • pos +=2  
  • Pos 指向了显存基址;每写入一个字符到显存后,pos指针都会加2;因为写入一个字符占1个字节,字符属性占1个字节,所以是加2,往后移动2个字节;

 


 【3】printf 整个过程 

【图解】

  • 步骤1:调用printf 库函数;
  • 步骤2:调用系统调用 sys_open 打开终端文件 "dev/tty0" 获得设备号fd(显示器的设备号是1),和文件指针file;
  • 步骤3:传入设备号fd 调用系统调用 sys_write;sys_write 通过文件指针找到外部设备文件的inode,判断该文件是否是字符文件;
  • 步骤4:若是字符文件,则通过 inode 从 字符设备操作函数数组  crw_table  中查询 设备操作函数指针 rw_ttyx (外设文件读写函数);
  • 步骤5:rw_ttyx 设备操作函数 通过传入的 WRITE标识 调用 tty_write() 函数写出数据到外设文件;
  • 步骤6:tty_write函数 先把数据写出到缓冲区(或队列) write_q;
  • 步骤7:写出数据到缓冲区 write_q 完成后,调用 显示器写函数 con_write();
  • 步骤8: con_write() 把缓冲区的字符循环写入到 显存; 写入显存命令为 mov pos,c ; pos+=2;其中 pos指针指向了显存基址;每写出一个字符,pos自加2;因为写入了2个字节,pos需要向后移动2个字节;1个字节是字符内容,1个字节是字符属性; 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

22.IO与显示器 的相关文章

随机推荐

  • 解决ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.26‘ not found

    报错信息如题所示 原因 xff1a 这个是默认路径下的libstdc 43 43 so 6缺少GLIBCXX 3 4 26 xff0c 你有可能缺少其它版本的比如3 4 26 xff0c 解决方法一样 xff0c 如下所示 xff1a xf
  • Eigen 库常用基本用法 备忘

    ps xff1a eigen 看到的时候较多 xff0c 自己写的时候总有一些用法想不起来具体函数名 xff0c 特此总结一下以备忘 官方doc eigen 官网最权威 目录 Eigen 矩阵定义 Eigen 基础使用 Eigen 特殊矩阵
  • Hector slam算法原理解析与代码详解

    写了markdown 上传 xff0c 公式都乱码 xff0c 无果 xff0c 截图上传吧 目录 1 hector 原理解析 1 4 多重分辨率地图 2 代码框架 2 1 回调函数 2 2 更新 3 扫描匹配 3 1 多分辨率匹配 3 2
  • Logistic映射的简单理解

    Logistic映射 在看论文时看到了这个概念 xff0c 于是就去简单了解了一下 参考博客 1 前言 谈到Logistic映射就要先谈一谈什么是混沌系统 百度百科上的解释是 xff0c 混沌系统是指在一个确定性系统中 xff0c 存在着貌
  • _findnext 报错

    ps 编译环境 qt 43 mingw32 编译没问题 xff1b 换到qt 43 msvc 2017 64 就出现问题 xff1b 报错信息 xff1a Stopped in thread 0 by Exception at 0x7ffb
  • bug解决: ffmpeg 在window下使用 PRId64 报错

    在timestamp h 中 调用 av ts make string报错 error expected before PRId64 原因 xff1a 该宏定义给c用的 xff0c C 43 43 要用它 xff0c 就要定义一个 STDC
  • qt: error: C2001: 常量中有换行符

    PS 这两天搞工程系统移植 xff0c 搞得疯掉了 xff0c 代码复用还不如重写呢 如下一句带有中文的程序 xff0c mingw 43 linux 运行没有任何问题 xff0c window下msvc 运行就报错C2001 time s
  • Eigen内存分配器aligned_allocator

    在使用Eigen的时候 xff0c 如果STL容器中的元素是Eigen数据库结构 xff0c 比如下面用vector容器存储Eigen Matrix4f类型或用map存储Eigen Vector4f数据类型时 xff1a vector lt
  • Ubuntu 升级cmake 版本

    PS 在编译一些包时需要更高的版本 xff0c 需要升级 cmake 千万别执行下面的命令 xff0c 这样会把之前用 cmake 编译好的包都给卸载掉 xff0c 包括ros sudo apt get autoremove cmake 比
  • 视觉slam十四讲(ch6) Ubuntu18.04安装 g2o库 报错error: FixedArray ... has no member named ‘fill’

    ps 再学习14讲第二版的时候 xff0c 运行g2o 报错 error FixedArray aka class ceres internal FixedArray lt double 6 gt has no member named f
  • 无人驾驶学习笔记-NDT 配准

    目录 1 NDT 的算法处理流程 2 NDT 公式推导 3 NDT 实例 3 1 常规NDT的位姿估计 3 2 front end node 1 ROS常规初始化 2 初始化操作 xff1a 读取传感器数据 获取lidar to imu变换
  • KD 树原理以及在三维激光点云中的应用

    目录 1 介绍 2 原理 2 1 数据结构 2 2 构建KD树 2 3 实例 3 程序示例 4 参考链接 1 介绍 kd tree简称k维树 xff0c 是一种空间划分的数据结构 常被用于高维空间中的搜索 xff0c 比如范围搜索和最近邻搜
  • slam 基础知识整理之- 最小二乘问题的引出与求解方法

    目录 1 最小二乘引出 2 线性最小二乘 及 求解方法 3 非线性最小二乘 编辑 3 1 求解思路 3 2 常用四种方法 3 3 四种方法总结 4 参考链接 在SLAM的过程中 xff0c 我们可以构建机器人状态过程 通过对其概率的计算 x
  • 无人驾驶学习笔记 - LOAM 算法论文核心关键点总结

    目录 1 框架 2 特征点提取 3 点云去畸变 4 帧间匹配 特征关联与损失函数计算 a 线特征 b 面特征 5 运动估计 6 建图 7 姿态融合 8 LOAM 优劣势 9 参考连接 1 框架 loam框架核心是两部分 xff0c 高频率的
  • 动态窗口法的理解和一些细节

    机器人局部路径规划 动态窗口法 动态窗口法 xff08 Dynamic Window Approach xff0c DWA xff09 是一类经典的机器人局部路径规划算法 它的过程主要分为两部分 xff1a 速度空间 v
  • 无人驾驶学习笔记 - A-LOAM 算法代码解析总结

    目录 1 概述 2 scanRegistration cpp 2 1 代码注释 2 1 1 主函数 2 1 2 removeClosedPointCloud xff08 雷达周边过近点移除 xff09 2 1 3 laserCloudHan
  • 无人驾驶学习笔记-LeGO-LOAM 算法源码学习总结

    目录 1 概述 2 lego loam的贡献 3 系统框图 4 ros graph中的节点关系表 5 lego loam 的文件系统架构 6 各部分方法原理及代码注释 6 1 点云投影与目标分割 1 总结概述 2 代码注释 2 1 copy
  • Boost 中 signal2 用法

    boost 函数与回调 xff08 三 xff09 signals2
  • 树莓派学习笔记

    文章目录 树莓派基础入门笔记无显示屏使用方式基础教程5 树莓派文件传输 配置编译环境使用U盘直接传输使用vnc传输文件FTP文件传输协议Python配置编译环境C C 43 43 配置编译环境Linux常用终端命令nano和vi编辑器的使用
  • 22.IO与显示器

    README 1 本文内容总结自 B站 操作系统 哈工大李治军老师 xff0c 内容非常棒 xff0c 墙裂推荐 xff1b 2 显示器是输入型外设 xff1b 3 本章主要内容是讲 显示器是如何被驱动的 xff1b 或操作系统是如何让用户