前言
键盘工作的探寻,以及硬件是如何工作的探寻
键盘是常见的输入设备,讨论他和操作系统的交互,以及操作系统与程序之间的交互.
概述
键盘的接口(现在一般是USB接口,以前是圆口)和主板相连,在某个程序中,敲击键盘执行某些指令.从数据的角度来仔细分析这一过程:键盘输入的数据,由操作系统扫描后,得到某些数据Data,再发给当前程序执行操作.其中操作系统想得到数据Data需要调用硬件驱动程序.当前程序执行的操作依赖于Data
键盘和操作系统的交互
下面用一段拟人的对话来解释键盘和操作系统之间的互动
键盘:报告,我是一个硬件设备,代号xxxxxxxxxx(每个硬件设备都有mac码--机器识别码)
操作系统:有什么指教?
键盘:我已连接主板接口,申请向你传送数据.请将这些数据,在程序programA运行时传给他
操作系统:好的
操作系统扮演的角色
操作系统负责提取端口数据,并将数据发给需要的程序.
操作系统像多个邮箱的所有者(邮箱就是硬件端口).而硬件是邮箱租赁者.他们有约定:硬件不断往邮箱里投递信件(数据),操作系统负责把邮件送到指定的位置,但不允许租赁邮箱的人翻看除了自己租赁外邮箱的内容(数据泄露)
但在笔者的例子中,所有租赁者的邮件(数据)内容完全公开,由操作系统屏蔽掉非当前进程.
1.提取端口数据
CPU有硬件中断机制,可以访问内存中的所有地址.这里笔者认为在安装主板的时候,应申请一些内存空间作为端口地址---此后的程序中这些地址不能被用作其他用途,例如分配给程序使用.然后由CPU轮询端口地址,取得数据.因此操作系统内部有下面一张表:
|-----|------------|---------|
| 端口号 | 端口地址 | 对应设备mac |
| 1 | 0x12345678 | 无 |
| 2 | 0x1234567c | 无 |
| 3 | 0x12345680 | 无 |
假设每个端口占4个字节---为了表述方便,没安插设备则无mac号.卸载设备后,删除对应内容.
2.发送数据给程序
这部分内容和操作系统的运行有关,笔者不知道,以下是笔者想的一个方案,内容不可实现,只谈思路.
=============================内容分割线↓===================================
全局变量设置(一和二)
一.
1.在键盘插上端口后,驱动文件可以获得端口地址.
2.操作系统每获得一次数据,即调用一次驱动程序handle().驱动程序有个全局变量char * keyboard_p;
//伪代码
void handle_kb(){
if(非激活程序)
keyboard_p=null; //如果是非当前激活程序,指针指向空,屏蔽数据
else
keyboard_p=端口地址; //预设端口地址可以被用在驱动程序中
}
//如果不成功,改
void change(char** k){
*k=端口地址; //将端口地址传给指针k(传入的是双重指针)
}
void handle_kb(){
if(非激活程序)
keyboard_p=null;
else
change(&keyboard_p);
}
二.
全局变量keyboard_p和handle_kb()函数由操作系统给出,当键盘插上端口后,mac信息给出这是一个键盘,操作系统扫描端口数据后调用全局变量keyboard_p和全局函数handle_kb().
一和二几乎相同,二选一.
三.进程端
现在进程端可以通过全局变量*keyboard_p获得键盘输入的内容.
用获得的键盘内容(数字)执行不同的函数.有三个部分需要编写,主程序main函数调用键盘数据处理程序handle_kb();键盘处理程序kb_handle()调用具体处理函数.
main函数部分
//伪代码
int main(void){
...
... //主程序其他内容
while(1){ //无限循环,程序一直运行
kb_handle(); //调用键盘处理函数
}
return;
}
键盘处理函数
//伪代码-键盘处理函数
void kb_handle(){
if(*keyboard==1){ //键盘字符a对应数字1,以下类同
printf("字母a\n");
push_a(); //输入字符a的程序
}
.... //其他输入字符处理程序
....
if(*keyboard==QUIT){ //取一个数字,表达Alt+F4,退出程序
push_Alt_F4(); //退出程序的代码
}
}
具体处理函数部分,如push_a(),push_Alt_F4()略
小结
笔者用全局变量的思路来解决硬件,操作系统和进程之间的通信问题.现实不是这样做的,但除了通信部分,其他内容是可行的.需要解决的内容:
1>操作系统识别硬件,怎么识别呢?端口给键盘的USB供电,通电后给出一些信息,如mac.
2>操作系统接收mac信息后,用几张表来记录数据,以此类推
|-----|------------|------------|------|---|-----|------------|------------|------|
| 端口号 | 端口地址 | 对应设备mac | 设备数据 | | 端口号 | 端口地址 | 对应设备mac | 设备数据 |
| 1 | 0x12345678 | 0xabcdefgh | 实时刷新 | | 1 | 0x12345688 | 0xabcdefgi | 实时刷新 |
| 2 | 0x12345679 | 无 | 无 | | 2 | 0x1234568a | 无 | 无 |
| 3 | 0x1234567a | 无 | 无 | | 3 | 0x1234568c | 无 | 无 |
| | | | | | | | | |
| 一个字节的端口(8位)表 |||| | 二个字节的端口(16位)表 ||||
3>操作系统判断"当前程序",每台机算机运行时只有唯一的"活动窗口".
4>全局变量的设定,可以由操作系统给出,也可以由设备驱动给出.
但不得不说这是个很大的漏洞,如果计算机有其他程序可以绕开屏蔽,则数据将泄露.所以这个方案只有一半可用.
其他
程序的实现是软硬件协作的结果,当现有的代码无法完成需求时,考虑向底层寻求支持.
操作系统应有硬件的解决方案,制定一套规范,并给出软件接口(例如外部用接口函数获得数据)
这里也引出一个分工问题,键盘设计人员向操作系统设计者索要接口.他们分属两个体系.
=============================内容分割线↑===================================
举例:用键盘打出字符(英文)
英文字符的组织
假设表示字符的像素图放在symbol.dat里,有一张索引表
|-----|------|----------|
| 序号 | 代表字符 | 占据空间(假设) |
| 1 | a | 28 |
| 2 | b | 24 |
| 3 | c | 16 |
| ... | ... | ... |
| 26 | z | 18 |
| | | |
| 英文字符文件索引表 |||
略去大写字母
数据端
把索引表+数据文件+查找算法视作数据端,数据端可以用像素图表达出任意一个字符(可参照笔者其他帖子).数据端对外接口是索引表中的序号,序号可以表达出对应的字符.因此如果外面的函数返回值是字符对应的序号,则可以表示出字符.
进程端处理函数
这里的处理函数对应上一小节的内容,如下
//对应函数代码
char push_a(){
return 1;
};
char push_b(){
return 2;
}
当进程端调用处理函数时,即可得到序号对应的数字,并查找到对应字符.
其他
索引表
索引表的形式不一定是数据库里的表,可用C/C++的数据类型表示,
因为不用计算偏移,所以可以直接这样表示:
//伪代码
char index[26]={28,24,16,....18};
字符写入光标位置
查找到字符,写到光标所在位置,关联到对象的设计方法,这里不展开了.
汉字字符
汉字字符的表达基本思路是一样的,但数据端设计会麻烦一点,可以参照笔者其他帖子.
小结
键盘的一点思考.键盘的实现思路可以用在其他输入设备(传感器,手写笔等)上