eBPF 實時捕獲鍵盤輸入

eBPF 實時捕獲鍵盤輸入

本文將帶你一步步實現一個基于eBPF kprobe的鍵盤記錄功能,通過Go語言配合libbpfgo,你將學會如何無損地監控系統鍵盤輸入,并從中獲取實時數據,進一步提高系統安全和監控能力。


1. 說明

本文屬于專欄 Go語言+libbpfgo實戰eBPF開發,示例代碼目錄為 040

如何下載并運行代碼,請參考 專欄介紹。

注: 老學員可以直接 git pull 拉取最新代碼。


2. 引言

在本篇文章中,我們將利用 eBPF 的 kprobe 技術捕捉鍵盤輸入事件,實現一個簡單的鍵盤記錄功能。你將學習如何在內核層面監控 input_handle_event 函數,并將捕獲到的按鍵事件傳遞到用戶空間進行處理。整個過程高效且非侵入式,適用于實時監控場景。💡

eBPF 允許在內核中動態加載和執行代碼。借助 kprobe,我們可以在 input_handle_event 函數入口處掛載探針,從而捕獲關鍵事件。

  • 鍵盤事件類型:我們關注 EV_KEY 類型的按鍵事件,并僅記錄按下(value=1)時的事件。
  • 事件傳遞方式:通過 bpf_perf_event_output 將事件數據發送到用戶空間,用戶空間程序使用 perf buffer 進行實時讀取。

這樣既能精準監控輸入事件,又無需對系統進行侵入式修改。


3. 原理詳解

3.1 當你按下一個鍵盤上的按鍵時,發生了什么?

流程圖

硬件中斷 → 鍵盤驅動 → 輸入子系統 → TTY 行規程 → Shell 進程 → TTY 回顯 → 終端渲染

在 Linux 系統中,從按下鍵盤到字符顯示在終端上,涉及 硬件中斷處理、內核子系統協作和用戶空間進程交互。以下是詳細流程和關鍵函數。

1. 硬件中斷觸發

  • 硬件層面:鍵盤控制器(如 PS/2 或 USB)檢測到按鍵動作,生成 硬件中斷(PS/2 鍵盤通常使用 IRQ 1)。
  • 中斷控制器:將中斷路由至 CPU,CPU 通過中斷向量表調用對應的 中斷處理程序

2. 內核中斷處理

  • 中斷處理函數:內核注冊的鍵盤中斷處理程序(irq_handler)被觸發。
    • 關鍵函數request_irq() 注冊中斷處理函數,如 PS/2 鍵盤的 kbd_event 或 USB 鍵盤的 usb_kbd_irq
  • 讀取掃描碼:驅動從鍵盤控制器讀取 掃描碼(Scancode)(如 inb(0x60) 讀取 PS/2 鍵盤數據端口)。
  • 轉換為鍵碼:掃描碼轉換為 鍵碼(Keycode)(如 kbd_keycode 處理映射關系)。

3. 輸入子系統(Input Subsystem)

  • 生成輸入事件:鍵碼通過輸入子系統封裝為 input_event 結構(包含時間戳、鍵值、動作等)。
    • 關鍵函數input_event()input_handle_event()
  • 傳遞事件:事件通過 /dev/input/eventX 設備節點傳遞,供用戶空間程序(如終端)讀取。
    • 關鍵結構struct input_handler 負責事件路由(如 evdev_handler)。

4. TTY 子系統處理

  • 綁定到 TTY:輸入事件傳遞到當前活動的 TTY(如 /dev/tty1)。
    • 關鍵函數tty_insert_flip_char() 將字符寫入 TTY 的 flip buffer。
  • 行規程(Line Discipline):處理特殊字符(如回車、退格)。默認行規程為 n_tty
    • 關鍵函數n_tty_receive_char() 解析字符并執行回顯邏輯。
  • 刷新緩沖區:調用 tty_flip_buffer_push() 推送數據至 TTY 讀隊列。

