從0開始的操作系統手搓教程23:構建輸入子系統——實現鍵盤驅動1——熱身驅動

目錄

所以,鍵盤是如何工作的

說一說我們的8042

輸出緩沖區寄存器

狀態寄存器

控制寄存器

動手!

注冊中斷

簡單整個鍵盤驅動

Reference

ScanCode Table


我們下一步就是準備進一步完善我們系統的交互性。基于這個,我們想到的第一個可以用來進行輸入的設備,就是鍵盤!這個不假。我們這個章節的核心內容,就是構建一個基于鍵盤的輸入子系統。給我們之后的系統更多功能添磚加瓦!

所以,鍵盤是如何工作的

雖然現在的鍵盤可以說是日新月異,但是基本的工作原理不會發生很大的改變。我們的鍵盤是一個獨立的設備,需要介入總線跟我們的主板系統溝通。不管怎么說,我們的主板上,存在一個Intel 8048 或兼容芯片,它的作用是:每當鍵盤上發生按鍵操作,它就向鍵盤控制器報告哪個鍵被按下,按鍵是否彈起。

上圖的8048 是鍵盤上的芯片,其主要責任就是監控哪個鍵被按下。當鍵盤上發生按鍵操作時,8048 當然知道是哪個鍵被按下。但光它自己知道還不 行,它畢竟要將按鍵信息傳給8042,必須得讓8042 知道到底是按下了哪個鍵,為此8048 必然要和8042達成一個協議,這個協議規定了鍵盤上的每個物理鍵對應的唯一數值,說白了就是對鍵盤上所有的按鍵進行編碼,為每個按鍵分配唯一的數字,這樣雙方都知道了每個數值代表哪個鍵。當某個鍵被按下時,8048 把這個鍵對應的數值發送給8042,8042根據這個數值便知道是哪個鍵被按下了。

現在,筆者就在使用鍵盤敲擊一些字符,我松開手,按鍵被彈起來了,輸入顯然完成了,8048就會通知在主板上的8042何時按鍵被彈起,也就是擊鍵操作何時結束,這樣8042才知道用戶在一次持續按鍵操作中到底輸入了多少個相同的字符。因此,鍵盤掃描碼中不僅僅要記錄按鍵被按下時對應的編碼,還要記錄按鍵被松開(彈起)時的編碼。這下事情變得顯然了,我們不得不請出兩個碼——按鍵被按下時的編碼叫通碼,也就是表示按鍵上的觸點接通了內部電路,使硬件產生了一個碼,故通碼也稱為 makecode。按鍵在被按住不松手時會持續產生相同的碼,直到按鍵被松開時才終止,因此按鍵被松開彈起時產生的編碼叫斷碼,也就是電路被斷開了,不再持續產生碼了,故斷碼也稱為breakcode。一個鍵的掃描碼是由通碼和斷碼組的。

鄭剛老師在《操作系統真相還原》中介紹了三種掃描碼,這里只介紹第二種,因為余下的已經幾乎沒人使用了,我們(程序員)不是不知道鍵盤用的是哪種掃描碼嗎,那好,只要 8042 知道就行。為了兼容第一套 鍵盤掃描碼對應的中斷處理程序,不管鍵盤用的是何種鍵盤掃描碼,當鍵盤將掃描碼發送到 8042 后,都 由 8042 轉換成第一套掃描碼,這就是我們上一節中所說的 8042 的“處理”。 因此,我們在鍵盤的中斷處理程序中只處理第一套鍵盤掃描碼就可以了。關于整張表,參考筆者的附錄即可。

看完這個表,你仔細觀察一下,大多數情況下第一套掃描碼中的通碼和斷碼都是 1 字節大小。而且不難發現:斷碼 = 0x80 + 通碼。

所以回過頭來,我們在分析一下:完整的擊鍵操作包括兩個過程,先是被按下,也許是被按下一瞬間,也許是持續保持被按下,然后是被松開,總之,按下的動作是先于松開發生的,因此每次按鍵時會先產生通碼,再產生斷碼。比如我們按下字符 a 時,按照第一套鍵盤掃描碼來說,先是產生通碼 0x1e,后是產生斷碼 0x9e。

