前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >操作系统I/O与显示器---16

操作系统I/O与显示器---16

作者头像
大忽悠爱学习
发布于 2022-08-23 02:48:58
发布于 2022-08-23 02:48:58
66800
代码可运行
举报
文章被收录于专栏:c++与qt学习c++与qt学习
运行总次数:0
代码可运行

操作系统I/O与显示器---16


继续那台“计算机”

在学完了进程和内存管理之后,下面进入到磁盘驱动和相关IO设备驱动的章节。


让外设工作起来

计算机如何让外设工作起来呢?

每个外设,例如: 显示器有对应的显卡,显卡里面有相关的寄存器,通过往这些寄存器中设置对应的值,就可以控制该外设工作起来了。

CPU通过向外部设备对应的控制器某个寄存器中写入命令,就可以操作对应外部设备的工作了,例如: 常用的out和in指令,来控制往某个外设寄存器中写入或者读取数据。

汇编语言中断及外部设备操作篇–06

而当外部设备处理完后,通过中断方式通知CPU进行处理。


向设备控制器的寄存器写不就可以了吗?

操作系统操作外设,首先需要查寄存器地址、还需要查找对应的硬件手册,来了解相关操作命令的格式和语义。

硬件设备种类繁杂,如果直接让用户面向这些外设控制器中的寄存器来进行操作,那么显然过于麻烦,因此操作系统要给用户提供一个简单 视图—文件视图,这样方便。

通过文件视图,就相当于提供了一个统一操作外设的接口,例如: 向显示器输出字符,都通过print这个接口即可,不需要管后面显示器的类型是什么。

总的来说,操作外设就是下面这三个步骤:

  • 提供统一操作某个外设的接口
  • 用户调用该接口,接口最终通过out指令,将操作命令发送到对应的外设寄存器中
  • 外设通过中断,通知操作系统任务处理完毕

一段操纵外设的程序

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//去打开对应的设备文件---通过统一的open接口
int fd = open(/dev/xxx”);
for (int i = 0; i < 10; i++) {
//向外设写数据----通过统一的write接口
write(fd,i,sizeof(int));
}
//关闭外设----通过统一的close接口
close(fd);

(1) 不论什么设备都是open, read, write, close

  • 操作系统为用户提供统一的接口

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

  • 根据设备文件找到控制器的地址、内容格式等等!

一个统一的视图-文件视图

操作系统提供给用户操作硬件的系统接口都是固定的,但是在操作具体硬件时,需要传入当前操作的文件描述符,然后操作系统根据文件描述符来决定当前操作的具体是哪个硬件,然后进行对应的处理。


概念有了,开始给显示器输出…

从哪里开始这个故事呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
printf(“Host Name: %s”, name);