5. 用戶空間進程讀取輸入

  • 前臺進程:Shell(如 Bash)通過 read() 系統調用讀取 TTY 設備的輸入。
    • 關鍵路徑read()tty_read()copy_from_read_buf()
  • 行編輯模式(Canonical Mode):啟用時,輸入緩存在內核直到用戶按下回車。

6. 字符顯示到終端

  • 回顯(Echo):默認 n_tty 規程會自動回顯字符到終端。
    • 關鍵函數n_tty_receive_char() 調用 echo_char() 進行回顯。
  • 終端寫入:字符通過 write() 系統調用發送至終端顯示。
    • 關鍵路徑write()tty_write()do_tty_write() → 終端驅動的寫函數。
  • 終端渲染方式
    • 物理終端:通過顯卡驅動(如 vt_console_print())直接輸出。
    • 偽終端(PTY):如 SSH 或終端模擬器(如 GNOME Terminal),字符通過 PTY 主從設備傳輸,最終由終端模擬器渲染。

3.2 在哪里捕獲鍵盤輸入事件?

在 Linux 系統中,鍵盤輸入事件可以在不同層次進行捕獲,主要包括 內核態用戶態,以下是常見的捕獲位置及方法。

1. 內核態捕獲

1.1 中斷處理程序(IRQ 級別)

  • 最底層的捕獲方式,在鍵盤觸發 硬件中斷 時,內核的 中斷處理函數 被調用。
  • 相關代碼位置:drivers/input/keyboard/atkbd.c(PS/2 鍵盤) 或 drivers/hid/usbhid/usbkbd.c(USB 鍵盤)。
  • 關鍵函數:
    • irq_handler_t kbd_event()(PS/2)
    • usb_kbd_irq()(USB)
    • 讀取 掃描碼 并傳遞至 輸入子系統

1.2 輸入子系統(Input Subsystem)

  • 內核的 input_event 機制封裝了鍵盤輸入事件。
  • 相關設備節點:/dev/input/eventX
  • 關鍵函數:
    • input_event() 生成鍵盤事件。
    • input_handle_event() 處理事件并分發至用戶空間。
2. 用戶態捕獲

2.1 通過 /dev/input/eventX 捕獲原始輸入事件

  • 適用于讀取底層輸入設備(適用于鍵盤監聽、按鍵統計等)。

  • 代碼示例(使用 evdev 接口):

    #include <stdio.h>
    #include <fcntl.h>
    #include <linux/input.h>int main() {int fd = open("/dev/input/event2", O_RDONLY);if (fd < 0) {perror("open");return 1;}struct input_event ev;while (read(fd, &ev, sizeof(ev)) > 0) {if (ev.type == EV_KEY && ev.value == 1) {printf("Key %d pressed\n", ev.code);}}close(fd);return 0;
    }
    

2.2 通過 /dev/tty 讀取終端輸入

  • 適用于讀取當前終端的鍵盤輸入(受 TTY 規程控制)。

  • 代碼示例(讀取標準輸入):

    #include <stdio.h>int main() {char c;while (1) {c = getchar();printf("Pressed: %c\n", c);}return 0;
    }
    

2.3 使用 libinput 監聽鍵盤輸入(Wayland 環境)

  • 適用于現代桌面環境(X11/Wayland)。

  • 監聽系統級輸入事件,適用于圖形界面應用。

  • 代碼示例(Python):

    from evdev import InputDevice, categorize, ecodesdev = InputDevice('/dev/input/event2')
    for event in dev.read_loop():if event.type == ecodes.EV_KEY:print(categorize(event))
    

2.4 監聽 X11 按鍵事件(X Window System)

  • 適用于 GUI 應用,使用 Xlib 監聽按鍵。

  • 代碼示例(Python + python-xlib):

    from Xlib import display, Xd = display.Display()
    r = d.screen().root
    r.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
    while True:event = r.display.next_event()if event.type == X.KeyPress:print("Key pressed")
    

3.3 選擇合適的捕獲方式