另一些我們注意到:一些按鍵的通碼和斷碼都以0xe0 開頭,它們占2 字節,甚至Pause 鍵以0xe1 開頭, 占6字節。原因是這樣的,并不是一種鍵盤就要用一套鍵盤掃描碼,最初第一套鍵盤掃描碼是由XT 鍵盤所使用的,它后來也被一些更新的鍵盤所使用。XT 鍵盤上的鍵很少,比如右邊回車鍵附近就沒有alt 和ctrl 鍵,這是在后來的鍵盤中才加進去的,因此表示擴展 extend,所以在掃描碼前面加了 0xe0 作為前綴。比如在 XT 鍵盤上,左邊有alt 鍵,其通碼為0x38,斷碼為0xb8。右邊的alt 鍵是后來在新的鍵盤上加進去的,因此,一方面為了表示都是同樣功能的alt 鍵,另一方面表示不是左邊那個alt,而是右邊的alt,于是這個擴展的alt 鍵的掃描碼便為“0xe0 和原來左邊alt 的掃描碼”。因此,右邊alt 鍵的通碼便為“0xe0,0x38”,斷碼為“0xe0,0xb8”。

那組合鍵呢?比如說我們嗯下Ctrl + C鍵,想要復制鄭剛老師的教授內容,自己偷個懶的時候,這個是如何處理的呢?

現在你自己慢慢做一次Ctrl + C試一下:

  1. 你先摁下Ctrl鍵,畢竟你先按C就會打印出字符C了。

  2. 你保持Ctrl鍵不松手

  3. 一個手指按下C

  4. 然后隨意的松開,比如說可能松開ctrl

  5. 松開C,然后感覺自己像是一個笨蛋一樣(笑)

不開玩笑了,當我們做步驟一的時候,8048向8042發送了<L-ctrl>鍵的通碼0x14,當然,這顯然是第二套掃描碼8042收到0x14后將其轉換為第一套鍵盤掃描碼0x1d,并將其保存到自己的輸出緩沖區寄存器中。接著,8042向中斷代理發送中斷信號,處理器隨后執行鍵盤中斷處理程序。鍵盤中斷處理程序從8042的輸出緩沖區寄存器中獲取掃描碼0x1d,并判斷這次按下的是<L-ctrl>鍵(實際上無論是<L-ctrl>還是<R-ctrl>,通常都被視為Ctrl鍵,因為它們只是位置不同,功能相同)。

我們的鍵盤處理程序在某個全局變量中記錄Ctrl鍵已被按下。這個,跟大部分的鍵盤處理程序是一樣的。

第二步的時候,我們的<L-ctrl>鍵持續按住不松手因此8048會持續向8042發送0x14。8042每次都會將其轉換為第一套鍵盤掃描碼0x1d,并向中斷代理發送中斷信號。每次鍵盤中斷處理程序都會從8042中獲取到0x1d。與步驟一相同,鍵盤處理程序判斷這是<L-ctrl>的通碼,并在全局變量中記錄Ctrl鍵被按下。盡管Ctrl鍵已經被按下,鍵盤處理程序可能只記錄最后一次按下的鍵,而不關心之前按下了多少次相同的鍵。(我們好像沒必要記著,對吧)

第三步,我們終于準備發送c的第二套鍵盤掃描碼0x21,8042將其轉換為第一套鍵盤掃描碼0x2e,并保存到輸出緩沖區寄存器中,隨后向中斷代理發送中斷信號。

鍵盤中斷處理程序開始執行,從8042的輸出緩沖區寄存器中獲取0x2e。鍵盤處理程序判斷這次按下的是c鍵,并檢查之前Ctrl鍵已經被按下(全局變量中有記錄),因此判斷用戶按下的是“Ctrl+c”組合鍵。Ctrl、Alt、Shift等控制鍵通常與后續按下的鍵組合使用,這是基于微軟的操作習慣,即控制鍵先按下,普通鍵后按下。由于這次按下的不是控制鍵,鍵盤處理程序將記錄Ctrl鍵是否按下的全局變量清空,并將“Ctrl+C”這一消息上報給上層模塊。我們的上層接受到后,就會做對應的Hook操作。