printf库展开的部分我们已经知道:先创建缓存buf将格式化输出都写到那里, 然后再write(1,buf,…)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/fs/read_write.c中
//fd是找到file的索引--向屏幕输出时,传入的fd=1,此时1表示的是标准输出的文件描述符号
int sys_write(unsigned int fd, char *buf, int count){ 
struct file* file;
//current就是当前进程,进程是带动整个系统的视图
//通过传入的文件描述符,找到对应的文件
file = current->filp[fd];
//获得文件对应的节点--该节点内封装了当前文件的信息
inode = file->f_inode;

file的目的是得到inode,显示器信息应该就在这里


fd=1的filp从哪里来?

  • 因为是被current指向,所以是从fork中来
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int copy_process(...){
*p = *current;
for (i=0; i<NR_OPEN;i++)
if ((f=p->filp[i])) f->f_count++;

显然filp文件打开指针都是从父进程拷贝来的,那么是谁一开始打开的?

  • shell进程启动了whoami命令,shell是其父进程

shell 0号进程的初始化过程中对相关文件指针进行从初始化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void main(void)
{ if(!fork()){ init(); }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void init(void){
//系统初始化的时候打开了一个文件,并且拷贝了两份
//所以filp下标1对应的打开文件指针也是dev/tty0,即终端设备文件
open(“dev/tty0”,O_RDWR,0);dup(0);dup(0);
execve("/bin/sh",argv,envp)}

open系统调用完成了什么?

open系统调用,最终会调用到sys_open。

sys_open的作用如下:

  • 解析目录,找到inode
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/fs/open.c中
int sys_open(const char* filename, int flag,int mode){ 
struct file* f;
int i,fd;
//调用函数open namei执行打开操作,若返回值小于0,则说明出错,于是释放
//刚申请到的文件结构,返回出错码i。若文件打开操作成功,则inode是已打开文件的i节点指针。
i=open_namei(filename,flag,&inode);
cuurent->filp[fd]=f; //第一个空闲的fd
f->f_mode=inode->i_mode; f->f_inode=inode; f->f_count=1; 
return fd;
}
  • 核心就是建立这样一个链

open(“dev/tty0”,O_RDWR,0)最终目的就是将dev/tty0设备文件对应的inode读入到内存中来,当需要操作该文件设备时,首先就需要获得当前设备对应的inode信息。


准备好了,真正向屏幕输出!

  • 继续sys_write!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/fs/read_write.c中
int sys_write(unsigned int fd, char *buf,int cnt){ 
//拿到当前要操作文件的inode后
inode = file->f_inode;
//判断对应的文件设备是否是字符设备---通过i_mode进行区分
///dev/tty0的inode中的 信息是字符设备
if(S_ISCHR(inode->i_mode))
//往该设备上写入字符,第二个参数是设备号,以及对应要写入的数据位于内存中那个buf缓冲区中
return rw_char(WRITE,inode->i_zone[0], buf, 
cnt); ...
  • 转到rw_char!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/fs/char_dev.c中
int rw_char(int rw, int dev, char *buf, int cnt){ 
//根据设备号查询对应的函数指针表---得到对应的函数指针
crw_ptr call_addr=crw_table[MAJOR(dev)]; 
//通过函数指针调用具体找到的函数
call_addr(rw, dev, buf, cnt); ...}

看看crw_table!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//设备号为4,去查询对应的函数表下标为4的地址,得到rw_ttyx函数指针
static crw_ptr crw_table[]={...,rw_ttyx,};
typedef (*crw_ptr)(int rw, unsigned minor, char *buf, int count)

可以猜测到rw_ttyx函数就是用来对终端设备进行具体读写操作的函数了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int rw_ttyx(int rw, unsigned minor, char *buf, int count){ 
//判断是读还是写
return ((rw==READ)? tty_read(minor,buf):tty_write(minor,buf));}

再转到tty_write! //实现输出的核心函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/kernel/tty_io.c中
int tty_write(unsigned channel,char *buf,int nr){ 
//tty_struct可以猜测到就是对设备抽象出来的一个结构体对象
struct tty_struct *tty;
//tty_table可以猜测到是设备列表,通过当前索引,查到当前设备对应的tty对象
tty=channel+tty_table;
//可以猜测:输出就是 放入队列!
sleep_if_full(&tty->write_q);
...
}

由于内存的速度和其他外部设备,例如: 显示器相比,差距很大,所以。

一般在内存与外设之间进行数据传输时,都需要一个共享缓冲区域,即我们需要先将数据写入到内存的一块缓冲区内,然后外设慢慢去读取数据。

这里是CPU将数据写入到当前设备关联的输出队列中去,然后外设去读取输出队列中的数据,因此CPU将数据写入到这个输出队列前,需要先判断该队列是否已经满了,如果队列已经塞满了,就阻塞等待。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sleep_if_full(&tty->write_q);

sleep_if_full判断当前写入队列是否已经满了,如果已经满了,当前进程就需要进入阻塞等待。

这边涉及到共享内存,因此就需要之前讲到的信号量对共享内存进行保护


继续tty_write这一核心函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/kernel/tty_io.c中
int tty_write(unsigned channel, char *buf, int nr){ 
...
char c, *b=buf; 
while(nr>0&&!FULL(tty->write_q)) { 
 //从用户共享缓冲区中读取数据
 //fs:从用户缓存区读!
  c = get_fs_byte(b);
  if(c==‘\r’){PUTCH(13,tty->write_q);continue;}
  if(O_LCUC(tty)) c = toupper(c);
   b++; 
   nr--; 
   //将从用户缓冲区读取出来的数据放入输出队列中去
   PUTCH(c,tty->write_q);
  } //输出完事或写队列满!
tty->write(tty);
}

tty->write应该就是真的开始输出屏幕了!


看看tty->write

先来看看tty_struct结构体的模样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在include/linux/tty.h中
struct tty_struct{ void (*write)(struct tty_struct 
*tty); struct tty_queue read_q, write_q; }

所有设备都会被抽象为一个tty对象,并且该tty对象内部给出的接口也都是固定的,常见的write和read接口等,不同的设备对应不同的tty对象,他们需要给出不同的接口具体方法实现,然后将这些tty对象加入一个tty_table表中。

  • 需要看tty_struct结构的初始化!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct tty_struct tty_table[] = {
{con_write,{0,0,0,0,””},{0,0,0,0,””}},{},};

如果我们需要操作屏幕输出,那么首先需要查表得到屏幕输出对应的tty对象,这里是con_write.

外部设备也分为输入和输出设备,这里屏幕属于输出设备,而常见的键盘和鼠标等,属于输入设备,下一节会讲

  • 到了con_write,真正写显示器!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在linux/kernel/chr_drv/console.c中
void con_write(struct tty_struct *tty){ 
//从队列中取出字符,然后赋值给C
GETCH(tty->write_q,c);
//按照对应的输出格式,将字符输出到屏幕上
if(c>31&&c<127){__asm__(“movb _attr,%%ah\n\t”
“movw %%ax,%1\n\t”::”a”(c),
”m”(*(short*)pos):”ax”); 
//指向显存的位置往后面移动两位,用来显示下一个字符
pos+=2;
}

有的外设控制器地址可以和内存统一编址,这时寻址就使用mov,而如果采用独立编址,寻址时则采用out和in

显存特别大,因此通常都是和内存统一编址,因此访问显存时,使用mov指令。


只有一句话:mov pos

在系统启动的时候,会根据BIOS中断,取出硬件参数,其中包括光标的位置,然后将光标的位置放置在90000处。

光标的位置就是显存的位置。


pos的修改


printf的整个过程!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
线上服务器老是卡,该如何优化?
我们开发的软件服务需要在服务器上运行,所以服务器性能代表了软件的性能上限,因此服务器性能调优是个十分重要的环节,然而大部分同学对服务器性能调优关注的较少,今天从3个部分对服务器性能调优进行介绍,分别是:服务器配置选择,服务器负载分析,服务器内核参数调优。
叶秋学长
2022/08/03
2.8K0
线上服务器老是卡,该如何优化?
服务器常见问题汇总(常见故障及相应的解决方法入口)
服务器支撑着整个企业的信息数据,对公司的信息储存、业务开展、正常运作等等环节都具有着至关重要的意义。然而,服务器在日常运行过程中,由于其复杂的硬件结构、繁琐的运行原理,经常会出现一些大大小小的问题困扰着各位。下面精心整理一些服务器的常见问题汇总,帮助各位排忧解难。 1.系统蓝屏、频繁死机、重启、反映速度迟钝
德迅云安全-李康强
2023/06/23
1.2K0
Linux应用性能分析及故障排查
CPU使用率:CPU的使用率 平均负载:单位时间内的活跃线程数 用户时间:CPU在用户进程上的实际百分比 系统时间:CPU在内核上花费的实际百分比 空闲时间:系统处于在等待IO操作上的时间总和 等待:CPU花费在等待IO操作上的时间总和 Nice时间:CPU优先执行的时间百分比
lyb-geek
2021/12/04
1.3K0
Linux应用性能分析及故障排查
服务器里没有网络是什么原因
服务器无法连接到网络并且频繁断网可能有多种原因。德迅云安全分享一些常见的原因及解决方法:
德迅杨德俊
2024/05/10
6170
服务器中毒了——菜是原罪
周五朋友生日,刚吃完饭准备唱歌,接到消息说业务支付失败,问题是银行前置机无法正常和银行建立连接。
没有故事的陈师傅
2022/09/15
9490
服务器中毒了——菜是原罪
linux服务器性能问题相关排查手册(总结向)
定义:平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,和 CPU 使用率并没有直接关系。换言之,要计算 CPU 负载的值,只考虑正在运行或等待分配 CPU 时间的进程。不考虑正常的休眠过程(休眠状态),僵尸或停止的过程。
scarlett学习手册
2019/12/13
2.3K0
关于Windows安全性
曾经有个朋友,几百台windows虽然都是内网,但他跳板机有公网,黑客先攻陷了跳板机然后几百台内网机器全被搞
Windows技术交流
2022/11/30
1.6K0
服务器数据丢包是什么原因?
相信大家在服务器租用的使用过程中,往往会遇到数据丢包的情况,造成网站的延时或者访问打不开的情况,给用户造成了很大一些困恼,那么服务器数据丢包是什么原因造成的呢?
德迅云安全-小娜
2022/04/20
2.2K0
网站服务器配置-应该选择多大带宽-同时能承载多少用户的访问
http://mpvideo.qpic.cn/0bc3viafaaaaziahw3c7l5rvbkwdkcvaauaa.f10002.mp4?dis_k=e54fb0f33e4a99b17e5545d
itclanCoder
2022/12/07
4.1K0
构建企业级监控平台系列(二):如何做好企业监控系统运维管理?
前面介绍了企业级监控概述及发展等相关的知识点,今天我将详细的为大家介绍 如何做好企业监控系统运维相关知识,希望大家能够从中收获多多!如有帮助,请点在看、转发朋友圈支持一波!!!
民工哥
2023/09/27
1K0
构建企业级监控平台系列(二):如何做好企业监控系统运维管理?
云服务器被利用lsass.exe、tcpsvcs.exe消耗带宽的解决办法
通过资源监视器 → 网络活动 → 看到都是lsass.exe,且pid相同,通过过滤相同的pid 508发现端口是xxx,在安全组或防火墙禁用xxx端口后带宽消耗就正常了。不排除lsass.exe是伪装的病毒木马,遇到了建议安装杀毒防护软件全盘杀毒。
Windows技术交流
2022/11/22
3.5K0
通过Nethogs查看服务器网卡流量情况
在日常运维工作中,会碰到服务器带宽飙升致使网站异常情况。作为运维人员,我们要能非常清楚地了解到服务器网卡的流量情况,观察到网卡的流量是由哪些程序在占用着。 今天介绍一款linux下查看服务器网卡流量占用情况的工具:Nethogs,来自github上的开源工具。 它不依赖内核中的模块。当我们的服务器网络异常时,可以通过运行nethogs程序来检测是那个程序占用了大量带宽。节省了查找时间。 Nethogs安装: 方法一:在epel源中可以直接yum安装 [root@dev src]# yum install -
洗尽了浮华
2018/01/23
4.1K0
通过Nethogs查看服务器网卡流量情况
黑客口中的肉鸡是什么?
今天我们来聊一聊计算机领域中的肉鸡这么个玩意。首先肉鸡是什么呢?在计算机领域中,肉鸡又被称为傀儡机,他是指被黑客成功入侵并受操控的主机。肉鸡是黑帽子黑客的致富来源,经常被拿来像白菜一样售卖,价格几分钱到几十块钱一个不等。接下来我们主要看一下肉鸡有什么危害,黑客拿肉鸡有什么用呢?
极安御信安全研究院
2023/03/15
1.3K0
黑客口中的肉鸡是什么?
挖矿木马详解
攻击者通过各种手段将挖矿程序植入受害者的计算机中,在受害者不知情的情况下利用其计算机的云算力进行挖矿,从而获取利益,这类非法植入用户计算机的挖矿程序就是挖矿木马。
网e渗透安全部
2019/12/02
12.6K0
Jtti:服务器CPU过高的原因及解决方案
服务器CPU过高不仅会导致响应速度变慢,还可能影响应用程序的稳定性和用户体验。了解导致CPU过高的原因,可以帮助管理员更有效地管理和优化服务器性能。本文将详细探讨服务器CPU过高的常见原因及其解决方案。
jtti
2024/10/10
2080
服务器负载率过高怎么解决?
晚上我登陆网站时发现后台输入账号密码后一直现在在登陆中,我以为是账号密码不对,重新输入后还是同样的问题,网站可以正常的浏览,可后台就是无法登陆,一直显示登陆中,我以为是插件问题造成的,登陆服务器进行查看发现网站负载率一直是在80-100%之间,网站卡的很,至此问题找出来了,具体什么是负载率,咱接着往下看。
白黎
2023/03/16
3.7K0
服务器负载率过高怎么解决?
购买腾讯云服务器后,外网访问丢包,如何解决?
我们在购买腾讯云服务器云服务器CVM_云主机_云计算服务器_弹性云服务器- 腾讯云 (tencent.com)的时候,对于网络方面,一就是考虑带宽,二就是考虑服务器所在的地理位置与大部分用户访问云服务器所在的位置;那么当我们的用户或者是自己在访问云服务器的时候,进行ping发现有丢包,那就可以从上面2大点去入手排查,先将最容易的、能快速规避解决的因素都进行排除解决。
子沐u
2021/09/10
16.5K0
购买腾讯云服务器后,外网访问丢包,如何解决?
一次很意外的网站故障经历。
大家好,我是鱼皮。这个周末不太愉快,因为今天刚起床迷迷糊糊地,我就收到了用户反馈说 “鱼皮,你的网站又打不开了!一直在加载!”
程序员鱼皮
2023/09/06
3100
一次很意外的网站故障经历。
从原理到实践:万字详解 Kubernetes 核心组件与指标监控
Kubernetes 可以说是容器编排领域的事实标准。不管你的业务是运行在公有云、私有云,还是混合云上,Kubernetes 都能给你一种“统一天下”的感觉。它不仅能帮你把容器化应用管理得井井有条,还能让你的系统在扩展性、弹性、高可用性上更上一层楼。Kubernetes 就是云原生时代的“基础设施底座”。
腾讯云可观测平台
2025/03/19
2490
从原理到实践:万字详解 Kubernetes 核心组件与指标监控
服务质量保障之性能监控
伴随着突发流量、系统变更或代码腐化等因素,性能退化随时会发生。如在周年庆大促期间由于访问量暴涨导致请求超时无法下单;应用发布变更后,页面频繁卡顿导致客诉上升;线上系统运行一段时间后,突然发生OOM或连接打满拒绝访问。
测试开发技术
2024/03/11
2880
服务质量保障之性能监控
相关推荐
线上服务器老是卡,该如何优化?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验