需求推薦方法
低級輸入監控(掃描碼)內核 input_event 或者 input_handle_event API
監聽所有鍵盤事件讀取 /dev/input/eventX
監聽終端輸入讀取 /dev/tty
GUI 應用按鍵捕獲Xliblibinput

不同場景下,可以選擇合適的方式進行鍵盤輸入捕獲。

本文將演示通過 hook 內核函數 input_handle_event 實現鍵盤記錄功能。


4. 代碼詳解

4.1 bpf代碼

4.1.1 整體邏輯
  • 加載入口: 代碼通過SEC("kprobe/input_handle_event")掛載到內核的input_handle_event函數。
  • 事件過濾: 判斷傳入的type是否為EV_KEYvalue是否等于1,只在按鍵按下時進行處理。
  • 數據發送: 如果滿足條件且按鍵碼小于MAX_KEYS,則利用bpf_perf_event_output將事件數據發送到用戶空間。
4.1.2 代碼細節解析
  • 頭文件與宏定義:

    • 包含了vmlinux.hbpf_helpers.hbpf_tracing.hbpf_core_read.h等頭文件,保證代碼能調用內核相關的API。
    • 定義了EV_KEYMAX_KEYS,分別代表按鍵事件類型和允許的最大按鍵數量。
  • 事件結構體:

    struct event_t {u32 type;u32 code;u32 value;
    };
    

    該結構體用于保存按鍵事件數據,包括事件類型、按鍵碼和按鍵狀態。

  • kprobe函數:

    SEC("kprobe/input_handle_event")
    int BPF_KPROBE(hook_input_handle_event, struct input_dev *dev, unsigned int type, unsigned int code, int value)
    {struct event_t event = { 0, };event.type = type;event.code = code;event.value = value;if (type == EV_KEY && value == 1){if(code < MAX_KEYS) {bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));} }return 0;
    }
    
    • 整體邏輯: 在每次input_handle_event調用時,將輸入數據封裝到event中;判斷條件滿足時,將事件通過perf event發送。
    • 細節說明:
      • SEC("kprobe/input_handle_event"):聲明該函數為kprobe處理函數。
      • BPF_KPROBE宏用于自動生成探針入口。
      • bpf_perf_event_output負責將數據傳遞到用戶空間,不涉及錯誤處理代碼。

4.2 go代碼

4.2.1 main.go
整體邏輯
  • 信號注冊: 利用signal.NotifyContext注冊系統信號(如SIGINTSIGTERM),確保程序能優雅退出。
  • 加載BPF程序: 調用BpfLoadAndAttach加載并附加BPF程序文件bpf.o
  • 創建perf buffer: 初始化perf buffer,通過eventsChannellostChannel接收事件數據及丟失事件統計。
  • 事件處理: 啟動一個事件處理循環,實時解析并打印鍵盤事件,直到收到退出信號。
代碼細節解析
  • 日志設置: 使用logrus設置日志級別和時間格式,使調試信息更直觀。
  • 資源管理: 利用defer語句確保BPF模塊和perf buffer在程序結束時被正確關閉。
  • 事件循環:
    for {// 循環接收事件select {// 接收到事件時打印事件信息case data := <-eventsChannel:var event Eventerr := event.Parse(data)if err != nil {log.Printf("parse event error: %v", err)} else {log.Println(event.String())}}
    }
    
    該循環實時響應來自內核空間的事件數據。

4.2.2 event.go
整體邏輯
  • 數據結構: 定義了Event結構體,用于與內核傳來的event_t數據一一對應。
  • 數據解析: Parse方法利用binary.Read將接收到的字節流轉化為結構體數據。
  • 數據展示: String方法調用keyStr函數,將按鍵碼轉換為對應的按鍵名稱,便于直觀展示。
