關於qemu的二三事(5)————qemu原始碼分析之引數解析
目前,這個qemu的版本號是:
[[email protected] x86_64-softmmu]# ./qemu-system-x86_64 --version
QEMU emulator version 2.9.50 (v2.9.0-941-g0748b35-dirty)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers
原始碼的commit號是:0748b3526e8cb78b9cd64208426bfc3d54a72b04
這篇文章我會嘗試用gdb單步除錯的方式,來簡要分析一下qemu原始碼的執行流程。
上篇文章提到,我們專門建立了一個目錄 /bin/debug/native作為本地除錯的目錄。現在開啟這個路徑,你會發現裡面多了許多檔案和目錄。其中有個x86_64-softmmu的資料夾下面就有文明要用的的qemu主程式qemu-system-x86_64。好了,現在準備gdb搞起來~
作為C語言編寫的程式,qemu的main函式是在vl.c這個檔案裡面的,開啟vl.c我們能看到的它包含一些標頭檔案,然後就是定義了一堆變數、陣列和結構體,還有一些函式,摘錄一點簡單例子:
int singlestep = 0; int smp_cpus = 1; int max_cpus = 1; int smp_cores = 1; int smp_threads = 1; int acpi_enabled = 1; int no_hpet = 0; int fd_bootchk = 1; static int no_reboot; int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; const char *watchdog; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; ... ...
還有這些:
簡單過一下,我們直接去看main函式,首先看到的就是一些變數、陣列、指標的初始化和一些初始化的函式,然後就是引數的解析。static QemuOptsList qemu_mem_opts = { .name = "memory", .implied_opt_name = "size", .head = QTAILQ_HEAD_INITIALIZER(qemu_mem_opts.head), .merge_lists = true, .desc = { { .name = "size", .type = QEMU_OPT_SIZE, }, { .name = "slots", .type = QEMU_OPT_NUMBER, }, { .name = "maxmem", .type = QEMU_OPT_SIZE, }, { /* end of list */ } }, }; ... ...
從第3084行開始,就是引數的具體解析了。
3083 /* first pass of option parsing */
3084 optind = 1;
3085 while (optind < argc) {
3086 if (argv[optind][0] != '-') {
3087 /* disk image */
3088 optind++;
3089 } else {
3090 const QEMUOption *popt;
3091
3092 popt = lookup_opt(argc, argv, &optarg, &optind);
3093 switch (popt->index) {
3094 case QEMU_OPTION_nodefconfig:
3095 defconfig = false;
3096 break;
3097 case QEMU_OPTION_nouserconfig:
3098 userconfig = false;
3099 break;
3100 }
3101 }
3102 }
3103
這裡涉及到一個結構體QEMUOption
1952 typedef struct QEMUOption {
1953 const char *name; //arg option name
1954 int flags; //has value or not
1955 int index; //option type
1956 uint32_t arch_mask; //architechture mask
1957 } QEMUOption;
3084到3102這一段,其實只是個初步的篩選,下一個階段,才是具體的引數的解析和結構體的填充。
3110 /* second pass of option parsing */
3111 optind = 1;
3112 for(;;) {
3113 if (optind >= argc)
3114 break;
3115 if (argv[optind][0] != '-') {
3116 hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
3117 } else {
3118 const QEMUOption *popt;
3119
3120 popt = lookup_opt(argc, argv, &optarg, &optind);
3121 if (!(popt->arch_mask & arch_type)) {
3122 error_report("Option not supported for this target");
3123 exit(1);
3124 }
3125 switch(popt->index) {
3126 case QEMU_OPTION_no_kvm_irqchip: {
3127 olist = qemu_find_opts("machine");
3128 qemu_opts_parse_noisily(olist, "kernel_irqchip=off", false);
3129 break;
3130 }
3131 case QEMU_OPTION_cpu:
3132 /* hw initialization will check this */
3133 cpu_model = optarg;
3134 break;
3135 case QEMU_OPTION_hda:
3136 {
3137 char buf[256];
3138 if (cyls == 0)
3139 snprintf(buf, sizeof(buf), "%s", HD_OPTS);
3140 else
... ...
這裡它就具體的一個引數一個引數的解析,lookup_opt(...)這個函式來解析和填充出QEMUOption *popt這個區域性變數的值,然後用QEMUOption 結構體裡面的index來判斷引數種類,比如 -cpu 或者 -hda之類的,並完成一些簡單引數的賦值,複雜引數或者引數有多個項多個值的還是需要呼叫具體的函式去解析。
那麼qemu這麼多的引數,是怎麼維護、儲存的呢,顯然僅僅憑藉這些簡單的變數和陣列是不夠的,實際上來說,qemu維護了一個類似連結串列的東西來儲存這些引數,在早先的3029行開始,就開始構建一個連結串列:
3029 qemu_add_opts(&qemu_drive_opts);
3030 qemu_add_drive_opts(&qemu_legacy_drive_opts);
3031 qemu_add_drive_opts(&qemu_common_drive_opts);
3032 qemu_add_drive_opts(&qemu_drive_opts);
3033 qemu_add_drive_opts(&bdrv_runtime_opts);
3034 qemu_add_opts(&qemu_chardev_opts);
3035 qemu_add_opts(&qemu_device_opts);
3036 qemu_add_opts(&qemu_netdev_opts);
3037 qemu_add_opts(&qemu_net_opts);
3038 qemu_add_opts(&qemu_rtc_opts);
3039 qemu_add_opts(&qemu_global_opts);
3040 qemu_add_opts(&qemu_mon_opts);
3041 qemu_add_opts(&qemu_trace_opts);
3042 qemu_add_opts(&qemu_option_rom_opts);
3043 qemu_add_opts(&qemu_machine_opts);
3044 qemu_add_opts(&qemu_accel_opts);
3045 qemu_add_opts(&qemu_mem_opts);
3046 qemu_add_opts(&qemu_smp_opts);
3047 qemu_add_opts(&qemu_boot_opts);
3048 qemu_add_opts(&qemu_sandbox_opts);
3049 qemu_add_opts(&qemu_add_fd_opts);
3050 qemu_add_opts(&qemu_object_opts);
3051 qemu_add_opts(&qemu_tpmdev_opts);
3052 qemu_add_opts(&qemu_realtime_opts);
3053 qemu_add_opts(&qemu_msg_opts);
3054 qemu_add_opts(&qemu_name_opts);
3055 qemu_add_opts(&qemu_numa_opts);
3056 qemu_add_opts(&qemu_icount_opts);
3057 qemu_add_opts(&qemu_semihosting_config_opts);
3058 qemu_add_opts(&qemu_fw_cfg_opts);
每個節點都是這樣一個結構體:
59 struct QemuOptsList {
60 const char *name;
61 const char *implied_opt_name;
62 bool merge_lists; /* Merge multiple uses of option into a single list? */
63 QTAILQ_HEAD(, QemuOpts) head;
64 QemuOptDesc desc[];
65 };
在所有引數都被遍歷解析之後,這個連結串列所有節點都被初始化和賦值。這時候才開始真正的執行。實際上直到4082行,這些引數的解析才算是初步完成,因為有些帶子項的引數還是需要單獨解析的。
相關推薦
關於qemu的二三事(5)————qemu原始碼分析之引數解析
目前,這個qemu的版本號是: [[email protected] x86_64-softmmu]# ./qemu-system-x86_64 --version QEMU emulator version 2.9.50 (v2.9.0-941-g0748
童年生活二三事(語法)
NowCoder小時候走路喜歡蹦蹦跳跳,他最喜歡在樓梯上跳來跳去。 但年幼的他一次只能走上一階或者一下子蹦上兩階。 現在一共有N階臺階,請你計算一下NowCoder從第0階到第N階共有幾種走法。 輸入
MonoTouch 二三事(一)
題外話: 最近工作室打算接個iPhone + iPad + Android Phone + Android Pad 雜誌客戶端的活,以前只只儲備了Android的開發知識, 因為個人原因,很討厭Apple的作風,所以從沒打算做Apple的開發,也就沒有儲備相關知識,臨時只好抱佛腳,看看能否用自己 會的知識
關於Java虛擬機器二三事(八)---JVM機器指令集及其執行引擎
1.前言 Java虛擬機器和真實的計算機一樣,執行的都是二進位制的機器碼;而我們將.java原始碼編譯成.class檔案,class檔案便是Java虛擬機器能夠認識的二進位制機器碼。Java能夠識別class檔案中的資訊和機器指令,進而執行這些機器指令。那麼,Java虛
關於Java虛擬機器二三事(五)---類檔案結構(上)
1.前言 當編寫完一段Java程式碼並儲存以後,其實Java程式碼會儲存在以.Java為副檔名作為結尾的檔案中,如test.java,而這個檔案若想在JVM上執行,則必須先利用javac編譯器進行編寫,形成所謂的“位元組碼(ByteCode)檔案”,即接下來要分析的重點
讀vue-element-admin原始碼二三事(一)
因為本人是前端純菜鳥,除了上週大概看了vue.js較簡單的部分以及簡單的ajax和js基礎框架什麼接觸的少,其他沒接觸過,以下沒接觸過的框架用單行程式碼表示,如123 文章目錄基礎登入主頁 基礎 登入 路由 看著路由帶#還是很不舒適,於是router/inde
(一)jdk原始碼分析之collection,List,Set
前言 標題取得有點大,一口氣分析三塊的原始碼,看上去是個很大的話題,不過在個人看來,一方面,這三個都是介面,不涉及程式碼實現,讀起來比較快,另一方面,大家都知道List,Set這兩個介面都繼承自collection,他們之間存在關聯,所以放在一塊分析討論最能凸顯,這三塊介面
Java併發系列(4)AbstractQueuedSynchronizer原始碼分析之條件佇列
通過前面三篇的分析,我們深入瞭解了AbstractQueuedSynchronizer的內部結構和一些設計理念,知道了AbstractQueuedSynchronizer內部維護了一個同步狀態和兩個排隊區,這兩個排隊區分別是同步佇列和條件佇列。我們還是拿公共廁所做比喻,同步佇
(一)ghostscript原始碼分析之interp()函式的第二個引數
/* Main interpreter. */ /* If execution terminates normally, return e_InterpreterExit. */ /* If an error occurs, leave the current object in *perror_o
再刷PAT系列~ 1008 童年生活二三事(斐波那契數列)
題目描述 NowCoder小時候走路喜歡蹦蹦跳跳,他最喜歡在樓梯上跳來跳去。 但年幼的他一次只能走上一階或者一下子蹦上兩階。 現在一共有N階臺階,請你計算一下NowCoder從第0階到第N階共有幾
zzuli OJ 1091: 童年生活二三事(多例項測試)
Description Redraiment小時候走路喜歡蹦蹦跳跳,他最喜歡在樓梯上跳來跳去。 但年幼的他一次只能走上一階或者一下子蹦上兩階。 現在一共有N階臺階,請你計算一下Redraimen
MonoTouch 二三事(三)mono mkbundle 打包程式的解包支援
許久以後,這個續上這個系列的第三篇。 玩過mono的可能知道mono有一個工具mkbundle ,可以把mono的執行時與類庫與你的程式的依賴程式集都打包成一個可執行檔案,在win下為exe檔案,例如mandroid.exe,mtouch.exe,在mac下的Mach-O檔案,例如mtouch,mtou
MonoTouch 二三事(二)
2013-02-26 更新 真機秒退問題,授權檔案格式原來的不對。 接上篇。 把client.exe拖入ilspy後發現,授權是靠mac地址+磁碟序列號等資訊,授權驗證依賴了rsa簽名,得了,咱沒超級計算機,跑不出對應的私鑰,只有直接該程式碼爆破了。 但是在改制前呢?我大概瀏覽了下client.exe
Android:Handler 二三事(三)訊息處理機制
主要內容 Handler 的訊息處理機制。 主要是關於 MessageQueue、Message、Looper、Handler 之間的關係。 Android 訊息驅動機制的四要素 接收訊息的訊息佇列–>MessageQueue 阻塞式的從訊
單身北漂生活二、三事(上)——北漂18年(8)
本週跟朋友吃飯,又聊起為啥要寫這個系列。我想了一下,回答:“怕忘。人過30歲,精力、體力、腦力都逐年下降。我三十有八了,要趁著還沒忘,多寫寫……” 97年來北京那會兒還沒搞物件,確切地說就沒搞過物件。不是因為什麼醉心學習之類的理由,一來是沒想搞,二來確實也搞不上——脾氣
Android:Handler 二三事(二)由記憶體洩漏所想到的(垃圾回收機制)
主要內容解決Handler記憶體洩漏以及延伸(垃圾回收、引用等)解決Handler記憶體洩漏及延伸為什麼Handler會引起記憶體洩漏?這是一段使用Handler的程式碼public class Lea
區塊鏈二三事兒(技術篇)
一 前言 區塊鏈的開始,還要提一箇中國人–戴偉,可以去他的網站(www.weidai.com)上看看他關於B-Money的文章。中本聰在論文《比特幣:一種點對點網路中的電子現金》中的第一個引用者就是戴偉。十年來,區塊鏈的概念早已飛入尋常百姓家。 挖個坑,認知區塊鏈需要如下四步:
第三章(5)方法引用
1.方法引用初探 方法呼叫可以被看作僅僅呼叫特定方法的lambda表示式的一種快捷寫法。如果一個Lambda代表的只是“直接呼叫這個方法”,那最好還是用名稱來呼叫它,而不是去描述如何呼叫它。事實上,方法引用就是讓你根據已有的方法實現來建立Lambda表示式
Java核心技術--第三章(5)
控制流程 條件語句+迴圈結構 控制流程 條件語句 迴圈語句 switch語句(判斷多個值) 塊 用 { }括起來的若干條Java語句 塊可巢狀在另一個塊中 public static void main(String[] args)
組合語言二三事(遇到的各種問題,bug)——不斷更新中
一、上機環境 DOSbox -0.74(64位) 編輯程式:EDIT.COM或其他文字編輯工具軟體,用於編輯源程式。 彙編程式:MASM.EXE,用於彙編源程式,得到目標程式。 連線程式:LINK.EX