我們在步驟四種假設你是,,<L-ctrl>鍵被松開,8048向8042發送它的第二套鍵盤掃描碼0xf0和0x14(斷碼)。第二套鍵盤掃描碼的斷碼通常由固定的前綴0xf0和其通碼組成。8042將這兩個字節轉換為第一套鍵盤掃描碼0x9d(斷碼),隨后發送中斷信號。鍵盤中斷處理程序發現最高位為1,表示這是斷碼,意味著鍵被松開。無論松開的是什么鍵,鍵盤處理程序都會忽略,不做任何處理。

在步驟五中,a鍵被松開,8048向8042發送它的第二套鍵盤掃描碼0xf0和0x21(斷碼)。8042將其轉換為0xae并保存到輸出緩沖區寄存器中,隨后發送中斷信號。鍵盤中斷處理程序讀取該掃描碼,發現是鍵被彈起,因此忽略該事件。

說一說我們的8042

Intel 8042 芯片或兼容芯片被集成在主板上的南橋芯片中,它是鍵盤控制器,也就是鍵盤的 IO 接口, 因此它是8048的代理,也是前面所得到的處理器和鍵盤的“中間層”。8048通過PS/2、USB 等接口與8042通信,處理器通過端口與8042通信(IO 接就是外部硬件的代理,它和處理器都位于主機內部,因此處理器與 IO 接口可以通過端口直接通信)。

我們來看看IO口:

寄存器端口讀/寫
Output Buffer(輸出緩沖區)0x60
Input Buffer(輸入緩沖區)0x60
Status Register(狀態寄存器)0x64
Control Register(控制寄存器)0x64

8042 是連接 8048 和處理器的橋梁,8042 存在的目的是:為了處理器可以通過它控制 8048 的工作方式,然后讓8048 的工作成果通過8042 回傳給處理器。此時8042 就相當于數據的緩沖區、中轉站,根據數據被發送的方向,8042 的作用分別是輸入和輸出。

處理器把對 8048 的控制命令臨時放在 8042 的寄存器中,讓 8042 把控制命令發送給 8048,此時 8042 充當了 8048 的參數輸入緩沖區。 8048 把工作成果臨時提交到8042 的寄存器中,好讓處理器能從 8042 的寄存器中獲取它(8048)的工作成果,此時 8042 充當了 8048 的結果輸出緩沖區。

當需要把數據從處理器發到8042 時(數據傳送尚未發生時),0x60 端口的作用是輸入緩沖區,此時應該用out 指令寫入0x60 端口。

當數據已從 8048 發到 8042 時,0x60 端口的作用是輸出緩沖 區,此時應該用in指令從8042 的0x60 端口(輸出緩沖區寄存器)讀 取8048 的輸出結果。

最后,出于編程目的,還差寄存器說明:

寄存器寬度讀寫屬性描述
輸入緩沖區寄存器8 位只寫鍵盤驅動程序通過 out 指令向此寄存器寫入對 8048 的控制命令、參數等。對于 8042 本身的控制命令也是寫入此寄存器。
狀態寄存器8 位只讀反映 8048 和 8042 的內部工作狀態。各位意義詳見描述。
控制寄存器8 位只寫用于寫入命令控制字。每個位都可以設置一種工作方式,意義詳見描述。
輸出緩沖區寄存器

8042的輸出緩沖區寄存器是一個8位寬度的寄存器,只讀,鍵盤驅動程序從此寄存器中通過in指令讀取來自8048 的掃描碼、來自8048 的命令應答以及對8042 本身設置時,8042 自身的應答也從該寄存器中獲取。

注意,輸出緩沖區寄存器中的掃描碼是給處理器準備的,在處理器未讀取之前,8042 不會再往此寄 存器中存入新的掃描碼。