代碼細節解析
package mainimport ("bytes""encoding/binary"
)type Event struct {Type  uint32Code  uint32Value uint32
}// 解析event數據
func (e *Event) Parse(data []byte) error {err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, e)if err != nil {return err}return nil
}// 轉換成字符串
func (e *Event) String() string {return keyStr(int(e.Code))
}const MAX_KEYS = 256var keyNames = [MAX_KEYS]string{0: "RESERVED",1: "ESC",2: "1", 3: "2", 4: "3", 5: "4", 6: "5", 7: "6", 8: "7", 9: "8", 10: "9", 11: "0",12: "MINUS", 13: "EQUAL", 14: "BACKSPACE", 15: "TAB",16: "Q", 17: "W", 18: "E", 19: "R", 20: "T", 21: "Y", 22: "U", 23: "I", 24: "O", 25: "P",26: "LEFTBRACE", 27: "RIGHTBRACE", 28: "ENTER", 29: "LEFTCTRL",30: "A", 31: "S", 32: "D", 33: "F", 34: "G", 35: "H", 36: "J", 37: "K", 38: "L", 39: "SEMICOLON",40: "APOSTROPHE", 41: "GRAVE", 42: "LEFTSHIFT", 43: "BACKSLASH",44: "Z", 45: "X", 46: "C", 47: "V", 48: "B", 49: "N", 50: "M", 51: "COMMA", 52: "DOT", 53: "SLASH",54: "RIGHTSHIFT", 55: "KPASTERISK", 56: "LEFTALT", 57: "SPACE",58: "CAPSLOCK", 59: "F1", 60: "F2", 61: "F3", 62: "F4", 63: "F5", 64: "F6", 65: "F7", 66: "F8", 67: "F9", 68: "F10",69: "NUMLOCK", 70: "SCROLLLOCK", 71: "KP7", 72: "KP8", 73: "KP9", 74: "KPMINUS", 75: "KP4", 76: "KP5", 77: "KP6", 78: "KPPLUS",79: "KP1", 80: "KP2", 81: "KP3", 82: "KP0", 83: "KPDOT",85: "ZENKAKUHANKAKU", 86: "102ND", 87: "F11", 88: "F12", 89: "RO", 90: "KATAKANA", 91: "HIRAGANA", 92: "HENKAN", 93: "KATAKANAHIRAGANA", 94: "MUHENKAN",95: "KPJPCOMMA", 96: "KPENTER", 97: "RIGHTCTRL", 98: "KPSLASH", 99: "SYSRQ", 100: "RIGHTALT", 101: "LINEFEED",102: "HOME", 103: "UP", 104: "PAGEUP", 105: "LEFT", 106: "RIGHT", 107: "END", 108: "DOWN", 109: "PAGEDOWN", 110: "INSERT", 111: "DELETE",112: "MACRO", 113: "MUTE", 114: "VOLUMEDOWN", 115: "VOLUMEUP", 116: "POWER", 117: "KPEQUAL", 118: "KPPLUSMINUS", 119: "PAUSE",120: "SCALE", 121: "KPCOMMA", 122: "HANGEUL", 123: "HANJA", 124: "YEN", 125: "LEFTMETA", 126: "RIGHTMETA", 127: "COMPOSE",128: "STOP", 129: "AGAIN", 130: "PROPS", 131: "UNDO", 132: "FRONT", 133: "COPY", 134: "OPEN", 135: "PASTE", 136: "FIND", 137: "CUT",138: "HELP", 139: "MENU", 140: "CALC", 141: "SETUP", 142: "SLEEP", 143: "WAKEUP", 144: "FILE", 145: "SENDFILE", 146: "DELETEFILE", 147: "XFER",148: "PROG1", 149: "PROG2", 150: "WWW", 151: "MSDOS", 152: "COFFEE", 153: "ROTATE_DISPLAY", 154: "CYCLEWINDOWS", 155: "MAIL", 156: "BOOKMARKS",157: "COMPUTER", 158: "BACK", 159: "FORWARD", 160: "CLOSECD", 161: "EJECTCD", 162: "EJECTCLOSECD", 163: "NEXTSONG", 164: "PLAYPAUSE",165: "PREVIOUSSONG", 166: "STOPCD", 167: "RECORD", 168: "REWIND", 169: "PHONE", 170: "ISO", 171: "CONFIG", 172: "HOMEPAGE", 173: "REFRESH",174: "EXIT", 175: "MOVE", 176: "EDIT", 177: "SCROLLUP", 178: "SCROLLDOWN", 179: "KPLEFTPAREN", 180: "KPRIGHTPAREN", 181: "NEW", 182: "REDO",183: "F13", 184: "F14", 185: "F15", 186: "F16", 187: "F17", 188: "F18", 189: "F19", 190: "F20", 191: "F21", 192: "F22", 193: "F23", 194: "F24",200: "PLAYCD", 201: "PAUSECD", 202: "PROG3", 203: "PROG4", 204: "ALL_APPLICATIONS", 205: "SUSPEND", 206: "CLOSE", 207: "PLAY", 208: "FASTFORWARD",209: "BASSBOOST", 210: "PRINT", 211: "HP", 212: "CAMERA", 213: "SOUND", 214: "QUESTION", 215: "EMAIL", 216: "CHAT", 217: "SEARCH", 218: "CONNECT",219: "FINANCE", 220: "SPORT", 221: "SHOP", 222: "ALTERASE", 223: "CANCEL", 224: "BRIGHTNESSDOWN", 225: "BRIGHTNESSUP", 226: "MEDIA",227: "SWITCHVIDEOMODE", 228: "KBDILLUMTOGGLE", 229: "KBDILLUMDOWN", 230: "KBDILLUMUP", 231: "SEND", 232: "REPLY", 233: "FORWARDMAIL", 234: "SAVE",235: "DOCUMENTS", 236: "BATTERY", 237: "BLUETOOTH", 238: "WLAN", 239: "UWB", 240: "UNKNOWN", 241: "VIDEO_NEXT", 242: "VIDEO_PREV",243: "BRIGHTNESS_CYCLE", 244: "BRIGHTNESS_AUTO", 245: "DISPLAY_OFF", 246: "WWAN", 247: "RFKILL", 248: "MICMUTE",
}func keyStr(code int) string {if code < 0 || code >= MAX_KEYS {return "UNKNOWN"}name := keyNames[code]if name == "" {return "UNKNOWN"}return name
}
  • Parse方法:
    • 通過binary.LittleEndian格式解析數據,確保與內核傳輸的數據格式一致。
  • String方法與鍵值映射:
    • 利用預定義的keyNames數組映射按鍵碼到具體按鍵名稱。
    • keyStr函數通過判斷碼值范圍返回對應的字符串,如果不存在則返回"UNKNOWN"

