基於 Bochs 的作業系統核心實現
簡介
Bochs 簡介
Bochs(讀音Box)是一個開源的模擬器(Emulator),它可以完全模擬x86/x64的硬體以及一些外圍裝置。與VirtualBox / VMware等虛擬機器(Virtual Machine)產品不同,它的設計目標在於模擬一臺真正的硬體,並不追求執行速度的高效,而追求模擬環境的真實,同時帶有強大的除錯功能,比如觀察暫存器、對實地址/虛擬地址下斷點、裝載符號表等等。對於作業系統核心的開發者而言,是一隻不可多得的強力工具,通過簡單的設定,即可大大地降低核心開發與除錯的困難。
作為開源軟體,我們可以很方便地獲取它:
安裝
在Ubuntu作業系統下,可以通過apt-get來安裝:
sudo apt-get install bochs
若要利用Bochs的除錯功能,則需要自己編譯安裝:
wget http://sourceforge.net/projects/bochs/files/bochs/2.5.1/bochs-2.5.1.tar.gz/download -O bochs.tar.gz tar -xvfz bochs.tar.gz cd bochs-2.5.1 ./configure --enable-debugger --enable-debugger-gui --enable-disasm --with-x --with-term make sudo cp ./bochs /usr/bin/bochs-dbg
配置
Bochs 提供了許多配置選項,在專案中,我們可以靈活的選擇/設定自己所需的功能,比如模擬器的記憶體大小、軟/硬碟映象以及引導方式等等。而這些配置選項都統一在一個.bochsrc檔案中,樣例如下:
.bochsrc:
# BIOS與VGA映象 romimage: file=/usr/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest # 記憶體大小 megs: 128 # 軟盤映象 floppya: 1_44=bin/kernel.img, status=inserted # 硬碟映象 ata0-master: type=disk, path="bin/rootfs.img", mode=flat, cylinders=2, heads=16, spt=63 # 引導方式(軟盤) boot: a # 日誌輸出 log: .bochsout panic: action=ask error: action=report info: action=report debug: action=ignore # 雜項 vga_update_interval: 300000 keyboard_serial_delay: 250 keyboard_paste_delay: 100000 mouse: enabled=0 private_colormap: enabled=0 fullscreen: enabled=0 screenmode: name="sample" keyboard_mapping: enabled=0, map= keyboard_type: at # 符號表(除錯用) debug_symbols: file=main.sym # 鍵盤型別 keyboard_type: at
在啟動bochs時,使用命令:
bochs -q -f .bochsrc
內建偵錯程式
bochs內建了強大且方便的除錯功能。主要命令如下:
-
b
,vb
,lb
分別為實體地址、虛擬地址、邏輯地址設定斷點 -
c
持續執行,直到遇到斷點或者錯誤 -
n
下一步執行 -
step
單步執行 -
r
顯示當前暫存器的值 -
sreg
顯示當前的段暫存器的值 -
info gdt
,info idt
,info tss
,info tab
分別顯示當前的GDT、IDT、TSS、頁表資訊 -
print-stack
列印當前棧頂的值 -
help
顯示幫助
fleurix 簡介
fleurix 是一個簡單的單核心(Monolithic Kernel)作業系統實現,它的功能精簡但不失完整,程式碼簡短(七千行C,二百多行彙編)且易於閱讀,可作為作業系統課程教學中的樣例系統。在設計時選擇採用了類UNIX的系統呼叫介面,因此在開發過程中可以獲取豐富的文件供參考,也可以作為學習UNIX作業系統實現的一個參考材料。
fleurix 在編寫時儘量使用最簡單的方案。它假定CPU為單核心、記憶體固定為128mb,前者可以簡化核心同步機制的實現,後者可以簡化記憶體管理的實現。從技術角度來看,這些假定並不合理,但可以有效地降低剛開始開發時的複雜度。待開發進入軌道,也不難回頭解決。 此外,你也可以在原始碼中發現許多窮舉演算法——在資料量較小的前提下,窮舉並不是太糟的解決方案。
- 開發環境: Ubuntu
- 平臺:x86
-
依賴:
bochs
,rake
,binutils
,nasm
,mkfs.minix
特性
- minix v1的檔案系統。原理簡單,而且可以利用linux下的mkfs.minix,fsck.minix等工具。
-
fork()
/exec()
/exit()
等系統。可執行檔案格式為a.out,實現了寫時複製與請求調頁。 - 訊號。
- 一個純分頁的記憶體管理系統,每個程序4gb的地址空間,共享128mb的核心地址空間。至少比Linux0.11中的段頁式記憶體管理方式更加靈活。
-
一個簡單的
kmalloc()
。 - 一個簡單的終端。
編譯執行
git clone [email protected]:Fleurer/fleurix.git
cd fleurix
rake
除錯
# 需要自行編譯安裝帶除錯功能的bochs-dbg,安裝步驟參見前文。
cd fleurix
rake debug
設計與實現
編譯與連結
fleurix的核心映象為裸的二進位制檔案,結構大體如下:
(補圖)
Rakefile
對於專案中的一些日常性質操作,比如:
- 編譯bootloader,生成引導映象
- 編譯並連結核心,生成核心映象
- 生成符號表
- 初始化根檔案系統,生成硬碟映象
- 編譯整個專案,並執行bochs進行除錯
它們需要的命令比較多,而且存在依賴關係,此任務必須在確保彼任務執行完畢併成功之後才可以執行。對此,比較通用的解決方案便是make,它可以自動分析任務之間的依賴關係再依次執行,從而簡化日常操作的指令碼編寫。但是make的語法比較晦澀,對於沒有任何基礎的初學者來講,上手起來並不容易。為此fleurix選擇了rake,它相當於make的ruby實現,可以使用ruby語言的語法來編寫make指令碼,好處是易於上手,而代價是不如make的語法簡潔。
fleurix中常用的rake命令有:
-
rake
或者rake bochs
,構建整個專案並執行bochs -
rake build
,構建整個專案到/bin
目錄 -
rake debug
,構建整個專案並執行bochs的偵錯程式 -
rake clean
,將/bin
目錄清空 -
rake nm
,生成符號表 -
rake todo
,列出程式碼中遺留的待解決事項 -
rake werr
,開啟gcc的-Werror選項進行編譯,方便排除程式碼中的warning -
rake rootfs
,構建根檔案系統 -
rake fsck
,對根檔案系統執行fsck,檢查結構是否正確
ldscript
核心開發與應用程式開發的不同之一便在於開發者需要對二進位制映象的結構有所瞭解,在必要時必須進行一些重定位。比如核心的入口為0x100000,為此需要將入口的程式碼(bin/entry.o
)安排到核心映象的最前方。而這便可以通過ldscript來完成,如下:
tool/main.ld:
ENTRY(kmain)
SECTIONS {
__bios__ = 0xa0000; # 繫結BIOS保留記憶體的地址到__bios__
vgamem = 0xb8000; # 繫結vga緩衝區的地址到符號vgamem
.text 0x100000 : { # 核心二進位制映象中的.text段(Section),從0x100000開始
__kbegin__ = .; # 核心映象的開始地址
__code__ = .;
bin/entry.o(.text) bin/main.o(.text) *(.text); # 將bin/entry.o中的.text段安排到核心映象的最前方
. = ALIGN(4096); # .text段按4kb對齊
}
.data : {
__data__ = .;
*(.rodata);
*(.data);
. = ALIGN(4096);
}
.bss : {
__bss__ = .;
*(.bss);
. = ALIGN(4096);
}
__kend__ = .; # 核心映象的結束地址
}
Rakefile中的相關命令如下,在連結時選擇tool/main.ld作為連結指令碼:
sh "ld #{ofiles * ' '} -o bin/main.elf -e c -T tool/main.ld"
bootloader
bootloader是一段小程式,負責執行一些初始化操作,並將核心裝載到記憶體,是核心執行的入口,也是核心開發的第一步。
x86體系結構的CPU在設計中為了保持向前相容,在PC機電源開啟之後,x86平臺的CPU會先進入真實模式(Real Mode),並從0xFFF0開始執行BIOS的一些初始化操作。隨後,BIOS將依次檢測啟動裝置(軟盤或者硬碟)的第一個扇區(512位元組),如果它的第510位元組處的值為0xAA55,則認為它是一個引導扇區,將它裝載到實體地址0x7C00,並跳轉到0x7C00處開始執行。這便是bootloader的入口地址。
真實模式中預設可用的地址匯流排為20位,可以定址1mb的記憶體,但暫存器只有16位。為此英特爾公司做出的設計是,在真實模式的定址模式中,令實體地址為16位段暫存器左移4位加16位邏輯地址的偏移所得的20位地址。若要訪問1mb之後的記憶體,則必須開啟A20 Line開關,將32位地址匯流排開啟,並進入保護模式(Protect Mode)才可以。
在真實模式中,0~4kb為中斷向量表保留,640kb~1mb為視訊記憶體與BIOS保留,實際可用的記憶體只有636kb。考慮到日後核心映象的體積有超過1mb的可能,所以將其裝載到實體地址1mb(0x100000)之後連續的一塊記憶體中可能會更好。但真實模式中並不可以訪問1mb以後的記憶體,若要裝載核心到實體地址1mb,一個解決方案便是在真實模式中暫時將其裝載到一個臨時位置,待進入保護模式之後再移動它。
由上總結可知,bootloader所需要做的工作便依次為:
- 裝載核心映象到一個臨時的地址;
- 進入保護模式;
- 移動核心映象;
- 跳轉到核心的入口。
相關程式碼可見於 src/boot/boot.S
。
保護模式與GDT
x86的保護模式是對段定址的增強,除去可以訪問32位的地址空間(4Gb)之外,更有了對保護級別(即ring0/ring1/ring2/ring3)的劃分、對記憶體區域的限制、以及訪問控制。為實現這些功能,x86的做法是引入了GDT(Global Descriptor Table)。將每個段(Segments)的屬性對應為GDT中的一項段描述符(Segment Descriptor),並通過段暫存器(如cs
、ds
、ss
)中指明的選擇符進行選擇。GDT是駐留於記憶體中的一個表,通過lgdt
指令裝載到CPU。
在bootloader中進入保護模式的目的僅僅是為了訪問1mb以後的記憶體,而且bootloader在完成引導系統之後即被視為廢棄,因此這裡的GDT只能做臨時使用。其中含有兩個段描述符,它們的選擇符分別為0x08與0x10,分別用於核心態程式碼與資料的訪問。
進入核心之後,fleurix會在gdt_init()
中重新設定GDT(見scr/kern/seg.c
)。
fleurix是一個純分頁的系統,雖然並不需要段式的記憶體管理,但依然需要一個GDT,只採用它的記憶體保護功能,而繞過它的分段功能。在fleurix最終的GDT中,將只保留四個段描述符,它們的記憶體區域皆為0~4Gb,選擇符分別為KERN_CS
、KERN_DS
、USER_CS
與USER_DS
——前兩者的許可權為ring0,用於核心態程式碼與資料的訪問;後兩者的許可權為ring3,分別用於使用者態程式碼與資料的訪問——從而實現核心態與使用者態的分離,使後者受到更多限制,將系統“保護”起來。
需要留意的是,除四個段描述符之外,fleurix的GDT中也帶有一個TSS描述符,其選擇符為(_TSS
)。英特爾公司引入TSS機制的動機為實現硬體的任務切換,每個任務擁有一個TSS,在程序切換時,將當前程序的所有上下文儲存在TSS中。比起軟體的任務切換,硬體任務切換的開銷相對比較大,而且沒有除錯與優化的餘地。fleurix採用了軟體的任務切換機制,並無用到TSS的任務切換功能,但依然保留一個TSS是為了儲存中斷處理時ss0與esp0兩個暫存器的值,在CPU通過中斷門或者自陷門轉移控制權時,據此獲取核心棧的位置。
裝載核心
在早期開發中為方便裝載,fleurix核心的二進位制映象被放置在軟盤映象中,自第二個扇區開始,大約為50kb。
在真實模式中,可以通過呼叫13h號中斷來讀取軟盤扇區,將核心映象臨時讀取到實體地址0x10000處。在設定臨時的GDT之後,通過jmp指令進入保護模式,並將核心拷貝至實體地址0x100000(1mb)處。
核心初始化
待bootloader執行完畢之後,核心會首先進入kmain()
(見src/kern/main.c
),執行一些初始化操作。這些操作依次為:
-
清理螢幕(
cls()
,見src/chr/vga.c
),初始化puts()
與printk()
等函式供除錯與輸出使用。 -
重新設定GDT(
gdt_init()
,見src/kern/seg.c
)。 -
初始化IDT(
idt_init()
,見src/kern/trap.c
)。 -
初始化記憶體管理(
mm_init()
,見src/kern/pm.c
)。 -
初始化程序0(
proc0_init()
,見src/kern/proc.c
)。 -
初始化高速緩衝(
buf_init()
,見src/blk/buf.c
)。 -
初始化tty(
tty_init()
,見src/chr/tty.c
)。 -
初始化硬碟驅動(
hd_init()
,見src/blk/hd.c
)。 -
初始化核心定時器(
timer_init()
,見src/kern/timer.c
) -
初始化鍵盤驅動(
keybd_init()
,見src/chr/keybd.c
)。 -
開啟中斷(
sti()
,見src/inc/asm.h
)。 -
初始化程序1(
kspawn(&init)
),通過do_exec()
(見src/kern/exec.c
)即進入使用者態。
中斷處理
中斷是CPU中打斷當前程式的控制流以處理外部事件、報告錯誤或者處理異常的一種機制。若詳細分類,仍可將中斷分為三種:
- 中斷(Interrupt):由CPU外部產生,CPU處於被動的位置,多用於CPU與外部裝置的互動。
-
自陷(Trap):在CPU本身的執行過程中產生。一般由專門的指令有意產生,比如
int $0x80
,因此又被稱作"軟體中斷"。 - 異常(Exception):因CPU執行某指令失敗而產生,如除0、缺頁等等。與自陷的不同在於,CPU會在處理例程結束之後重新執行產生異常的指令。
(注:即,自陷發生時,入棧的返回地址為下一條指令的地址;而異常發生時,入棧的返回地址為當前指令的地址)
在保護模式的x86平臺中,中斷通過中斷門(Interrupt Gate)轉移控制權,自陷與異常通過自陷門(Trap Gate)轉移控制權。
每個中斷對應一箇中斷號,系統開發者可以將自己的中斷處理例程繫結到相應的中斷號,表示中斷號與中斷處理例程之間對映關係的結構被稱作中斷向量表(Interupt Vector Table)。在保護模式中的x86平臺,這一結構的實現為IDT(Interrupt Descriptor Table)。與GDT類似,IDT也是一個駐留於記憶體中的結構,通過lidt
指令裝載到CPU。每個中斷處理例程對應一個門描述符(Gate
Descriptor)。在fleurix中初始化IDT的程式碼位於idt_init()
(見src/trap.c
)。
在中斷髮生時,CPU會先執行一些許可權檢查,若正常,則依據特權級別從TSS中取出相應的ss與esp切換棧到核心棧,並將當前的eflags、cs、eip暫存器壓棧(某些中斷還會額外壓一個error code入棧),隨後依據門描述符中指定的段選擇符(Segment Selector)與目標地址跳轉到中斷處理例程。 儲存當前程式的上下文則屬於中斷處理例程的工作。在fleurix中,儲存中斷上下文的操作由_hwint_common_stub
(見src/kern/entry.S.rb
)負責執行,它會將中斷上下文儲存到棧上的struct
trap
結構(見src/inc/idt.h
)。
在這裡有三個地方值得留意:
-
只有部分中斷會壓入error code,這會導致棧結構的不一致。為了簡化中斷處理例程的介面,fleurix採用的方法是通過程式碼生成,在中斷處理例程之初為不帶有error code的中斷統一壓一個雙字入棧,值為0,佔據error code在
struct trap
中的位置。並將中斷呼叫號壓棧,以方便程式的編寫與除錯。 -
fleurix中的中斷處理例程都經過彙編例程
_hwint_common_stub
,它在儲存中斷上下文之後,會呼叫hwint_common()
(見src/kern/trap.c
)函式。hwint_common()
函式將依據中斷號,再查詢hwint_routines
陣列找到並呼叫相應的處理例程。 - 中斷的發生往往就意味著CPU特權級別的轉換,因此,可以將陷入(或稱"軟體中斷")作為使用者態進入核心態的入口,從而實現系統呼叫。在fleurix中系統呼叫對應的中斷號為0x80,與linux相同。
I/O
外部裝置一般分為機械部分與電路部分。電路部分又被稱作控制器(Controller)或者介面卡(Adapter),負責裝置的邏輯與介面。
CPU一般都是通過暫存器的形式來訪問外部裝置。外設的暫存器通常包括控制暫存器、狀態暫存器與資料暫存器三類,分別用於傳送命令、讀取狀態、讀寫資料。按照訪問外設的暫存器的方式,CPU又主要分為兩類:
- 將外設暫存器與記憶體統一編址(Memory-Mapped):訪問暫存器即一般的記憶體讀寫,沒有專門用於I/O的指令。
-
將外設暫存器獨立編址(I/O-Mapped):每個暫存器對應一個埠號(port),通過專門讀/寫的指令訪問外設的暫存器,如
in
與out
指令。
x86是後者:採用獨立編址的方式,外設暫存器即I/O埠,並通過in、out等彙編指令進行讀寫。
在fleurix中,提供瞭如下的幾個函式來讀寫埠:
-
inb()
與outb()
:按位元組讀寫埠 -
inw()
與outw()
:按字讀寫埠 -
insb()
與outsb()
:對某埠讀/寫一個位元組序列 -
insl()
與outsl()
:對某埠讀/寫一個雙字的序列
以上函式都是對彙編指令的簡單包裝,定義於src/inc/asm.h
。
留意它們的原始碼,可以注意到它們都會在最後呼叫一個io_delay()
函式。這是因為對於一些老式匯流排的外部裝置,讀寫I/O埠的速度若過快就容易出現丟失資料的現象,為此在每次I/O操作之間插入幾條指令作為延時,等待慢速外設。
PIT
fleurix通過Intel 8253 PIT(Programmable Interval Timer)定時器定時產生中斷,用於計時與程序排程。
Intel 8253 PIT晶片擁有三個定時器:作為系統時鐘,定時器1為歷史遺留中用於定期重新整理DRAM,定時器2用於揚聲器。三個定時器分別對應三個資料暫存器0x40、0x41、0x42,以及一個命令暫存器0x43。這裡只需要關心計時器0的功能,用到的暫存器只有0x40與0x43。
定時器0的預設頻率為1193180HZ,可以通過如下的程式碼調整它的頻率:
uint di = 1193180/HZ;
outb(0x43, 0x36);
outb(0x40, (uchar)(di&0xff));
outb(0x40, (uchar)(di>>8));
相關推薦
基於 Bochs 的作業系統核心實現
簡介 Bochs 簡介 Bochs(讀音Box)是一個開源的模擬器(Emulator),它可以完全模擬x86/x64的硬體以及一些外圍裝置。與VirtualBox / VMware等虛擬機器(Virtual Machine)產品不同,它的設計目標在於模擬一
X86架構作業系統核心實現過程
#作業系統核心實現(一) 作業系統的學習還是需要落地不能停留在概念上,於是打算模仿並實現作業系統核心,前面一直在做川合秀實先生的《30天自制作業系統》,但是由於它的底層系統是windows,以及講解較為淺顯不能深入瞭解具體的核心執行模式狀態,轉到了劉歡師兄寫的《
作業系統程序遍歷(進入linux核心實現)
實驗要求概述: Part I—Iterating over Tasks Linearly As illustrated in Section 3.1, the PCB in Linux is represented by the structure task_struct, whic
基於shatter tookit外掛實現場景模型切割(附外掛及核心程式碼)
前言 專案需求就是,把當前視野內的模型摳下來,然後資料匯出供後邊的模組使用,是一個三維模擬相關的專案。 專案結構比較簡單,主要分三個部分: 1.獲取視野內不需要切割的模型列表,挨個克隆並寫入結果集陣列 2.獲取視野邊界線上的需要切割的模型列表,按順序克隆切割並把視野內的部分寫入結果集陣列
direct IO 核心實現分析及揭示一個坑——基於3.10.0-693.11.1
linux的讀寫系統呼叫提供了一個O_DIRECT標記,可以讓嘗試繞過快取,直接對磁碟進行讀寫(為啥是嘗試繞過?當直接落盤失敗時還要通過快取去落盤)。為了能實現直接落盤,使用direct IO限制多多,檔案偏移得對齊到磁碟block,記憶體地址得對齊到磁碟block,讀寫size也得對齊
Dubbo原理解析-Dubbo核心實現之基於SPI思想Dubbo核心實現(轉)
SPI介面定義 定義了@SPI註解 public @interface SPI { String value() default ""; //指定預設的擴充套件點 } 只有在介面打了@SPI註解的介面類才會去查詢擴充套件點實現 會依次從這幾個檔案中讀取擴
高版本bochs-2.6.7的bochsrc的改寫 一個作業系統的實現
前幾天做實驗又看起了於淵的一個作業系統的實現第二版 在centos下搭載了環境 下載的是bochs-2.6.7 安裝網上有教程 很順利 在把配置檔案bochsrc放入的是發現不好用 原來是他那個版本太低了 現將我這個好用的版本拿出來 # Configurate filefo
Nginx之——日誌按日期分割的實現(基於CentOS作業系統)
Nginx自身是沒有按日期切割日誌的功能,可以用shell指令碼實現。新建一個cut_log.sh, #!/bin/sh # Program: # Auto cut nginx log script. # nginx日誌路徑 LOGS_PATH=/usr/local/nginx
Linux核心--基於Netfilter的核心級包過濾防火牆實現
測試核心版本:Linux Kernel 2.6.35----Linux Kernel 3.2.1作者:閆明知識基礎:本防火牆的開發基於對Linux核心網路棧有個良好的概念,本人對網路棧的分析是基於早期版本(Linux 1.2.13),在明確了網路棧架構的前提下,上升一步分析高
Orange's 一個作業系統的實現--Bochs遇到的問題
1.Message: dlopen failed for module ‘x’: file not found 這是未安裝bochs-x的緣故 解決方案: sudo apt-get install bochs以後接著安裝bochs
2. Dubbo原理解析-Dubbo核心實現之基於SPI思想Dubbo核心實現
SPI介面定義 定義了@SPI註解 public @interface SPI { Stringvalue() default ""; //指定預設的擴充套件點 } 只有在介面打了@SPI註解的介面類才會去查詢擴充套件點實現 會依次從這幾個檔案中讀取擴充套
基於mykernel 2.0編寫一個作業系統核心
資源下載:[https://github.com/mengning/mykernel](https://github.com/mengning/mykernel) > 實驗內容: > > 1、配置實驗環境,完成Linux核心編譯。 > > 2、對系統原始碼進行修改,基於myke
(一二三)基於GCD的dispatch_once實現單例設計
super dispatch ret 強引用 一次 nslog span imp int 要實現單例,關鍵是要保證類的alloc和init僅僅被調用一次。而且被自身強引用防止釋放。 近日讀唐巧先生的《iOS開發進階》。受益匪淺,通過GCD實現單例就是收獲之中的一個,以下
基於cookie在nginx實現業務灰度發布
灰度基於cookie在nginx實現業務灰度發布背景灰度發布是指在黑與白之間,能夠平滑過渡的一種發布方式。灰度發布可以保證整體系統的穩定,在初始灰度的時候就可以發現、調整問題,以保證其影響度。業務存在灰度發布的需求,可以通過nginx+lua形式實現業務的灰度發布,目前這一形式已在廣平互動廣告相關業務已經實現
jQuery基於json與cookie實現購物車的方法
構造 exp als com else cookies 一個 可選參數 int /** * 添加商品及數量到購物車cookie中,返回當前商品在cookie中的總數 */ function AddToShoppingCar(id, num, t
HDMI調試經驗分享(一)—— 基於KC705的example實現
alt 如果 追蹤 分享 生成 必須 還需要 pro 註意 在KC705的平臺上,實現HDMI的example有兩個,xapp1287,xapp1291。其中1291偏復雜。 因為我沒有HDCP的license,使用xapp1287的tcl生成
Django基於Cookie裝飾器實現用戶認證
.html 用戶 print class request 裝飾器 col password bsp def login(request): if request.method =="GET": return render(request,"logi
基於express+redis高速實現實時在線用戶數統計
posit itl git term agent bus class net img 作者:zhanhailiang 日期:2014-11-09 本文將介紹怎樣基於express+redis高速實現實時在線用戶數統計。 1. 在github.com上創建項目u
【金陽光測試】基於控件核心技術探討---Android自己主動化系列(2)---2013年5月
進程 實用 版本 信號 監聽 dialog otto 設計 核心技術 第一講分享了下安卓自己主動化一些概況和一些自己主動化框架現狀和技術可以解決什麽樣的問題。這次課就深入到android世界裏面。遨遊、翺翔。深入了解自己主動化測試核心技術。
基於CapAnalysis軟件實現的本地流量可視化
流量分析 capanalysis 自動化 無意間發現了款可視化的數據包分析工具CanAnalysis試用之下感覺不錯。關鍵還是開源的免費軟件,正好公司領導又有需求要看看網絡數據流量的相關數據。於是乎花了幾天時間稍微試著搭了個架子。現將如何實現的給大家詳細列出來。 需求的系統: 1. 安裝u