操作系统 4.1-I/O与显示器
外设工作起来
操作系统让外设工作的基本原理和过程,具体来说,它概括了以下几个关键步骤:
-
发出指令:操作系统通过向控制器中的寄存器发送指令来启动外设的工作。这些指令通常是通过I/O指令(如
out
指令)来实现的。 -
控制器处理:控制器接收到指令后,根据寄存器中的内容来操控硬件。控制器内部可能包含有计算电路,能够根据CPU发出的指令来具体操作设备。
-
中断处理:一旦外设完成其任务,它会向CPU发送一个中断信号。CPU接收到中断后,会暂停当前的工作,转而处理中断,这可能涉及到数据传输等操作。
-
统一的文件接口:为了让外设的使用变得简单,操作系统提供了一种统一的视图,即文件视图。这意味着,无论操作哪种外设,用户都可以通过统一的文件操作接口(如
open
、read
、write
等)来进行。
总结来说,操作系统让外设工作的核心原理非常简单,即通过发出指令让外设工作,然后编写中断处理程序来响应外设完成任务后的中断信号。此外,操作系统通过提供统一的文件接口,使得用户可以方便地使用各种外设,而无需关心底层的硬件细节。接下来我们将围绕这三个方面讲解。
外设工作的开始
提取的代码如下:
int fd = open("/dev/xxx");
for (int i = 0; i < 10; i++) {write(fd, i, sizeof(int));
}
close(fd);
外设工作的开始可以总结为以下几个步骤:
-
打开设备:
-
使用
open
函数打开指定的设备文件(/dev/xxx
),这个文件是系统中外设的接口。 -
open
函数返回一个文件描述符fd
,用于后续对该设备的操作。
-
-
数据传输:
-
通过
write
函数将数据传输到外设。在这个例子中,数据是一个整数i
,大小为sizeof(int)
。 -
这个过程在一个循环中进行,循环10次,每次写入一个整数。
-
-
关闭设备:
-
完成数据传输后,使用
close
函数关闭设备文件,释放文件描述符fd
所占用的资源。
-
文件视图概念
文件视图是操作系统提供的两大视图之一,它将所有的I/O设备统一抽象为文件,使得用户可以通过一组标准的文件操作接口(如open
、read
、write
、close
)来访问和操作这些设备。这种抽象极大地简化了用户与硬件设备的交互,并隐藏了底层硬件的具体细节。
在文件视图中,操作系统将设备属性数据和设备驱动程序结合在一起,通过系统调用接口与用户空间进行交互。当用户程序调用这些系统调用时,操作系统会进行解释,并将其转换为对特定设备的命令。这些命令随后被发送到相应的设备控制器(如键盘控制器或磁盘控制器),并由控制器执行具体的硬件操作。
文件视图的样貌可以总结如下:
-
统一接口:无论什么设备,用户都通过统一的系统调用接口(
open
、read
、write
、close
)来进行操作。 -
设备抽象:不同的设备对应不同的设备文件(如
/dev/xxx
),操作系统根据这些设备文件找到控制器的地址、内容格式等信息。 -
设备驱动:设备驱动程序是操作系统与硬件设备之间的桥梁,它负责将系统调用转换为对特定硬件的操作。
-
中断处理:当设备完成操作后,会通过中断机制通知操作系统,操作系统再进行相应的中断处理。
-
I/O系统:操作系统中的I/O系统负责管理设备属性数据和设备驱动程序,协调用户程序与硬件设备之间的交互。
通过这种文件视图,操作系统为用户提供了一个简单、统一的方式来操纵外设,同时隐藏了底层硬件操作的复杂性。这种抽象不仅简化了用户程序的开发,还提高了系统的可移植性和可扩展性。
代码思路讲解
提取的代码如下:
int sys_write(unsigned int fd, char *buf, int count) {struct file* file;file = current->filp[fd]; // fd是找到file的索引inode = file->f_inode; // file的目的是得到inode
}
总结显示器输出的过程:
-
系统调用:
-
用户程序通过
printf
函数输出信息,printf
函数内部会先创建一个缓存区(buf
),将格式化后的输出写入该缓存区。
-
-
写入系统调用:
-
printf
函数最终会调用write
系统调用,将缓存区中的数据写入指定的文件描述符(fd
)。
-
-
文件描述符索引:
-
在Linux内核中,
sys_write
函数通过文件描述符(fd
)找到对应的文件结构体(file
)。文件描述符是用户空间和内核空间之间的索引。
-
-
获取inode:
-
从文件结构体中获取inode结构体,inode包含了文件的元数据和设备信息。对于设备文件(如显示器),inode中包含了设备驱动的相关信息。
-
wirte->filp
提取的代码如下:
int copy_process(...){*p = *current;for (i = 0; i < NR_OPEN; i++)if ((f = p->filp[i])) f->f_count++;
}
void main(void) {if (!fork()) { init(); }
}
void init(void) {open("/dev/tty0", O_RDWR, 0);dup(0);dup(0);execve("/bin/sh", argv, envp);
}
fd=1
的filp
从哪里来?
在UNIX和Linux系统中,当一个进程创建一个新的子进程时(通常是通过fork
系统调用),子进程会继承父进程的文件描述符。这意味着子进程会拥有与父进程相同的文件描述符集合,包括指向相同文件结构(struct file
)的指针。
在提供的代码中,copy_process
函数负责复制父进程的文件描述符信息到子进程。这是通过遍历父进程的filp
数组(每个元素都是指向struct file
的指针)并递增相应文件结构的引用计数来实现的。这样做确保了文件在两个进程间正确共享。
在main
函数中,通过调用fork
创建了一个新的子进程。如果fork
返回0(表示这是子进程),则调用init
函数。在init
函数中,首先打开/dev/tty0
(通常是控制台设备),然后使用dup(0)
将标准输入、输出和错误都重定向到这个控制台设备。最后,通过execve
调用替换当前进程映像为/bin/sh
(shell),从而启动一个新的shell进程。
因此,fd=1
(通常用于标准输出)的filp
指针是从父进程继承来的,并且在子进程中通过dup(0)
调用被重定向到/dev/tty0
设备。这样,当shell进程写入标准输出时,数据会被发送到控制台设备。
filp->open
提取的代码如下:
int sys_open(const char* filename, int flag) {i = open_namei(filename, flag, &inode);current->filp[fd] = f; // 第一个空闲的fdf->f_mode = inode->i_mode;f->f_inode = inode;f->f_count = 1;return fd;
}
open
系统调用完成了什么?
open
系统调用主要完成了以下步骤:
-
解析目录,找到inode:系统需要解析传入的文件名,找到对应的inode结构,inode包含了文件的元数据和设备信息。
-
分配文件描述符(fd):在进程的文件描述符数组(
filp
)中找到一个空闲的文件描述符,并将其分配给这个文件。 -
建立文件结构体(file):创建一个文件结构体(
file
),该结构体包含了文件的状态信息,如文件模式(f_mode
)和指向inode的指针(f_inode
)。
开始输出
提取的代码如下:
// sys_write function in 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);...
}
// rw_char function in 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);...
}
-
系统调用:用户程序通过
write
系统调用向内核请求写操作,传递文件描述符(fd
)、缓冲区地址(buf
)和要写入的字节数(cnt
)。 -
字符设备检查:在
sys_write
函数中,首先获取文件结构体的inode,并检查该inode表示的是否为字符设备(通过S_ISCHR(inode->i_mode)
判断)。 -
调用设备驱动:如果是字符设备,调用
rw_char
函数,传入写操作标志(WRITE
)、inode中的设备信息(i_zone[0]
)、缓冲区地址和字节数。 -
设备驱动操作:在
rw_char
函数中,根据设备的主要号码(MAJOR(dev)
)从字符设备驱动表(crw_table
)中获取对应的操作函数指针,并调用该函数执行实际的写操作。 -
输出到屏幕:对于显示器这样的字符设备,
rw_char
函数最终会调用显示器的驱动函数,将缓冲区中的数据写入显示器的显存,实现向屏幕的输出。
这个过程展示了从用户空间的 printf
调用开始,经过系统调用接口,到内核空间的文件操作,再到设备驱动程序,最终实现数据向硬件设备的输出。这是操作系统中I/O系统工作的一个典型流程。
rw_char->crw_table
提取的代码如下:
// 定义字符设备操作函数指针数组
static crw_ptr crw_table[] = {..., rw_ttyx, ...};
// 函数指针类型定义
typedef (*crw_ptr)(int rw, unsigned minor, char *buf, int count);
// 字符设备读写函数
static int rw_ttyx(int rw, unsigned minor, char *buf, int count) {return ((rw == READ) ? tty_read(minor, buf) : tty_write(minor, buf));
}
// 真正的写函数
int tty_write(unsigned channel, char *buf, int nr) {struct tty_struct *tty;tty = channel + tty_table;sleep_if_full(&tty->write_q);...
}
总结代码所做的事情及用途:
-
定义字符设备操作函数指针数组(
crw_table
):-
crw_table
是一个数组,包含了指向不同字符设备操作函数的指针。这些函数负责对字符设备进行读写操作。
-
-
函数指针类型定义(
crw_ptr
):-
crw_ptr
是一个函数指针类型,用于指向符合特定签名的函数,即接受读写标志、次要设备号、缓冲区指针和计数作为参数的函数。
-
-
字符设备读写函数(
rw_ttyx
):-
rw_ttyx
函数根据传入的读写标志(rw
),决定调用tty_read
还是tty_write
函数。这个函数作为字符设备的通用入口点,根据操作类型分发到具体的读写处理函数。
-
-
真正的写函数(
tty_write
):-
tty_write
是实现字符设备(如终端)写操作的核心函数。它负责将数据从内核缓冲区写入到设备。 -
函数首先通过
channel
和tty_table
获取到tty_struct
结构体,该结构体包含了终端设备的相关信息和状态。 -
然后检查输出队列(
write_q
)是否已满,如果已满,则调用sleep_if_full
函数使进程休眠,等待队列有空间。 -
一旦队列有空间,数据就被写入队列,后续操作(可能是中断处理程序)会负责将队列中的数据实际输出到设备。
-
crw_table->tty_write
提取的代码如下:
// 在 linux/kernel/tty_io.c 中的 tty_write 函数
int tty_write(unsigned channel, char *buf, int nr) {char c, *b = buf;while (nr > 0 && !FULL(tty->write_q)) {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);
}
总结代码所做的事情及用途:
-
初始化:
-
定义字符变量
c
和字符指针b
指向缓冲区buf
的起始位置。
-
-
循环处理每个字符:
-
使用
while
循环,当还有字符要写入(nr > 0
)且写队列未满(!FULL(tty->write_q)
)时,继续处理。 -
从用户空间的缓冲区中读取一个字符到
c
。
-
-
处理回车字符:
-
如果字符是回车符(
'\r'
),将其转换为换行符('\n'
)并继续下一个循环。
-
-
字符大小写转换:
-
如果终端设置为转换为大写(
O_LCUC(tty)
),将字符c
转换为大写。
-
-
写入队列:
-
将处理后的字符放入终端的写队列
tty->write_q
中。 -
更新缓冲区指针
b
和字符计数nr
。
-
-
触发实际写操作:
-
一旦所有字符都已处理或写队列满,调用
tty->write(tty)
触发实际的写操作,将队列中的数据输出到屏幕上。
-
-
提取的代码如下:
-
// 在 include/linux/tty.h 中定义的 tty_struct 结构体 struct tty_struct {void (*write)(struct tty_struct *tty);struct tty_queue read_q, write_q; }; // tty_struct 结构体数组的初始化 struct tty_struct tty_table[] = {{con_write, {0,0,0,0,""}, {0,0,0,0,""}},{}, ... }; // con_write 函数在 linux/kernel/chr_drv/console.c 中的定义 void con_write(struct tty_struct *tty) {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;} }
-
con_write
函数是 Linux 内核中负责将字符输出到控制台显示器的关键函数。它通过直接操作显存来实现字符的显示,这是 Linux 内核中实现控制台输出的底层机制。 -
通过这种方式,内核可以将用户程序的输出(如通过
printf
函数)转换为屏幕上的可见字符,实现用户与系统的交互。
-
-
总结代码所做的事情及用途:
con_write
函数定义:-
如果字符
c
在可打印范围内(ASCII码 32 到 126),则通过内联汇编代码将其写入显存(视频内存)的特定位置。 -
函数从
tty->write_q
队列中获取一个字符c
。 -
con_write
函数是tty_struct
结构体中的write
函数指针所指向的实际函数,负责将字符写入显示器。
-
tty_write->mov pos
-
这两张图片提供了关于如何在Linux内核中实现向屏幕输出字符的详细信息。以下是提取的代码和总结:
提取的代码:
-
// 在 include/linux/tty.h 中定义的 tty_struct 结构体 struct tty_struct {void (*write)(struct tty_struct *tty);struct tty_queue read_q, write_q; }; // tty_struct 结构体数组的初始化 struct tty_struct tty_table[] = {{con_write, {0,0,0,0,""}, {0,0,0,0,""}}, {}, ... }; // con_write 函数在 linux/kernel/chr_drv/console.c 中的定义 void con_write(struct tty_struct *tty) {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;} }
总结代码的作用:
用途:
-
con_write
函数是 Linux 内核中负责将字符输出到控制台显示器的关键函数。它通过直接操作显存来实现字符的显示,这是 Linux 内核中实现控制台输出的底层机制。 -
通过这种方式,操作系统能够统一管理不同程序的输出,提供一致的接口给用户程序,同时隐藏了硬件操作的复杂性。
-
这种机制是操作系统中设备驱动程序的一部分,它展示了如何通过编程接口与硬件设备进行交互,是学习操作系统工作原理和设备驱动开发的重要内容。
关于
mov pos
的解释:-
mov pos, c
是完成显示中最核心的秘密,它将字符c
的值移动到pos
指向的显存位置,从而在屏幕上显示字符。 -
pos
指向显存的起始地址(例如0xA0000
),每次写入一个字符后,pos
的值会增加,以指向下一个字符的位置。 -
这种直接操作显存的方法是早期计算机系统中常见的屏幕输出方式,它允许操作系统直接控制屏幕上的每个像素点。
关于
pos += 2
的解释:-
在彩色图形适配器(CGA)中,屏幕上的一个字符在显存中除了字符本身还应该有字符的属性(如颜色等)。因此,每个字符及其属性占用两个字节。
-
pos += 2
表示在写入一个字符后,pos
的值增加2,以指向下一个字符及其属性的起始位置。 -
这种机制确保了字符及其属性能够正确地存储在显存中,从而在屏幕上正确显示。
-
总结
printf
的整个过程涉及多个步骤和组件,具体如下:
-
库函数(printf):
-
用户程序调用标准库中的
printf
函数来输出格式化的文本。
-
-
系统调用(write):
-
printf
函数处理完格式化字符串后,通过系统调用write
将数据写入文件描述符指向的设备。
-
-
字符设备接口(crw_table[]):
-
系统调用
write
通过字符设备接口数组crw_table[]
找到对应的设备处理函数。
-
-
tty设备写(tty_write):
-
对于终端设备,
tty_write
函数负责将数据写入write_q
队列。
-
-
write_q队列:
-
write_q
队列用于暂存要写入设备的数据,直到设备准备好接收数据。
-
-
显示器写(con_write):
-
con_write
函数负责将write_q
队列中的数据实际写入显存。
-
相关文章:
操作系统 4.1-I/O与显示器
外设工作起来 操作系统让外设工作的基本原理和过程,具体来说,它概括了以下几个关键步骤: 发出指令:操作系统通过向控制器中的寄存器发送指令来启动外设的工作。这些指令通常是通过I/O指令(如out指令)来实现…...
Qt 5.14.2 入门(三)基本知识理解
目录 一、新建项目后生成的三个文件的作用和内容1、.h文件:头文件(如mainwindow.h)2、.cpp文件:源文件(如mainwindow.cpp)3、main.cpp:主程序 二、信号和槽机制1、信号(Signal)2、槽(Slot)3、连…...
Java中常见的设计模式
Java中常见的设计模式 Java 中有 23 种经典设计模式,通常被分为三大类:创建型、结构型和行为型。每个设计模式都解决了不同类型的设计问题。以下是几种常见设计模式的总结,并附带了实际应用场景、示例代码和详细的注释说明。 一、创建型设计…...
nltk 是怎么和 Transformers(比如 BERT 模型)联动来处理文本的
🧠 一句话解释nltk: nltk 是一个用于自然语言处理(NLP)的 Python 库,功能包括分词、词性标注、命名实体识别、文本分类等。 全称是:Natural Language Toolkit(自然语言工具包) &…...
项目日志配置模板示例
1.新增application.properties配置 logging.configclasspath:logback-spring.xml spring.profiles.activedev 将项目部署到服务器时需要将dev修改为test后再进行打包部署 2.新增logback-spring.xml <?xml version"1.0" encoding"UTF-8"?> <…...
sql server统计小时数据中每日最大风速及风速对应的风向
客户提出一个需求,有历年逐小时的风速、风向资料,想获取历年最大的风速及风速对应的风向值,通过sql 怎么实现,这个统计,我认为主要考虑一个问题,就是数据重复性,所以我采用以下方法实现…...
【SQL】MySql常见的性能优化方式
MySQL性能优化的常用方式及对比说明 一、引言二、MySQL性能优化的常用方式及对比说明1. 索引优化1.1 合理创建索引1.2 覆盖索引1.3 索引下推 2. SQL语法优化2.1 避免低效操作2.2 分页优化2.3 JOIN优化 3. 配置与架构优化3.1 参数调优3.2 读写分离与分库…...
Prometheus实现负载均衡并将多个实例数据汇总到一个主Prometheus
一、Prometheus实现负载均衡策略原理 要实现 Prometheus 的负载均衡并将多个 Prometheus 实例的数据汇总到一个主 Prometheus 实例中,可以结合 Prometheus 联邦(Federation) 和 负载均衡器 来进行配置。 这种方法的核心是在主 Prometheus 实例…...
力扣 — — 最长公共子序列
力扣 — — 最长公共子序列 最长公共子序列 题源:1143. 最长公共子序列 - 力扣(LeetCode) 题目: 分析: 一道经典的题目:最长公共子序列(LCS) 题目大意:求两个字符串的最长公共序列。 算法&…...
通过AWS EKS 生成并部署容器化应用
今天给大家分享一个实战例子,如何在EKS上创建容器化应用并通过ALB来发布。先介绍一下几个基本概念: IAM, OpenID Connect (OIDC) 2014 年,AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的联合身份支持。此功能允许…...
GNSS静态数据处理
1 安装数据处理软件:仪器之星(InStar )和 Trimble Business Center 做完控制点静态后,我们需要下载GNSS数据,对静态数据进行处理。在处理之前需要将相关软件在自己电脑上安装好: 仪器之星(InS…...
NVIDIA H100 vs A100:新一代GPU架构性能对比分析
一、核心架构演进对比 Ampere架构(A100)采用台积电7nm工艺,集成540亿晶体管,配备6,912个CUDA核心和432个第三代Tensor Core,支持FP16、TF32和INT8精度计算。其显存子系统采用HBM2e技术,80GB版本带宽可…...
AI图像生成
要通过代码实现AI图像生成,可以使用深度学习框架如TensorFlow、PyTorch或GANs等技术。下面是一个简单的示例代码,演示如何使用GANs生成手写数字图像: import torch import torchvision import torchvision.transforms as transforms import …...
计算机考研一战上岸宁波大学经验分享
目录 话不多说先上分数 个人介绍 政治 英语二 数学二 408 复试 话不多说先上分数 初试排名15/65 复试79.81分,复试排名13/65 总成绩13/65,研招网招考50人 (均为公开数据,非泄露复试信息) 个人介绍 本人山东…...
泛微ECOLOGY9 记 数据展现集成 自定义开窗测试中对SQL 的IN语法转换存在BUG
背景 搭建流程时,需将明细表1中的合同字段 供明细表2中的合同开窗查询使用。 最终实现如下图: 选择 发票号时,自动带出明细表1中的采购合同号清单,然后在明细表2中开窗采购合同号时,只跳出明细表1中有的采购合同号&am…...
【Nginx】Nginx代理Tomcat配置及404问题解决
当Tomcat返回HTTP 404未找到错误时,可以通过以下两种方式设置跳转到指定地址: ① 在Tomcat应用内部配置错误页面跳转(直接修改Tomcat的Web应用配置) ② 在Nginx反向代理层拦截404错误并跳转(无需修改Tomcat,…...
【Vue】案例——To do list:
【Vue】案例——To do list: 一、案例介绍:二、效果展示(如图)三、主要功能:四、技术要点:补充:【Vue】Vue模板语法(点击可跳转)补充:【Vue】数据绑定(单双向)…...
JVM不同环境不同参数配置文件覆盖
背景 需要在启动Java服务并且参数不同的场景,例如端口号在yml中的配置是这样的: server:port: 9100 覆盖配置对应JVM参数: java -jar xxxx.jar -Dserver.port12306 [JVM其他参数] 这样12306就会覆盖掉9100端口的配置作为启动配置 IDE…...
游戏引擎学习第215天
总结并为今天做铺垫 今天的工作内容是解决调试系统中的一个小问题。昨天我们已经完成了大部分的调试系统工作,但还有一个小部分没有完全处理,那就是关于如何层次化组织数据的问题。我们遇到的一个问题是,演示代码中仍有一个尚未解决的部分&a…...
C语言--求n以内的素数(质数)
求n以内的素数,可以用试除法或者埃拉托斯特尼筛法(埃氏筛法) 输入:数字n 输出:n以内所有的素数 不管是哪个方法,都有一个数学结论可以减少循环次数: 如果有一个数不是质数,那么它至…...
多版本go冲突问题
今天执行go build时遇到一个报错: compile: version “go1.22.7 (Red Hat 1.22.7-1.moduleel8.10.0700fd5cfc7a)” does not match go tool version “go1.23.4” 结果一查 which -a go 发现 当前系统居然有四个不同的位置都安装了go 于是先看go version…...
Windows 10系统出现无法使用键鼠的问题
有一位系统之家的小伙伴在升级Windows 10系统电脑的时候,出现键盘鼠标不能正常工作打问题,其实,遇到这个问题,有时只需重新启动计算机或断开连接并重新连接鼠标或键盘可以提供帮助,如果没有,可以看看下面系…...
NFC 数据传输
前言 初次接触NFC协议,很多人都会感觉困惑,搜索相关资料,大多数都介绍协议的理论及应用领域,数据传输的内容却很少,但对与开发人员来说,除了理论知识外,数据传输也是非常重要环节,尤…...
基于Streamlit的智能创业计划生成器开发实践
一、应用概述 在数字经济时代,创业者亟需高效工具进行系统化的商业规划。本文介绍的智能创业计划生成器基于Streamlit框架构建,整合了财务建模、时间轴规划、智能文档生成等核心功能。该工具通过模块化设计实现了九大创业要素的系统化配置,显…...
蓝桥杯单片机刷题——按键控制距离显示精度
设计要求 驱动超声波传感器,启动距离测量功能,并将其结果显示到数码管上,距离数据单位为m。 按键“S4”定义为“切换”按键,通过此按键切换距离的显示精度(一位或两位小数)。切换顺序如图所示。 数码管显示格式如下图…...
c++拷贝构造函数(深浅拷贝)+运算符重载
1拷贝构造函数 1.1定义 只有一个形参,且该形参是对本类类型对象的引用(一般用const 修饰),在用已经存在的类类型对象穿件新对象是由编译器自动调用。(是一种特殊构造,即初始化一个一模一样的新对象&#…...
操作系统 3.5-内存换入-请求调页
案例分析内存换入 内存换入分析: 内存换入(Swapping)是指操作系统将不常使用的内存页从物理内存(RAM)移动到磁盘上的交换空间(Swap Space),以释放物理内存供其他进程使用。当需要访…...
stm32工程,拷贝到另一台电脑编译,错误提示头文件找不到cannot open source input file “core_cm4.h”
提示 cannot open source input file “core_cm4.h” ,找不到 [ core_cm4.h ] 这个头文件 . 于是我在原电脑工程文件里找也没有找到这个头文件 接下来查看原电脑keil的头文件引入配置,发现只引入了工程文件下的头文件, 那么core_cm4.h到底哪里来的? (到现在我也不清楚怎…...
使用platformio如何定位hard fault错误
这里写自定义目录标题 前言过程记录结语前言 hard fault是单片机开发过程中经常会遇到的问题,通常是内存溢出、野指针访问等导致,对于有经验的工程师,在代码改动不大的情况下,一般可以通过代码审查定位到问题原因,但也有很多情况下需要借助调试工具进行定位,像Keil就有比…...
全局异常处理器的基本使用
那使用全局异常处理器可以么? 是的,使用全局异常处理器是一个非常好的选择,因为它可以将异常处理逻辑集中化,避免在 Service 层或 Controller 层中重复编写异常处理代码。以下是使用全局异常处理器来处理添加用户时 username 唯一…...
python入门:简单介绍和python和pycharm软件安装/学习网址/pycharm设置(改成中文界面,主题,新建文件)
Python 目前是 AI 开发的首选语言 软件安装 python解释器 官网下载 Python |Python.org 勾选 Add python.exe to PATH 将python.exe添加到PATH 勾选这个选项会将Python的可执行文件路径添加到系统的环境变量PATH中。这样做的好处是,你可以在命令行中从任何位置直…...
众趣科技助力商家“以真示人”,让消费场景更真实透明
在当今的消费环境中,消费者权益保护问题日益凸显。无论是网购商品与实物不符、预定酒店民宿与图文描述差异大,还是游览景区遭遇“照骗”,这些问题不仅让消费者在消费和决策过程中倍感困扰,也让商家面临信任危机。 消费者在享受便…...
【Redis】string类型
目录 1、介绍2、底层实现【1】SDS【2】int编码【3】embstr编码【4】raw编码【5】embstr和raw的区别 3、常用指令【1】字符串基本操作:【2】批量操作【3】计数器【4】过期时间【5】不存在就插入 4、使用场景 1、介绍 string是redis中最简单的键值对形式,…...
EPLAN许可证更新教程
随着电气设计软件的不断更新和优化,确保您的EPLAN许可证始终是最新版本对于顺畅的项目管理至关重要。本文将为您提供一份详尽的EPLAN许可证更新教程,帮助您轻松完成更新操作,确保您的软件始终保持最佳状态。 一、为什么需要更新EPLAN许可证&…...
学习笔记五——Rust 控制流全解析
📚 目录 什么是控制流?Rust 有什么特别?if 表达式完整语法loop / while / for 三种循环写法match 表达式 _ 通配符深入解释if let 表达式用法与场景Option、Some、None 全面通俗讲解 "Tom" 和 "Tom".to_string() 有啥本…...
远程桌面协议(RDP)详解:原理、优势与局限和优化方案分享
文章目录 导言一. RDP的工作原理二. RDP的优势三. RDP的局限性四. RDP的优化与替代方案五. 内网穿透远程访问总结 导言 远程桌面协议(RDP)是一种微软开发的专有协议,允许用户通过网络连接到另一台计算机,并像操作本地计算机一样进行操作。它广泛应用于远…...
Linux 系统管理常用命令
以下是 Linux 系统管理常用命令 的详细介绍,涵盖 IP地址查看、端口管理、进程监控 等核心操作,并附上实际示例: 一、查看网卡 IP 地址 1. 使用 ip 命令 # 查看所有网络接口信息(包括 IP 地址) ip addr show# 查看特定…...
蓝桥杯篇---客观题
文章目录 前言 前言 本文简单介绍了蓝桥杯中客观题各个部分的知识点。 一、单片机相关 IAP15F2K61S2单片机的定时器0具有4种工作模式,当采用外部12MHz晶振时,定时器最大定时长度65535us。8051单片机的P0口,当使用外部存储器时它是一个传输低…...
RK3568 基于Gstreamer的多媒体调试记录
文章目录 1、环境介绍2、概念理清3、提前准备4、GStreamer编译5、GStreamer基础介绍6、视频播放初体验7、视频硬编码7.1、h2647.2、h265 8、视频硬解码8.1、解码视频并播放解码视频并播放带音频 1、环境介绍 硬件:飞凌ok3568-c开发板 软件:原厂rk356x …...
ZYNQ笔记(五):AXI GPIO 中断
版本:Vivado2020.2(Vitis) 任务:使用 AXI GPIO IP 核以中断方式实现按键 KEY 控制 LED 亮灭翻转(两个都在PL端) 目录 一、介绍 二、硬件设计 三、软件设计 四、效果 一、介绍 AXI GPIO 中断通常…...
C++23 多维下标运算符:探索 P2128R6 提案
文章目录 一、背景与动机二、语法与实现2.1 语法2.2 实现方式 三、应用场景3.1 多维数组3.2 自定义数据结构3.3 并行计算 四、性能影响4.1 编译时优化4.2 自定义数据结构的优化 五、总结 C23 引入了许多新特性,其中之一便是多维下标运算符(P2128R6&#…...
原理图设计准备:页面栅格模板应用设置
一、页面大小的设置 (1)单页原理图页面设置 首先,选中需要更改页面尺寸的那一页原理图,鼠标右键,选择“Schmatic Page Properties”选项,进行页面大小设置。 (2)对整个原理图页面设…...
LeeCode 409.最长回文串
给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的 回文串 的长度。 在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串。 示例 1: 输入:s "abccccdd" 输出:7 解释: 我们可以构造的…...
保护PCBA的不同方法:喷三防漆 vs 镀膜
PCBA(印刷电路板组件)的防护工艺中,喷三防漆和镀膜(如Parylene气相沉积)是两种常见技 术。它们在防护目的上类似,但在具体实现方式和应用场景上有显著差异。以下从外观、工艺、性 能、物理性质和成本五个…...
XILINX FPGA万兆光电口PXIE板卡设计
主要性能指标 1、 FPGA 型号: XC7K325-TFFG676-2 ; 2、 网络端口速率 : 10Gbps/1Gbps/2.5Gbps ; 3、 网络端口形式 : 3 路 SFP 万兆光电口 ; 4、 内存数量: 4Gb 4 ; 5、 内存带宽&…...
LangChain4j实战-Java AI应用开源框架之LangChain4j和Spring AI
今天这篇文章我来分享LangChain4j实战-Java AI应用开源框架之LangChain4j和Spring AI。 在贝恩聊架构AI专栏中通过学习如何使用Java相关AI应用开源框架,到后续开发企业级Java AI应用,将大型语言模型和AI工具集成到现有系统中。我们将重点介绍如何使用Sp…...
机器学习十大算法全解析机器学习,作为人工智能的基石,涵盖了众多高效的算法。今天,我们就来深入探讨其中的十大核心算法!
1️⃣ 线性回归:通过最小化误差的平方和来寻找最佳函数匹配。 2️⃣ 逻辑回归:用于分类问题,通过逻辑函数来预测事件发生的概率。 3️⃣ 决策树:基于特征选择和阈值来构建树形结构,用于分类和回归。 4️⃣ 朴素贝叶…...
day26图像处理OpenCV
文章目录 一、OpenCV1.介绍2.下载3.图像的表示4.图像的基本操作4.1图片读取或创建4.1.1读取4.1.2创建 4.2创建窗口4.3显示图片4.3.1设置读取的图片4.3.2设置显示多久4.3.3释放 4.4.保存图片4.5图片切片(剪裁)4.6图片大小调节 5.在图像中绘值5.1绘制直线5…...
怎么查询SQL Server AlwaysOn
1. SQL Server AlwaysOn 是什么? SQL Server AlwaysOn 是 Microsoft 提供的高可用性(High Availability, HA)和灾难恢复(Disaster Recovery, DR)解决方案,包含以下两个核心技术: 组件描述故障…...
10分钟做了一个投资回报计算器,欢迎大家使用
一、背景 今天突然想算一下1万本金,2%利率存2年情况下的投资回报收益情况,但是发现手上没有计算器,想着自己做一个网页简单实现一下,于是有了这个小工具(FutureValueCalculator——未来价值计算器)。 二、…...