5. 總結

本文通過Go語言結合libbpfgo演示了如何利用eBPF實現鍵盤記錄功能,具體如下:

  • 實時性: 直接利用內核kprobe捕捉鍵盤事件,無需輪詢,實時性極佳??。
  • 高效性: 通過perf buffer將數據高效傳遞到用戶空間,確保系統性能不受影響。

該方案不僅適用于鍵盤事件監控,還可以拓展到其他系統監控和安全檢測場景。動手實踐后,你也可以根據業務需求調整代碼,實現更多高級功能。


6. 練習題

  1. 按鍵過濾: 修改代碼使其只記錄特定按鍵(如ESCENTER)的事件,并在用戶空間進行特別處理。
  2. 數據統計: 增加功能,對每種按鍵的出現次數進行統計,并定時輸出統計結果。
  3. 擴展應用: 在BPF程序中增加其他類型事件(如鼠標事件)的監控,探索更多內核事件的捕捉方式。

🚀 動手試試吧!遇到問題歡迎留言交流,一起進步!

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

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

相關文章

APB-清華聯合騰訊等機構推出的分布式長上下文推理框架

APB (Accelerating Distributed Long-Context Inference by Passing Compressed Context Blocks acrossGPUs)是清華大學等機構聯合提出的分布式長上下文推理框架。通過稀疏注意力機制和序列并行推理方式&#xff0c;有效解決了大模型處理長文本時的效率瓶頸。APB采用更小的Anch…

