Linux 詳細介紹strace命令

system call(系統調用)是程序向內核請求服務的一種編程方式,strace是一個功能強大的工具,可以跟蹤用戶進程和 Linux 內核之間的交互。

要了解操作系統如何工作,首先需要了解系統調用如何工作。操作系統的主要功能之一是為用戶程序提供了一個抽象。

操作系統大致可以分為兩種模式:

內核模式(Kernel mode:):操作系統內核使用的特權且強大的模式
用戶模式(User mode):大多數用戶應用程序運行的地方
用戶主要使用命令行程序和圖形用戶界面 (GUI) 來完成日常任務。系統調用在后臺默默工作,與內核交互以完成工作。

system call(系統調用)與function call(函數調用)非常相似,都接受并處理參數和返回值。唯一的區別是system call進入內核,而function call則不進入內核。從用戶空間切換到內核空間是使用特殊的trap機制完成的。

下面將通過一些通用命令來使用 strace 分析每個命令進行的系統調用,并探索一些實際示例。 范例將使用 Red Hat Enterprise Linux,這些命令在其他 Linux 發行版上的工作方式應該也相同:

[root@sandbox ~]# cat /etc/redhat-release 
Red Hat Enterprise Linux Server release 7.7 (Maipo)
[root@sandbox ~]# 
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]# 

首先,確保系統上安裝了所需的工具。 使用下面的 RPM 命令驗證是否安裝了 strace; 并使用 -V 選項檢查 strace 實用程序版本號:

[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]# 
[root@sandbox ~]# strace -V
strace -- version 4.12
[root@sandbox ~]# 

如果沒有安裝,使用如下命令安裝

yum install strace

基于演示的目的,在 /tmp 中創建一個測試目錄,并使用 touch 命令創建兩個文件:

[root@sandbox ~]# cd /tmp/
[root@sandbox tmp]# 
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]# 
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#

我使用 /tmp 目錄是因為每個人都可以訪問它,其實也可以選擇其他目錄。)

驗證文件是否是在 testdir 目錄上使用 ls 命令創建的:

[root@sandbox tmp]# ls testdir/
file1  file2
[root@sandbox tmp]#

可能每天都使用 ls 命令,但其實他的工作是基于到系統調用之上。它的工作原理如下:
Command-line utility -> Invokes functions from system libraries (glibc) -> Invokes system calls
ls 命令在 Linux 上內部調用系統庫(又名 glibc)中的函數。 這些庫調用完成大部分工作的系統調用。
如果想知道從 glibc 庫調用了哪些函數,可以使用 ltrace 命令,后跟常規 ls testdir/ 命令:

ltrace ls testdir/

如果沒有安裝,可以使用如下命令安裝

yum install ltrace

一堆輸出將被轉儲到屏幕上; 不用擔心——只要跟著做就可以了。 ltrace 命令輸出中與本示例相關的一些重要庫函數包括:

opendir("testdir/")                                  = { 3 }
readdir({ 3 })                                       = { 101879119, "." }
readdir({ 3 })                                       = { 134, ".." }
readdir({ 3 })                                       = { 101879120, "file1" }
strlen("file1")                                      = 5
memcpy(0x1665be0, "file1\0", 6)                      = 0x1665be0
readdir({ 3 })                                       = { 101879122, "file2" }
strlen("file2")                                      = 5
memcpy(0x166dcb0, "file2\0", 6)                      = 0x166dcb0
readdir({ 3 })                                       = nil
closedir({ 3 })                       

通過查看上面的輸出,可能可以理解發生了什么。 opendir 庫函數正在打開一個名為 testdir 的目錄,然后調用 readdir 函數來讀取該目錄的內容。 最后,調用 closedir 函數,該函數關閉之前打開的目錄。 暫時忽略其他 strlen 和 memcpy 函數。

可以看到正在調用哪些庫函數,但本文將重點討論由系統庫函數調用的系統調用。

與上面類似,要了解調用了哪些系統調用,只需將 strace 放在 ls testdir 命令之前即可,如下所示。 再次,一堆亂碼將被轉儲到您的屏幕上

[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL)                               = 0x1f12000
<<< truncated strace output >>>
write(1, "file1  file2\n", 13file1  file2
)          = 13
close(1)                                = 0
munmap(0x7fd002c8d000, 4096)            = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
[root@sandbox tmp]# 