8042 是怎樣知道輸出緩沖區寄存器中的值是否被讀取了呢?這個簡單,8042 也有個 智能芯片,它為處理器提供服務,當處理器通過端口跟它要數據的時候它當然知道了,因此,每當有 in 指令來讀取此寄存器時,8042 就將狀態寄存器中的第0位置成0,這就表示寄存器中的掃描碼數據已經被取走,可以繼續處理下一個掃描碼了。當再次往輸出緩沖寄存器存入新的掃描碼時,8042 就將狀態寄存器中的第 0 位置為 1,這表示輸出緩沖寄存器已滿,可以讀取了。

狀態寄存器
描述
位 0置 1 時表示輸出緩沖區寄存器已滿,處理器通過 in 指令讀取后該位自動置 0。
位 1置 1 時表示輸入緩沖區寄存器已滿,8042 將值讀取后該位自動置 0。
位 2系統標志位,最初加電時為 0,自檢通過后置為 1。
位 3置 1 時,表示輸入緩沖區中的內容是命令,置 0 時,輸入緩沖區中的內容是普通數據。
位 4置 1 時表示鍵盤啟用,置 0 時表示鍵盤禁用。
位 5置 1 時表示發送超時。
位 6置 1 時表示接收超時。
位 7來自 8048 的數據在奇偶校驗時出錯。
控制寄存器
描述
位 0置 1 時啟用鍵盤中斷。
位 1置 1 時啟用鼠標中斷。
位 2設置狀態寄存器的位 2。
位 3置 1 時,狀態寄存器的位 4 無效。
位 4置 1 時禁止鍵盤。
位 5置 1 時禁止鼠標。
位 6將第二套鍵盤掃描碼轉換為第一套鍵盤掃描碼。
位 7保留位,默認為 0。

動手!

注冊中斷

這里我們先把中斷一次性注冊了,省事

; -------------------------------------------------------------------------
; ? Part 2 Table Page for the interrupt for kernel
; -------------------------------------------------------------------------
INTR_VECTOR 0x20, PUSH_ZERO ?; Entry for the timer interrupt.
INTR_VECTOR 0x21, PUSH_ZERO ?; Entry for the keyboard interrupt.
INTR_VECTOR 0x22, PUSH_ZERO ?; Cascade interrupt.
INTR_VECTOR 0x23, PUSH_ZERO ?; Entry for serial port 2.
INTR_VECTOR 0x24, PUSH_ZERO ?; Entry for serial port 1.
INTR_VECTOR 0x25, PUSH_ZERO ?; Entry for parallel port 2.
INTR_VECTOR 0x26, PUSH_ZERO ?; Entry for the floppy disk.
INTR_VECTOR 0x27, PUSH_ZERO ?; Entry for parallel port 1.
INTR_VECTOR 0x28, PUSH_ZERO ?; Entry for the real-time clock.
INTR_VECTOR 0x29, PUSH_ZERO ?; Redirect.
INTR_VECTOR 0x2a, PUSH_ZERO ?; Reserved.
INTR_VECTOR 0x2b, PUSH_ZERO ?; Reserved.
INTR_VECTOR 0x2c, PUSH_ZERO ?; PS/2 mouse.
INTR_VECTOR 0x2d, PUSH_ZERO ?; FPU floating-point unit exception.
INTR_VECTOR 0x2e, PUSH_ZERO ?; Hard disk.
INTR_VECTOR 0x2f, PUSH_ZERO ?; Reserved.

記得修改一下支持的中斷數

#define IDT_DESC_CNT (0x30) // The number of interrupt descriptors in the IDT

然后在pci.c種,記得只打開鍵盤的中斷

 ? ?// Mask interrupts to disable all IRQsoutb(PCI_MASTER_DATA_PORT, 0xfd); ? ? ?// Mask all IRQs on the master PIC (set bit 0)outb(PCI_SLAVE_DATA_PORT, 0xff); ? ? ? // Mask all IRQs on the slave PIC (set all bits)

簡單整個鍵盤驅動

實際上就是直接讀緩存端口就好了哈哈