數據庫分庫分表介紹

分庫分表是解決數據庫性能瓶頸的常用技術手段&#xff0c;主要用于應對數據量過大、讀寫壓力過高的問題。通過將數據分散到多個數據庫或表中&#xff0c;可以提高系統的擴展性和性能。 1. 分庫分表的核心概念 &#xff08;1&#xff09;分庫 定義&#xff1a;將數據分散到多個…

#mapreduce打包#maven:could not resolve dependencies for project

打包報錯&#xff1a; #報錯信息&#xff1a; [ERROR] Failed to execute goal on project mapreduce_teacher1: Could not resolve dependencies for project org.example:mapreduce_teacher1:jar:1.0-SNAPSHOT: Failed to collect dependencies at org.apache.hive:hive-exe…

Rabit

之前發過rabit了&#xff0c;所以這里不再贅述&#xff0c;講講原理 在線Rabbit加密 | Rabbit解密- 在線工具 (sojson.com) rabbit加密原理 Rabbit加密算法是一種流密碼算法&#xff0c;由Daniel J. Bernstein設計&#xff0c;并被廣泛用于多種加密和安全通信應用中。它的設…

【A2DP】深入解讀A2DP中通用訪問配置文件(GAP)的互操作性要求

目錄 一、模式支持要求 1.1 發現模式 1.2 連接模式 1.3 綁定模式 1.4 模式間依賴關系總結 1.5 注意事項 1.6 協議設計深層邏輯 二、安全機制&#xff08;Security Aspects&#xff09; 三、空閑模式操作&#xff08;Idle Mode Procedures&#xff09; 3.1 支持要求 …

模型蒸餾系列——開源項目

推薦項目&#xff1a;MiniMind&#xff08;低成本全流程訓練框架&#xff09; GitHub&#xff1a;https://github.com/jingyaogong/minimind 核心特性&#xff1a;完整實現從數據清洗到模型部署的全流程&#xff0c;支持單卡低成本訓練&#xff0c;代碼全透明&#xff0c;適合…

【軟考-架構】13.1、軟件架構概述-構件技術

?資料&文章更新? GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目錄 ?【重點】系統架構設計軟件架構概述軟件架構設計與生命周期構件&#x1f31f;軟件架構風格數據流風格調用/返回風格獨立構件風格虛擬機風格倉庫風格閉環控制風格C2體系結…

《Android啟動偵探團:追蹤Launcher啟動的“最后一公里”》

1. 開機儀式的“黑屏懸案” 當Android設備完成開機動畫后&#xff0c;某些產品會陷入詭異的“黑屏時刻”——仿佛系統在玩捉迷藏。此時&#xff0c;**Launcher&#xff08;桌面&#xff09;**就是躲貓貓的主角。我們的任務&#xff1a;揪出Launcher何時完成啟動&#xff0c;終…

Redis事務與管道

Redis事務 可以一次執行多個命令&#xff0c;本質是一組命令的集合。一個事務中的所有命令都會序列化&#xff0c;按順序地串行執行而不會被其他命令插入&#xff0c;不許加塞。 一個隊列中&#xff0c;一次性、順序性、排他性的執行一系列命令。 Redis事務VS數據庫事務 常用…

掌握這些 UI 交互設計原則,提升產品易用性

在當今數字化時代&#xff0c;用戶對于產品的體驗要求越來越高&#xff0c;UI 交互設計成為決定產品成敗的關鍵因素之一。一個易用的產品能夠讓用戶輕松、高效地完成各種操作&#xff0c;而實現這一目標的核心在于遵循一系列科學合理的 UI 交互設計原則。本文將詳細闡述簡潔性、…

Alembic 實戰指南:快速入門到FastAPI 集成