運行 strace 命令后屏幕上的輸出只是為運行 ls 命令而進行的系統調用。 每個系統調用都有特定的操作系統用途,它們可以大致分為以下幾部分:
1.Process management system calls
2.File management system calls
3.Directory and filesystem management system calls
4.Other system calls

分析轉儲到屏幕上的信息的一種更簡單的方法是使用 strace 的 -o 選項將輸出記錄到文件中。 在 -o 標志后添加合適的文件名并再次運行命令:

[root@sandbox tmp]# strace -o trace.log ls testdir/
file1  file2
[root@sandbox tmp]#

這次,沒有輸出轉儲到屏幕上 - ls 命令按預期工作,顯示文件名并將所有輸出記錄到文件 trace.log 中。 僅一個簡單的 ls 命令,該文件就有近 100 行內容:

[root@sandbox tmp]# ls -l trace.log 
-rw-r--r--. 1 root root 7809 Oct 12 13:52 trace.log
[root@sandbox tmp]# 
[root@sandbox tmp]# wc -l trace.log 
114 trace.log
[root@sandbox tmp]# 

看一下示例的trace.log 中的第一行:

execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0

1.該行的 execve 是正在執行的系統調用的名稱。
2.括號內的文本是提供給系統調用的參數。
3.= 符號后面的數字(在本例中為 0)是 execve 系統調用返回的值。

這只是范本解釋, 可以應用相同的邏輯來理解其他行。

現在,將注意力集中到調用的單個命令,即 ls testdir。 知道命令 ls 使用的目錄名稱,grep testdir trace.log 詳細查看每一行結果:

[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[root@sandbox tmp]# 

回顧一下上面對execve的分析,能看出這個系統調用是做什么的嗎?

execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0

不需要記住所有的系統調用或它們的作用,因為您可以在需要時參考文檔。 手冊頁來救援! 在運行 man 命令之前確保安裝了以下軟件包:

[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]# 

請記住,需要在 man 命令和需要查詢的系統調用名稱之間添加 2。 如果使用 man man 閱讀 man 的手冊頁,可以看到第 2 部分是為系統調用保留的。 同樣,如果需要庫函數的信息,則需要在man和庫函數名之間添加3。

[root@sandbox tmp]# man man1   Executable programs or shell commands2   System calls (functions provided by the kernel)3   Library calls (functions within program libraries)4   Special files (usually found in /dev)5   File formats and conventions eg /etc/passwd6   Games7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)8   System administration commands (usually only for root)9   Kernel routines [Non standard]

以下 man 命令查看上面范例中涉及的execve系統調用的文檔:

man 2 execve

輸出如下:

EXECVE(2)                  Linux Programmer’s Manual                 EXECVE(2)DESCRIPTIONexecve()  executes  the  program  pointed to by filename.  filename must be either a binary executable, or a script starting with a line of the form "#! interpreter [arg]".  In the latter case, the interpreter must be avalid pathname for an executable which is not itself a script, which will be invoked as interpreter [arg] filename

根據 execve 手冊頁說名,這個execve系統調用會執行一個作為參數傳入的程序(在本例中為 ls)。 還可以向 ls 提供其他參數,例如本示例中的 testdir。 因此,該系統調用僅以 testdir 作為參數運行 ls:

下一個名為 stat 的系統調用使用 testdir 參數:

stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0

使用 man 2 stat 訪問文檔。 stat 是獲取文件狀態的系統調用 - 請記住,Linux 中的所有內容都是文件,包括目錄。

接下來,openat 系統調用打開 testdir。 留意返回的 3。 這是文件描述,后面的系統調用會用到:

openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3

到目前為止,一切都很好。 現在,打開trace.log 文件并轉到openat 系統調用后面的行。 將看到 getdents 系統調用被調用,它執行執行 ls testdir 命令所需的大部分操作。 現在,從trace.log 文件中grep getdents:

[root@sandbox tmp]# grep getdents trace.log 
getdents(3, /* 4 entries */, 32768)     = 112
getdents(3, /* 0 entries */, 32768)     = 0
[root@sandbox tmp]# 

man getdents,我們將得知getdents = get directory entries,這里注意,getdents 的參數是 3,它是上面 openat 系統調用中的文件描述符。

現在您已經有了目錄列表,需要一種在終端中顯示它的方法。 因此,grep 另一個系統調用 write,用于在將輸出的內容寫入終端:

[root@sandbox tmp]# grep write trace.log
write(1, "file1  file2\n", 13)          = 13
[root@sandbox tmp]# 

在這些參數中,您可以看到將顯示的文件名:file1 和 file2。 關于第一個參數(1),請記住在 Linux 中,當任何進程運行時,默認情況下會為其打開三個文件描述符。 以下是默認的文件描述符:
0 - Standard input
1 - Standard out
2 - Standard error

因此,write 系統調用正在"1 - Standard out"上顯示 file1 和 file2,默認將輸出到顯示屏幕上,由 1 標識。

現在知道哪些系統調用完成了 ls testdir/ 命令的大部分工作。 但是trace.log 文件中的其他100 多個系統調用又如何呢? 操作系統必須執行大量內部工作才能運行進程,因此在日志文件中看到的很多內容都是initialization 和cleanup。 閱讀整個trace.log 文件并嘗試了解發生了什么使ls 命令正常工作。

現在知道如何分析給定命令的系統調用,可以將此知識用于其他命令來了解正在執行哪些系統調用。 strace 提供了許多有用的命令行選項,使strace過程更輕松,下面介紹了其中一些選項。

默認情況下,strace 不包含所有系統調用信息。 但是,它有一個方便的 -v verbose 選項,可以提供有關每個系統調用的附加信息:

strace -v ls testdir

運行 strace 命令時始終使用 -f 選項是一個很好的做法。 它允許 strace 跟蹤當前正在跟蹤的進程創建的任何子進程:

strace -f ls testdir

假設只需要系統調用的名稱、它們運行的次數以及每個系統調用所花費的時間百分比。 您可以使用 -c 選項來獲取這些統計信息:

strace -c ls testdir/

假設想專注于特定的系統調用,例如專注于open系統調用而忽略其余的。 您可以使用 -e 選項后跟系統調用名稱:

[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1  file2
+++ exited with 0 +++
[root@sandbox tmp]# 

如果您想專注于多個系統調用,可以使用相同的 -e 命令行選項,并在兩個系統調用之間使用逗號。 例如,要查看 write 和 getdents 系統調用:

[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 entries */, 32768)     = 112
getdents(3, /* 0 entries */, 32768)     = 0
write(1, "file1  file2\n", 13file1  file2
)          = 13
+++ exited with 0 +++
[root@sandbox tmp]# 

到目前為止的示例都是顯式地跟蹤運行的命令。 但是那些已經運行并且正在執行的命令呢? 例如,如果想跟蹤只是長時間運行的進程的daemon(守護進程),該怎么辦? 為此,strace 提供了一個特殊的 -p 選項,可以向其提供進程 ID。

替代在一個守護進程上運行strace命令,這里我們以cat 命令為例進行演示,如果提供文件名給cat命令作為參數,這個命令通常會顯示文件的內容。 如果沒有給出參數,cat 命令只是在終端等待用戶輸入文本。 輸入文本后,它會重復給定的文本,直到用戶按 Ctrl+C 退出。

從一個終端運行 cat 命令; 它會顯示一個提示,然后只需等待(記住 cat 仍在運行并且尚未退出):

[root@sandbox tmp]# cat

從另一個終端,使用 ps 命令查找進程標識符 (PID):

[root@sandbox ~]# ps -ef | grep cat
root      22443  20164  0 14:19 pts/0    00:00:00 cat
root      22482  20300  0 14:20 pts/1    00:00:00 grep --color=auto cat
[root@sandbox ~]# 

現在,使用 -p 選項和 PID(上面使用 ps 找到的)對正在運行的進程運行 strace。 運行 strace 后,輸出會顯示進程所附加的內容以及 PID 號。 現在,strace 正在跟蹤 cat 命令發出的系統調用。 您看到的第一個系統調用是 read,它正在等待來自 0 或標準輸入的輸入,這是運行 cat 命令的終端:

[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0,

現在,返回到運行 cat 命令的終端并輸入一些文本。 我輸入 x0x0 是出于演示目的。 請注意 cat 是如何簡單地重復我輸入的內容的; 因此,x0x0 出現兩次。 我輸入第一個,第二個是 cat 命令重復的輸出:

[root@sandbox tmp]# cat
x0x0
x0x0

返回到 strace 連接到 cat 進程的終端。 現在會看到兩個額外的系統調用:之前的 read 系統調用,現在在終端中讀取 x0x0,另一個用于 write,它將 x0x0 寫回終端,還有一個新的 read,它正在等待從終端讀取。 請注意,標準輸入 (0) 和標準輸出 (1) 均位于同一終端中:

[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0, "x0x0\n", 65536)                = 5
write(1, "x0x0\n", 5)                   = 5
read(0, 

想象一下,當對守護進程運行 strace 以查看它在后臺執行的所有操作時,這有多么有用。 按 Ctrl+C 退出cat 命令; 這也將會終止strace 會話,因為該進程不再運行。
如果想查看所有系統調用的時間戳,只需將 -t 選項與 strace 結合使用即可:

14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
14:24:47 brk(NULL)                      = 0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

如果想知道系統調用之間所花費的時間, strace 有一個方便的 -r 命令,可以顯示執行每個系統調用所花費的時間。 非常有用

[root@sandbox ~]#strace -r ls testdir/
0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
0.000368 brk(NULL)                 = 0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

結論
strace 實用程序對于理解 Linux 上的系統調用非常方便。 要了解其其他命令行選項,可參閱手冊頁和在線文檔。

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

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

相關文章

HJ94 記票統計

題目&#xff1a; HJ94 記票統計 題解&#xff1a; 利用哈希表&#xff0c;投票是按姓名從哈希表中取出對應的票數&#xff0c;如果不在哈希表內證明為無效。 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);int n Inte…

PyQt6 QTimeEdit時間控件

?鋒哥原創的PyQt6視頻教程&#xff1a; 2024版 PyQt6 Python桌面開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili2024版 PyQt6 Python桌面開發 視頻教程(無廢話版) 玩命更新中~共計39條視頻&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面開發 視頻教程(無廢話…

C++新經典模板與泛型編程:將trait類模板用作模板參數

將trait類模板用作模板參數 template<typename T> struct SumFixedTraits;template<> struct SumFixedTraits<char> {using sumT int;static sumT initValue() {return 0;} };template<> struct SumFixedTraits<int> {using sumT __int64;sta…

Proteus仿真--基于DAC0808設計的直流電機調速器

本文介紹基于DAC0808設計的直流電機調速器設計&#xff08;完整仿真源文件及代碼見文末鏈接&#xff09; 設置按鍵A-H按鍵&#xff0c;每個按鍵分別對應不同的速度&#xff0c;按下后電機按照設定速度轉動 仿真圖如下 仿真運行視頻 Proteus仿真--基于DAC0808設計的直流電機調…

互聯網數據傳輸原理 |OSI七層網絡參考模型

網絡模型 OSI 網絡參考模型&#xff0c;僅作為參考&#xff0c;也就是說OSI網絡實際中并不使用。我們只是把OSI網絡模型作為參考&#xff0c;在網絡出現問題的時候&#xff0c;可以從一個宏觀的整體去分析和解決問題。而且搭建網絡的時候也并不一定需要劃分為7層 但是當今互聯…

【uniapp】小程序中input輸入框的placeholder-class不生效解決辦法

問題描述 uniapp微信小程序&#xff0c;使用input組件時&#xff0c;想要改變提示詞 placeholder 的樣式&#xff0c;但是使用placeholder-class 改變不了 如下&#xff1a; <input type"text" placeholder"搜索" placeholder-class"placeholde…

2024最新金三銀四軟件測試面試題

一直以來大大小小參與過不少面試&#xff0c;遇到過不少坑&#xff0c;但是沒來的及好好總結匯總下。現在把之前遇到的問題匯總下&#xff0c;希望以后自己能加深印象。 1、appium 怎么定位toast彈框 appium1.6以后回答需要升級u2進行定位。 2、什么是事務&#xff0c;知道事…

PADS9.5封裝庫轉換為AD庫

1、打開PADS Layout&#xff0c;File – Library&#xff0c;選中usr&#xff0c;如下圖&#xff1a; 2、封裝– 導入&#xff0c;選中你的 .d后綴文件(也就是PADS的封裝文件)&#xff0c;打開。 3、元件 – 新建 – PCB封裝 - 分配 - 確定。 4、&#xff0c;選擇“斜線”…

Laya2.13.3接入第三方庫Socket.io

服務端&#xff1a; 1.新建一個文件夾&#xff0c;使用npm.init -y創建node工程 2.在控制臺使用以下代碼下載Socket.io npm install socket.io 3.創建一個app.js的文件&#xff0c;將以下代碼填入 import { Server } from "socket.io"; import { createServer }…

Linux學習筆記3 xshell(lnmp)

xshell能連接虛擬機的前提是真機能夠ping通虛擬機網址 裝OpenSSL依賴文件 [rootlocalhost nginx-1.12.2]# yum -y install openssl pcre-devel 依賴檢測[rootlocalhost nginx-1.12.2]# ./configure [rootlocalhost nginx-1.12.2]# yum -y install zlib [rootlocalhost n…

【騰訊云 HAI域探秘】StableDiffusionWebUI 讓我找到了宮崎駿動漫里的夏天

目錄 前言一、HAI二、應用場景三、構建 Stable Diffusion 模型1、新建HAI應用2、StableDiffusionWebUI&#xff08;1&#xff09;功能介紹&#xff08;2&#xff09;頁面轉中文&#xff08;3&#xff09;AI繪圖① 正向提示詞語② 反向提示詞③ “” 、“ AND”、“|” 用法④ 權…

自定義函數參數傳遞問題

最近&#xff0c;被一個函數調用參數傳遞的問題困惑了一陣。自己寫的解釋程序&#xff0c;一直用的好好的。在暗自得意的過程中&#xff0c;突然出現了bug&#xff0c;被潑了一頭冷水。當然&#xff0c;bug是在無意中被發現的&#xff0c;確定以后則可以編制專用的代碼來揭示它…

重積分的應用@物體對外部質點的引力問題

文章目錄 引力(*)分析兩質點間的引力公式三重積分計算引力薄片情形計算例 引力(*) 這里討論的是:空間一物體對于物體外一點 P 0 ( x 0 , y 0 , z 0 ) P_{0}(x_0,y_0,z_0) P0?(x0?,y0?,z0?)處單位質量的質點的引力 分析 仍然使用元素法, 設占有空間有界閉區域 Ω \Omega …

網絡協議與 IP 編址

網絡協議與 IP 編址 之前大概了解過了網絡的一些基礎概念&#xff0c;見文章&#xff1a; 網絡基礎概念。 之前簡單了解OSI模型分層&#xff1a; TCP/IP模型OSI模型TCP/IP對等模型應用層應用層表示層應用層會話層主機到主機層傳輸層傳輸層因特網層網絡層網絡層網絡接入層數據鏈…

jsonwebtoken生成token和解析

先上npm地址 jsonwebtoken&#xff1a;jsonwebtoken - npm express-jwt&#xff1a;express-jwt - npmps const express require(express); const jwt require(jsonwebtoken); const { expressjwt: expressJWT} require(express-jwt)const app express();// 設置密鑰 co…

愛智EdgerOS之深入解析數據庫模塊的Redis Client

一、Redis 簡介 無論是什么類型的應用&#xff0c;都少不了和數據打交道。尤其是一些復雜的應用場景&#xff0c;都少不了一個高效可靠的數據庫。例如日常開發中最常見的 MySQL 等關系型數據庫&#xff0c;讓數據的存儲、檢索輕松簡單起來&#xff0c;甚至可以輕松地處理百萬量…

51單片機c語言燒錄軟件,51單片機燒寫程序的方法

STC89C51是應用廣泛的51單片機&#xff0c;很多人都是通過該單片機入門學習的&#xff0c;單片機的學習需要勤動手。單片機需要燒寫程序&#xff0c;要用到相關的軟件和硬件。下面介紹一下51單片機燒寫程序的方法。 1 所用到的硬件工具 51單片機燒寫程序需要用到單片機的UART…

C語言數組(下)

我希望各位可以在思考之后去看本期練習&#xff0c;并且在觀看之后獨立編寫一遍&#xff0c;以加深理解&#xff0c;鞏固知識點。 練習一&#xff1a;編寫代碼&#xff0c;演?多個字符從兩端移動&#xff0c;向中間匯聚 我們依舊先上代碼 //編寫代碼&#xff0c;演?多個字…

數據庫Delete的多種用法

數據庫的Delete操作是用來刪除數據庫中的數據記錄的&#xff0c;它是數據庫操作中的一種重要操作&#xff0c;能夠幫助用戶刪除不需要的數據&#xff0c;以便保持數據庫的整潔和高效。在使用Delete操作時&#xff0c;需要注意確保操作的準確性和安全性&#xff0c;以免誤刪重要…

基于 Gin 的 HTTP 中間人代理 Demo

前面實現的代理對于 HTTPS 流量是進行盲轉的&#xff0c;也就是說直接在 TCP 連接上傳輸 TLS 流量&#xff0c;但是我們無法查看或者修改它的內容。當然了&#xff0c;通常來說這也是不必要的。不過對于某些場景下還是有必要的&#xff0c;例如使用 Fiddler 進行抓包或者監控其…