#include "include/device/keyboard.h"
#include "include/library/ccos_print.h"
#include "include/kernel/interrupt.h"
#include "include/device/configs/keyboard_ascii.h"
#include "include/device/configs/keyboard_mappings.h"
#include "include/io/io.h"
?
static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place__ccos_putchar('C');// uint8_t scancode = inb(KEYBOARD_BUF_PORT);// __ccos_display_int(scancode);return;
}
?
// register the interrupt here
void init_basic_input_subsystem(void)
{ccos_puts("initing subsystem of input: from keyboard!\n");register_intr_handler(KEYBOARD_INTERRUPT_N, keyboard_intr_handler);ccos_puts("init subsystem of input: from keyboard done!\n");
}

這里呢,#include "include/device/configs/keyboard_ascii.h" #include "include/device/configs/keyboard_mappings.h"兩個文件就具體到筆者的代碼種看先。

嘿!我們上電試試看,不用擔心線程的事情,我們把時鐘中斷關閉了。

可以看到我們摁下摁鍵的時候,這些字符就會蹦出來了!注意,嗯下一次,彈起一次。這就說明了通碼和斷碼的存在了。

小小的修改一下代碼哈:

static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place// __ccos_putchar('C');uint8_t scancode = inb(KEYBOARD_BUF_PORT);__ccos_display_int(scancode);__ccos_putchar(' ');return;
}

看看!現在外面收到了scancode了!

代碼

CCOperateSystem/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code at main · Charliechen114514/CCOperateSystemhttps://github.com/Charliechen114514/CCOperateSystem/tree/main/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code

下一篇

nullhttps://blog.csdn.net/charlie114514191/article/details/146105521

Reference

ScanCode Table

通碼斷碼通碼斷碼
<esc>0181<caps lock>3aba
F13bbba1e9e
F23cbcs1f9f
F33dbdd20a0
F43ebef21a1
F53fbfg22a2
F640c0h23a3
F741c1j24a4
F842c2k25a5
F943c3l26a6
F1044c4:;27a7
F1157d7"'28a8
F1258d8<enter>1c9c
29a9<L-Shift>2aaa
!10282z2cac
@20383x2dad
#30484c2eae
$40585v2faf
%50686b30b0
^60787n31b1
&70888m32b2
*80989<,33b3
(90a8a>.34b4
)00b8b?/35b5
_-0c8c<R-shift>36b6
+=0d8d<L-ctrl>1d9d
<backspace>0e8e<L-alt>38b8
<tab>0f8f<space>39b9
q1090<R-alt>e0,38e0,b8
w1191<R-ctrl>e0,1de0,9d
e1212
r1393
t1494
y1595
u1696
i1797
o1898
p1999
{[1a9a
}]1b9b
|\2bab
通碼斷碼通碼斷碼
PrintScreen SysRqe0,2a,e0,37e0,b7,e0,aaNumLock45c5
Scroll Lock46c6/e0,35e0,b5
Pause Breake1,1d,45e1,9d,c5*37b7
Inserte0,52e0,d2-4aca
Homee0,47e0,c77Home47c7
Page Upe0,49e0,c98Up48c8
Deletee0,53e0,d39PgUp49c9
Ende0,4fe0,cf4Left4bcb
Page Downe0,51e0,d154ccc
e0,46e0,c66Right4dcd
e0,4de0,cd1End4fcf
e0,48e0,c82Down50d0
e0,50e0,d03PgDn51d1
0Ins52d2.Del53d3
+4eceEntere0,1ce0,9c

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/71636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/71636.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/71636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

百度SEO關鍵詞布局從堆砌到場景化的轉型指南

百度SEO關鍵詞布局&#xff1a;從“堆砌”到“場景化”的轉型指南 引言 在搜索引擎優化&#xff08;SEO&#xff09;領域&#xff0c;關鍵詞布局一直是核心策略之一。然而&#xff0c;隨著搜索引擎算法的不斷升級和用戶需求的多樣化&#xff0c;傳統的“關鍵詞堆砌”策略已經…