一、快速開始 1.1 簡介 Alembic 是一個基于 SQLAlchemy 的數據庫遷移工具&#xff0c;主要用于管理數據庫模式&#xff08;Schema&#xff09;的變更&#xff0c;例如新增表、修改字段、刪除索引等&#xff0c;確保數據庫結構與應用程序的 ORM 模型保持一致。 Alembic 通過版…

LRU(最近最少使用)算法實現

核心思想與基本思路 LRU&#xff08;Least Recently Used&#xff09;算法是一種緩存淘汰策略&#xff0c;其核心思想是淘汰最近最少使用的數據。 最近使用原則&#xff1a;最近被訪問的數據在未來被訪問的概率更高&#xff0c;因此應保留在緩存中。淘汰機制&#xff1a;當緩…

現在有分段、句子數量可能不一致的中英文文本,如何用python實現中英文對照翻譯(即每行英文對應相應的中文)

以下是處理分段且中英文句子數量可能不一致的文本的Python實現方案&#xff0c;包含分句、翻譯和對齊功能&#xff1a; from googletrans import Translator import redef split_paragraphs(text):"""按空行分割段落并清洗"""return [p.strip()…

C語言每日一練——day_8

引言 針對初學者&#xff0c;每日練習幾個題&#xff0c;快速上手C語言。第八天。&#xff08;連續更新中&#xff09; 采用在線OJ的形式 什么是在線OJ&#xff1f; 在線判題系統&#xff08;英語&#xff1a;Online Judge&#xff0c;縮寫OJ&#xff09;是一種在編程競賽中用…

基礎知識《Redis解析》

Redis 詳細解析與介紹 Redis&#xff08;Remote Dictionary Server&#xff09;是一個開源的高性能鍵值對&#xff08;Key-Value&#xff09;數據庫&#xff0c;支持多種數據結構&#xff08;如字符串、哈希、列表、集合等&#xff09;&#xff0c;廣泛應用于緩存、消息隊列、…

區跨鏈知識和概念

1、以太坊 Geth 源碼解析 Geth&#xff08;Go Ethereum&#xff09;是以太坊官方提供的 Go 語言實現的客戶端&#xff0c;廣泛用于以太坊全節點運行、挖礦、DApp 開發等。理解 Geth 的源碼有助于掌握以太坊區塊鏈底層邏輯&#xff0c;如區塊同步、EVM 執行、P2P 交互等。 2、…

Vue 計算屬性與 Data 屬性同名問題深度解析

文章目錄 1. 問題背景與核心概念1.1 Vue 響應式系統架構1.2 核心概念定義 2. 同名問題的技術分析2.1 同名場景示例2.2 問題發生機制 3. 底層原理剖析3.1 Vue 初始化流程3.2 響應式系統關鍵代碼 4. 問題解決方案4.1 最佳實踐建議4.2 錯誤處理機制 5. 性能影響分析5.1 遞歸調用性…

Mybatis——基礎操作、動態SQL

目錄 一.基礎操作 1.刪除 2.新增 3.更新 4.查詢 5.XML映射文件 二、動態SQL 1.<if> 2.<where> 3.<set> 4.<foreach> 5.<sql> 6.<include> 一.基礎操作 1.刪除 參數占位符&#xff1a; 注意&#xff1a; #{...}相比于${...}…

[設計模式]1_設計模式概覽

摘要&#xff1a;設計模式原則、設計模式的劃分與簡要概括&#xff0c;怎么使用重構獲得設計模式并改善代碼的壞味道。 本篇作概覽與檢索用&#xff0c;后續結合源碼進行具體模式深入學習。 目錄 1、設計模式原理 核心原則&#xff08;語言無關&#xff09; 本質原理圖 原…

C語言數據類型取值范圍及格式化符號

一、數據類型取值范圍與格式化輸出符號表格 數據類型大小&#xff08;字節&#xff09;取值范圍格式化輸出符號char1-128到127<br>或0到255&#xff08;如果聲明為unsigned char&#xff09;%c (字符)<br>%hhu (無符號)signed char1-1.2810到1.2710%hhd (有符號)u…