1. 程式人生 > >關於qemu的二三事(5)————qemu原始碼分析之引數解析

關於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;

... ... 

還有這些:

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 */ }
    },
};

... ... 
簡單過一下,我們直接去看main函式,首先看到的就是一些變數、陣列、指標的初始化和一些初始化的函式,然後就是引數的解析。

從第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行,這些引數的解析才算是初步完成,因為有些帶子項的引數還是需要單獨解析的。

相關推薦

關於qemu5————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併發系列4AbstractQueuedSynchronizer原始碼分析條件佇列

通過前面三篇的分析,我們深入瞭解了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