天天看點

libvirt源碼分析——libvirtd的初始化

libvirtd的初始化在libvirtd.c中main函數裡。

首先是分析指令行傳進來的參數,常用的是libvirtd -d,即啟動libvirtd守護程序。

c = getopt_long(argc, argv, "ldf:p:t:vVh", opts, &optidx);
    case 'd':
        godaemon = ;  
           

然後執行

該函數是根據是初始化一些配置,比如最大的用戶端連接配接數,主控端的主機名。

接下來就是确定配置檔案的路徑,當privileged = geteuid() == 0為true時,則privileged=true,remote_config_file=”/libvirt/libvirtd.conf”

緊接着就是從配置檔案中讀取資料,并分析資料,将一些配置指派在config結構體中,比如有日志等級,日志過濾器,預設的連接配接uri

下面是日志的初始化

daemonSetupLogging會先從環境變量讀取日志的一些配置資訊,若讀取不到,則會将從配置檔案讀取到的配置指派給全局變量,比如,其函數裡有下面的一個調用

virLogSetDefaultPriority(config->log_level);

就是将從配置檔案讀取到的日志等級log_level指派給全局變量virLogDefaultPriority,以後列印日志就會根據virLogDefaultPriority的值來決定是否列印日志。

同時它還會确定日志的輸出位置,是系統的日志檔案,還是自己的日志檔案(從配置檔案或者環境變量讀取到日志檔案的路徑)。

接下來是确定pid檔案的路徑

對于root使用者,最總确定的pid檔案路徑是LOCALSTATEDIR/run/libvirtd.pid。

接下來就是根據從檔案讀取到的配置來确定unix套接字的路徑

daemonUnixSocketPaths會根據前面讀取到的config資訊來指派sock_file,sock_file_ro,sock_file_adm三個值,sock_file是unix套接字的路徑,三個值具體為config->unix_sock_dir/libvirt-sock,config->unix_sock_dir/libvirt-sock-ro , config->unix_sock_dir/libvirt-admin-sock。

daemonForkIntoBackground是将啟動的Libvirtd程序變為背景的守候程序。

進入該函數,可以看到該函數的調用大概如下

pipe(statuspipe) 
pid = fork()
switch (pid)
{
case : /*child 程序 */
    setsid()
    nextpid = fork();
            switch (nextpid) {
            case : /* grandchild */
                return statuspipe[];  /*grand child 程序傳回描述符的寫端*/
            default:  
                _exit(EXIT_SUCCESS); /*子程序退出*/
            }
default:
    virProcessWait(pid, NULL, false)
    ret = read(statuspipe[], &status, );
}
           

可以知道daemonForkIntoBackground,是将該程序的grand child程序變為守護程序,同時,該程序等待grand child給其發消息,若收到成功消息,則退出。grand child程序傳回管道的寫端,如果後面初始化完成,就會往管道寫資料。

再往下走

virPidFileAcquirePath是為了防止重複的運作libvirtd程序,該函數中,先嘗試設定獨占鎖給pid_file,如果失敗,則說明已經有libvirtd程序運作了,如果成功,則将pid寫到pid_file中。

virNetDaemonNew中首先會調用virEventRegisterDefaultImpl,該函數會建立一個管道eventLoop.wakeupfd,并設定管道的讀端的回調函數(僅僅從讀端讀取一個字元)和期待事件。然後virNetDaemonNew再調用virEventRegisterImpl給全局變量addHandleImpl,updateHandleImpl,removeHandleImpl指派函數位址,這些函數都是為了更改描述符事件和回調函數。

接下來會調用daemonInitialize函數來注冊libvirtd側的驅動。

qemu_driver就是在libvirtd側注冊的,所謂的注冊就是将存儲了qemu相關的兩個結構體qemuConnectDriver和qemuStateDriver填充到全局數組virConnectDriverTab和virStateDriverTab中。

daemonSetupNetworking(srv, srvAdm, 
                              config,
                              sock_file,
                              sock_file_ro,
                              sock_file_adm,
                              ipsock, privileged) 
           

sock_file等socket的路徑在前面已經初始化了,該函數根據路徑去建立socket,以sock_file unix套接字為例,會首先建立一個套接字,然後給監聽描述符添加回調函數virNetServerServiceAccept和virObjectFreeCallback,virNetServerServiceAccept函數會在每次有連接配接到來時去處理連接配接請求,同時調用virNetServerDispatchNewClient函數,該函數會開辟每個已連接配接描述符對應的緩存,用來存儲接收的資料,同時也會添加資料讀寫函數virNetServerClientDispatchEvent,資料讀寫函數中會調用資料分析函數virNetServerDispatchNewMessage,資料分析函數會根據消息的頭類型,去srv(virNetServerPtr)結構中尋找符合的工程結構virNetServerProgramProc(對于qemu就是qemuProcs),該工程中包含相應的函數,去進一步調用注冊的驅動函數。

至此,Libvirtd的基本初始化已經完成,開始向grand father程序發送消息,讓其退出

後面的就是初始化驅動

該函數會調用virStateDriverTab數組中的每個注冊的函數,來初始化各個驅動。

最後

virNetDaemonRun會循環調用virEventRunDefaultImpl,該函數會運作poll去檢測eventloop.handles中的每個描述符的事件,如果有事件發生就會調用回調函數去處理事件。

繼續閱讀