Python ? Unix時間戳轉日期或日期轉時間戳工具分享

設計一款Unix時間戳和日期轉換工具&#xff0c;其代碼如下&#xff1a; from datetime import datetimeclass Change_Date_Time(object):def __init__(self, date_strNone, date_numNone):self.date_str date_strself.date_num date_num# 轉時間戳def datetime2timestamp(s…

【目標檢測】【NeuralPS 2023】Gold-YOLO:通過收集與分發機制實現的高效目標檢測器

Gold-YOLO&#xff1a; Efficient Object Detector via Gather-and-Distribute Mechanism Gold-YOLO&#xff1a;通過收集與分發機制實現的高效目標檢測器 0.論文摘要 在過去的幾年中&#xff0c;YOLO系列模型已成為實時目標檢測領域的領先方法。許多研究通過修改架構、增強數…

π0源碼解析——一個模型控制7種機械臂:對開源VLA sota之π0源碼的全面分析,含我司的部分落地實踐

前言 ChatGPT出來后的兩年多&#xff0c;也是我瘋狂寫博的兩年多(年初deepseek更引爆了下)&#xff0c;比如從創業起步時的15年到后來22年之間 每年2-6篇的&#xff0c;干到了23年30篇、24年65篇、25年前兩月18篇&#xff0c;成了我在大模型和具身的原始技術積累 如今一轉眼…

K8s 1.27.1 實戰系列(六)Pod

一、Pod介紹 1、Pod 的定義與核心設計 Pod 是 Kubernetes 的最小調度單元,由一個或多個容器組成,這些容器共享網絡、存儲、進程命名空間等資源,形成緊密協作的應用單元。Pod 的設計靈感來源于“豌豆莢”模型,容器如同豆子,共享同一環境但保持隔離性。其核心設計目標包括…

企業日常工作中常用的 Linux 操作系統命令整理

Linux 操作系統命令整理 在企業級運維、開發和日常工作中&#xff0c;Linux 命令是繞不開的核心技能。不論是日志排查、進程管理&#xff0c;還是高效運維優化&#xff0c;掌握這些命令都能讓你事半功倍&#xff01;本篇文章整理了自己在日常工作中積累最常用的 Linux 命令&am…

實現NTLM relay攻擊工具的Python代碼示例

以下是一個實現NTLM relay攻擊工具的Python代碼示例&#xff0c;該工具可以完成自動掃描IP、配置相關協議、獲取hash、自動化設置和執行攻擊步驟等功能。 代碼思路 IP掃描&#xff1a;使用scapy庫進行IP掃描&#xff0c;找出活躍的IP地址。Responder配置&#xff1a;自動配置…

Kotlin和Java區別

哈哈哈&#xff0c;前段時間&#xff0c;面試的時候&#xff0c;突然問到我Kotlin和Java的區別&#xff0c;一下子把我問懵逼了&#xff0c;確實沒遇到問這個的&#xff0c;想了下&#xff0c;說了下Kotlin的編譯時空檢查機制&#xff0c;代碼更簡潔&#xff0c;很多封裝好的AP…

【大模型】大模型分類

大模型&#xff08;Large Models&#xff09;通常指參數量巨大、計算能力強大的機器學習模型&#xff0c;尤其在自然語言處理&#xff08;NLP&#xff09;、計算機視覺&#xff08;CV&#xff09;等領域表現突出。以下是大模型的常見分類方式&#xff1a; 1. 按應用領域分類 …

centos中使用svn整理

centos中使用svn整理 1. 安裝 SVN 客戶端2. 常見 SVN 用法及示例2.1 創建 SVN 倉庫2.2 檢出&#xff08;Checkout&#xff09;項目2.3 添加文件到版本控制2.4 提交&#xff08;Commit&#xff09;更改2.5 更新&#xff08;Update&#xff09;本地工作副本2.6 查看文件狀態2.7 查…

游戲元宇宙崛起:AI代理IP驅動虛擬世界“無限可能”?

在科技飛速發展的當下&#xff0c;游戲元宇宙正以一種前所未有的姿態崛起&#xff0c;它猶如一顆璀璨的新星&#xff0c;吸引著無數人的目光。而AI代理IP&#xff0c;正成為驅動這個虛擬世界展現“無限可能”的關鍵力量。 「快代理&#xff5c;11年專注企業級代理IP云服務 —…

基于Servlet + JSP 的物業管理系統

Javaweb物業管理系統&#xff5c;Java&#xff5c;Servlet | JavaWeb&#xff5c;web網站&#xff5c; 分管理員登錄&#xff0c;用戶登錄。 一、內容 項目源碼 配套文檔 環境部署教程 項目運行教程 二、技術介紹 技術應用&#xff1a; Servlet JavaBean CSS JSP 開發環…

Billing的patient balance的2個例子

Billing的patient balance的2個例子 第一個例子 下面是 0852醫院的00005641的計算&#xff1a; 主保險 Allowable: 78.81 Applied:61.79 CoInsurance:17.02 第二保險 Allowable: 15.76 Applied:15.76 我們在計算時是用主保險的Allowable: 78.81&#xff…

哪些培訓課程適合學習PostgreSQL中級認證知識?

PostgreSQL 中級工程師 PGCP 認證培訓&#xff1a;由重慶思 莊經驗豐富的講師鄭全老師授課。課程內容系統全面&#xff0c;涵蓋了 PostgreSQL 數據庫從基礎到高級的知識&#xff0c;包括數據庫的安裝、建庫、用戶與角色管理等基礎內容&#xff0c;也有性能調優、索引原理與應用…

計算機二級MS之PPT

聲明&#xff1a;跟著大貓和小黑學習隨便記下一些筆記供大家參考&#xff0c;二級考試之前將持續更新&#xff0c;希望大家二級都能輕輕松松過啦&#xff0c;過了二級的大神也可以在評論區留言給點建議&#xff0c;感謝大家&#xff01;&#xff01; 文章目錄 考題難點1cm25px…

第5章 使用OSSEC進行監控(網絡安全防御實戰--藍軍武器庫)

網絡安全防御實戰--藍軍武器庫是2020年出版的&#xff0c;已經過去3年時間了&#xff0c;最近利用閑暇時間&#xff0c;抓緊吸收&#xff0c;總的來說&#xff0c;第5章開始進入主機安全&#xff08;HIDS&#xff09;領域了&#xff0c;2022年的時候有幸做過終端安全一段時間&a…

Android 低功率藍牙之BluetoothGattCallback回調方法詳解

BluetoothGattCallback 是 Android 中用于處理藍牙低功耗&#xff08;BLE&#xff09;設備通信的核心回調類。它負責處理與 BLE 設備的連接、服務發現、數據讀寫等操作的結果。以下是對 BluetoothGattCallback 的詳細解析&#xff1a; 1. onConnectionStateChange 觸發時機&am…

DeepSeek group-limited expert routing和負載均衡

Ref https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py GitHub - deepseek-ai/EPLB: Expert Parallelism Load Balancer DeepSeek-V3 Technical Report DeepSeek的路由方法 class Gate(nn.Module):def __init__(self, args: ModelArgs):super().__…

Python的Pandas和matplotlib庫:讓數據可視化賊簡單

在數據爆炸的時代&#xff0c;數據可視化已成為數據分析的關鍵環節。Python 作為強大的編程語言&#xff0c;擁有眾多用于數據可視化的庫&#xff0c;而 pandas 庫在其中扮演著重要角色。它不僅能高效處理和分析數據&#xff0c;還具備強大的數據可視化功能&#xff0c;讓我們輕…

【代碼分享】基于IRM和RRT*的無人機路徑規劃方法詳解與Matlab實現

基于IRM和RRT*的無人機路徑規劃方法詳解與Matlab實現 1. IRM與RRT*的概述及優勢 IRM&#xff08;Influence Region Map&#xff09;通過建模障礙物的影響區域&#xff0c;量化環境中的安全風險&#xff0c;為RRT算法提供啟發式引導。RRT&#xff08;Rapidly-exploring Random…