QT進(jìn)程間通信 詳細(xì)介紹
1、QT通信機(jī)制
為了更好的實(shí)現(xiàn)QT的信息交互,在QT系統(tǒng)中創(chuàng)建了較為完善的通信機(jī)制。QT的通信可分為QT內(nèi)部通信和外部通信兩大類(lèi)。對(duì)于這兩類(lèi)通信機(jī)制及應(yīng)用場(chǎng)合做如以下分析:
(1)QT內(nèi)部對(duì)象間通信
在圖形用戶(hù)界面編程中,經(jīng)常需要將一個(gè)窗口部件的變化通知給窗口的其它部件使其產(chǎn)生相應(yīng)的變化。對(duì)于這種內(nèi)部對(duì)象間的通信,QT主要采用了信號(hào)和槽的機(jī)制。這種機(jī)制是QT區(qū)別于其他GUI工具的核心機(jī)制。在大部分的GUI工具中,通常為可能觸發(fā)的每種行為通過(guò)定義回調(diào)函數(shù)來(lái)實(shí)現(xiàn)。這種回調(diào)函數(shù)是一個(gè)指向函數(shù)的指針,在進(jìn)行函數(shù)回調(diào)執(zhí)行時(shí)不能保證所傳遞的函數(shù)參數(shù)類(lèi)型的正確性,因此容易造成進(jìn)程的崩潰。
在QT中,信號(hào)和槽的機(jī)制取代了這種繁雜的、易崩潰的對(duì)象通信機(jī)制。信號(hào)是當(dāng)對(duì)象狀態(tài)改變時(shí)所發(fā)出的。槽是用來(lái)接收發(fā)射的信號(hào)并響應(yīng)相應(yīng)事件的類(lèi)的成員函數(shù)。信號(hào)和槽的連接是通過(guò)connect()函數(shù)來(lái)實(shí)現(xiàn)的。例如,實(shí)現(xiàn)單擊按鈕終止應(yīng)用程序運(yùn)行的代碼connect(button , SIGNAL(clicked()) , qApp , SLOT(quit()) );實(shí)現(xiàn)過(guò)程就是一個(gè)button被單擊后會(huì)激發(fā)clicked信號(hào),通過(guò)connect()函數(shù)的連接qApp會(huì)接收到此信號(hào)并執(zhí)行槽函數(shù)quit()。在此過(guò)程中,信號(hào)的發(fā)出并不關(guān)心什么樣的對(duì)象來(lái)接收此信號(hào),也不關(guān)心是否有對(duì)象來(lái)接收此信號(hào),只要對(duì)象狀態(tài)發(fā)生改變此信號(hào)就會(huì)發(fā)出。此時(shí)槽也并不知曉有什么的信號(hào)與自己相聯(lián)系和是否有信號(hào)與自己聯(lián)系,這樣信號(hào)和槽就真正的實(shí)現(xiàn)了程序代碼的封裝,提高了代碼的可重用性。同時(shí),信號(hào)和槽的連接還實(shí)現(xiàn)了類(lèi)型的安全性,如果類(lèi)型不匹配,它會(huì)以警告的方式報(bào)告類(lèi)型錯(cuò)誤,而不會(huì)使系統(tǒng)產(chǎn)生崩潰。
(2)QT與外部設(shè)備間通信
QT與外部通信主要是將外部發(fā)來(lái)的消息以事件的方式進(jìn)行接收處理。外部設(shè)備將主要通過(guò)socket與QT應(yīng)用程序進(jìn)行連接。在此,以輸入設(shè)備與QT應(yīng)用程序的通信為例說(shuō)明QT與外部通信的原理。
在QT的應(yīng)用程序開(kāi)始運(yùn)行時(shí),主程序?qū)⑼ㄟ^(guò)函數(shù)調(diào)用來(lái)創(chuàng)建并啟動(dòng)qwsServer服務(wù)器,然后通過(guò)socket建立該服務(wù)器與輸入硬件設(shè)備的連接。服務(wù)器啟動(dòng)后將會(huì)打開(kāi)鼠標(biāo)與鍵盤(pán)設(shè)備,然后將打開(kāi)的設(shè)備文件描述符fd連接到socket上。等到QT應(yīng)用程序進(jìn)入主事件循環(huán)時(shí),事件處理程序?qū)⑼ㄟ^(guò)Linux系統(tǒng)的select函數(shù)來(lái)檢測(cè)文件描述符fd的狀態(tài)變化情況以實(shí)現(xiàn)對(duì)socket的監(jiān)聽(tīng)。如果文件描述符fd狀態(tài)改變,說(shuō)明設(shè)備有數(shù)據(jù)輸入。此時(shí),事件處理程序?qū)?huì)發(fā)出信號(hào)使設(shè)備輸入的數(shù)據(jù)能及時(shí)得到QT應(yīng)用程序的響應(yīng)。數(shù)據(jù)進(jìn)入服務(wù)器內(nèi)部就會(huì)以事件的形式將數(shù)據(jù)放入事件隊(duì)列里,等待QT客戶(hù)應(yīng)用程序接收處理。處理結(jié)束后再將事件放入請(qǐng)求隊(duì)列里,通過(guò)服務(wù)器將事件發(fā)送到相應(yīng)硬件上,完成外部輸入設(shè)備與QT應(yīng)用程序的整個(gè)通信過(guò)程。
2、 QProcess機(jī)制分析
QProcess類(lèi)通常是被用來(lái)啟動(dòng)外部程序,并與它們進(jìn)行通信的。QProcess是把外部進(jìn)程看成是一個(gè)有序的I/O設(shè)備,因此可通過(guò)write()函數(shù)實(shí)現(xiàn)對(duì)進(jìn)程標(biāo)準(zhǔn)輸入的寫(xiě)操作,通過(guò)read(),readLine()和getChar()函數(shù)實(shí)現(xiàn)對(duì)標(biāo)準(zhǔn)輸出的讀操作。
(1) QProcess通信機(jī)制
QT可以通過(guò)QProcess類(lèi)實(shí)現(xiàn)前端程序?qū)ν獠繎?yīng)用程序的調(diào)用。這個(gè)過(guò)程的實(shí)現(xiàn)首先是將前端運(yùn)行的程序看成是QT的主進(jìn)程,然后再通過(guò)創(chuàng)建主進(jìn)程的子進(jìn)程來(lái)調(diào)用外部的應(yīng)用程序。這樣QProcess的通信機(jī)制就抽象為父子進(jìn)程之間的通信機(jī)制。QProcess在實(shí)現(xiàn)父子進(jìn)程間的通信過(guò)程中是運(yùn)用Linux系統(tǒng)的無(wú)名管道來(lái)實(shí)現(xiàn)的,因此為了能更加清楚的說(shuō)明QProcess的通信機(jī)制,在此首先介紹關(guān)于無(wú)名管道實(shí)現(xiàn)父子進(jìn)程間的通信機(jī)制。
無(wú)名管道是一種只能夠在同族父子之間通信,并且在通信過(guò)程中,只能從固定的一端寫(xiě),從另一端讀的單向的通信方式。該無(wú)名管道是通過(guò)調(diào)用pipe()函數(shù)而創(chuàng)建的。創(chuàng)建代碼如下:
- #include <unistd.h>
 - int pipe(int fd[2]) ;
 - 返回:若成功則為0,若出錯(cuò)則為-1
 
創(chuàng)建后經(jīng)參數(shù)fd返回兩個(gè)文件描述符:fd[0]為讀而打開(kāi),fd[1]為寫(xiě)而打開(kāi)。經(jīng)過(guò)fork()函數(shù)創(chuàng)建其子進(jìn)程后,子進(jìn)程將擁有與父進(jìn)程相同的兩個(gè)文件描述符。如果想要實(shí)現(xiàn)父進(jìn)程向子進(jìn)程的通信則關(guān)閉父進(jìn)程的讀端fd[0],同時(shí)關(guān)閉子進(jìn)程的寫(xiě)端fd[1]。這樣就建立了從父進(jìn)程到子進(jìn)程的通信連接。
由于無(wú)名管道的單向通信性,所以如果要應(yīng)用無(wú)名管道實(shí)現(xiàn)父子進(jìn)程之間的雙向通信則至少需要應(yīng)用雙管道進(jìn)行通信。QProcess類(lèi)的通信原理就是利用多管道實(shí)現(xiàn)了父子進(jìn)程之間的通信。然而對(duì)于外部運(yùn)行的應(yīng)用程序大都是通過(guò)標(biāo)準(zhǔn)輸入而讀得信息,通過(guò)標(biāo)準(zhǔn)輸出而發(fā)送出信息,因此只通過(guò)建立管道并不能完成內(nèi)外進(jìn)程?之間的通信。要解決此問(wèn)題,就如該模塊開(kāi)始時(shí)所說(shuō),QProcess是把外部進(jìn)程看成是一個(gè)I/O設(shè)備,然后通過(guò)對(duì)I/O設(shè)備的讀寫(xiě)來(lái)完成內(nèi)外進(jìn)程的通信。
在QProcess中父子進(jìn)程之間是通過(guò)管道連接的,要實(shí)現(xiàn)子進(jìn)程能從標(biāo)準(zhǔn)輸入中讀得父進(jìn)程對(duì)管道的寫(xiě)操作,同時(shí)父進(jìn)程能從管道中讀得子進(jìn)程對(duì)標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)容錯(cuò)的寫(xiě)操作,就要在子進(jìn)程中將管道的讀端描述符復(fù)制給標(biāo)準(zhǔn)輸入端,將另外管道的寫(xiě)端描述符復(fù)制給標(biāo)準(zhǔn)輸出端和標(biāo)準(zhǔn)容錯(cuò)端,即實(shí)現(xiàn)管道端口地址的重定向。這樣子進(jìn)程對(duì)標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出及標(biāo)準(zhǔn)容錯(cuò)的操作就反應(yīng)到了管道中。
QProcess在正常渠道模式下具體實(shí)現(xiàn)共用了五個(gè)無(wú)名管道進(jìn)行通信。五個(gè)管道的描述符分別用childpipe[2],stdinChannelpipe[2],stdoutChannelpipe[2],stderrChannelpipe[2]和deathpipe[2]五個(gè)數(shù)組來(lái)保存。deathpipe指代的管道會(huì)用在消亡的子進(jìn)程與父進(jìn)程之間。當(dāng)子進(jìn)程準(zhǔn)備撤銷(xiāo)時(shí)會(huì)發(fā)送一個(gè)表示該子進(jìn)程消亡的字符給父進(jìn)程來(lái)等待父進(jìn)程進(jìn)行處理。stdinChannelpipe,stdoutChannelpipe和stderrChannelpipe所指代的管道分別與標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)容錯(cuò)進(jìn)行綁定,實(shí)現(xiàn)了與外部程序的通信。childpipe指代的管道主要是為父子進(jìn)程之間的通信而建立的。
如果在管道中有新數(shù)據(jù)寫(xiě)入,就會(huì)通知相應(yīng)進(jìn)程去讀。另外圖2是QProcess在正常渠道模式下的通信原理圖,如果是在融合渠道模式下,將沒(méi)有容錯(cuò)管道,此時(shí)原理圖中將沒(méi)有***個(gè)管道,也就不會(huì)有管道描述符。同時(shí),標(biāo)準(zhǔn)容錯(cuò)端和標(biāo)準(zhǔn)輸出端將共同掛接到子進(jìn)程的stdoutChannelpipe的寫(xiě)端,來(lái)實(shí)現(xiàn)內(nèi)外進(jìn)程的通信。
(2) QProcess應(yīng)用方式
由于QProcess類(lèi)實(shí)現(xiàn)了對(duì)底層通信方式較為完善的封裝,因此利用QProcess類(lèi)將更為方便的實(shí)現(xiàn)對(duì)外部應(yīng)用程序的調(diào)用。在此,通過(guò)在QT界面中調(diào)用外部mplayer的例子來(lái)簡(jiǎn)單說(shuō)明QProcess的應(yīng)用方式。
- const QString mplayerPath("/mnt/yaffs/mplayer");
 - const QString musicFile("/mnt/yaffs/music/sound.mp3");
 - QProcess* mplayerProcess=new QProcess();
 - QStringList args;
 - args<<"-slave";
 - args<<"-quiet";
 - args << "-wid";
 - args<<musicFile;
 - mplayerProcess->setProcessChannelMode(QProcess::MergedChannels);
 - yProcess->start(mplayerPath,args);
 
***行指明了所要調(diào)用的外部應(yīng)用程序mplayer的位置。第二行指明了所要播放的歌曲文件及地址。第五行設(shè)置mplayer為后臺(tái)模式。在此模式下,mplayer將從標(biāo)準(zhǔn)輸入中讀得信息,并通過(guò)標(biāo)準(zhǔn)輸出向主進(jìn)程發(fā)送信息。六七行為mplayer運(yùn)行的參數(shù)。第九行為設(shè)置進(jìn)程渠道的模式為融合模式,即將標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)容錯(cuò)綁定到同一個(gè)管道的寫(xiě)端。第十行為啟動(dòng)外部應(yīng)用程序mplayer。內(nèi)核中管道及通信環(huán)境的建立都是在此步中完成的。
mplayer在slave模式下運(yùn)行會(huì)自動(dòng)從標(biāo)準(zhǔn)輸入中讀取信息并執(zhí)行。由QProcess的通信原理可知,管道的讀端描述符stdinChannelpipe[0]復(fù)制給了標(biāo)準(zhǔn)輸入,即標(biāo)準(zhǔn)輸入的描述符也為stdinChannelpipe[0],因此按照標(biāo)準(zhǔn)輸入的描述符去讀信息就是到stdinChannelpipe所對(duì)應(yīng)的管道中讀取信息。所以如果想在QT的主進(jìn)程中發(fā)送命令使mplayer退出,只需在主程序中向stdinChannelpipe[1]端寫(xiě)入命令quit就可以,執(zhí)行語(yǔ)句為myProcess->write(”quit\n”);(此處的write()函數(shù)為QProcess類(lèi)的成員函數(shù),具體實(shí)現(xiàn)就是向stdinChannelpipe[1]端寫(xiě)入信息)
(3)QProcess的發(fā)展及分析
QProcess類(lèi)伴隨著QT/Embedded的發(fā)展逐漸趨于完善。在QTE2及其更前版本中還沒(méi)有QProcess類(lèi),如果想實(shí)現(xiàn)與外部應(yīng)用程序的通信,必須要自己實(shí)現(xiàn)對(duì)管道或socket的建立與重定向。到了QTE3版本,就實(shí)現(xiàn)了對(duì)QProcess類(lèi)的封裝。在QTE3的版本中,QProcess類(lèi)的實(shí)現(xiàn)是通過(guò)應(yīng)用socket來(lái)建立主進(jìn)程與外部應(yīng)用程序之間通信的。通信原理與圖3所示基本相同,只是將圖中的管道描述符改為是socket的描述符即可。QT主程序在建立成對(duì)socket描述符時(shí)需要調(diào)用Linux系統(tǒng)函數(shù)socketpair()。在生成的成對(duì)socket描述符之間可以實(shí)現(xiàn)父子進(jìn)程之間的雙向通信,即無(wú)論是socket的0套接口還是1套接口都可進(jìn)行讀寫(xiě)。
但為了避免出現(xiàn)通信過(guò)程中父子進(jìn)程對(duì)同一個(gè)socket的爭(zhēng)奪,例如,在子進(jìn)程還未將父進(jìn)程發(fā)送的信息全部讀出時(shí),子進(jìn)程又要求將自己產(chǎn)生的數(shù)據(jù)返回給父進(jìn)程。如果父子進(jìn)程雙向通信只用一個(gè)socket來(lái)完成,就會(huì)出現(xiàn)父子進(jìn)程發(fā)送的信息混亂情況。因此,對(duì)于QProcess的實(shí)現(xiàn)仍然必須通過(guò)多個(gè)socket來(lái)共同完成。
由上面的描述可知,盡管socket有雙向通信功能,但在實(shí)現(xiàn)QProcess過(guò)程中只是利用socket實(shí)現(xiàn)了單向通信功能。因此既浪費(fèi)了對(duì)資源的利用又增加了系統(tǒng)的開(kāi)銷(xiāo)。為了解決此問(wèn)題,QTE4版本將QProcess的通信連接方式由socket改為了只能實(shí)現(xiàn)單向通信的無(wú)名管道來(lái)實(shí)現(xiàn)。通信原理就是以上3.1 QProcess通信機(jī)制中所描述的。
3、其它通信方式
除了上面介紹的無(wú)名管道和socket通信方式外,一般操作系統(tǒng)中常用的進(jìn)程間通信機(jī)制也都可以用于QT系統(tǒng)內(nèi)部不同進(jìn)程之間的通信,如消息隊(duì)列、共享內(nèi)存、信號(hào)量、有名管道等機(jī)制。其中信號(hào)量機(jī)制在QT中已經(jīng)重新進(jìn)行了封裝;有些機(jī)制則可以直接通過(guò)操作系統(tǒng)的系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)。另外,如果我們只是想通過(guò)管道或socket來(lái)實(shí)現(xiàn)較簡(jiǎn)單的外部通信,也可以重新創(chuàng)建管道或socket來(lái)實(shí)現(xiàn)自己要求的功能。例如,還是在QT主程序中調(diào)用外部mplayer。如果我們只是想在QT主程序中控制mplayer,而不要求得到mplayer輸出的信息。則可以按照以下方式來(lái)實(shí)現(xiàn):
- const char* mplayerPath = "/mnt/yaffs/mplayer";
 - const char* musicFile = "/mnt/yaffs/music/sound.mp3";
 - const char* arg[5];
 - arg[0] = mplayerPath;
 - arg[1] = "-slave";
 - arg[2] = "-quiet";
 - arg[3] = musicFile;
 - arg[4] = NULL;
 - int fd[2],pid;
 - if(pipe(fd)<0)
 - printf("creating pipe is error\n");
 - else while((pid=fork())<0);
 - if(pid==0)
 - {
 - ::close(fd[1]);
 - ::dup2(fd[0],STDIN_FILENO);
 - execvp(arg[0],(const* char*)arg);
 - }
 - else{
 - ::close(fd[0]);}
 
第1到8行與前面QProcess類(lèi)實(shí)現(xiàn)調(diào)用mplayer一樣,是用來(lái)指明mplayer運(yùn)行時(shí)參數(shù)的。第10行是創(chuàng)建一個(gè)管道。第12行是創(chuàng)建一個(gè)子進(jìn)程。15,20行是關(guān)閉父子進(jìn)程中沒(méi)用的管道描述符。此時(shí)可結(jié)合圖2.1和圖2.2來(lái)理解從父進(jìn)程到子進(jìn)程通信環(huán)境的建立。第16行是把子進(jìn)程的讀端與標(biāo)準(zhǔn)輸入綁定,以便mplayer能夠接收到父進(jìn)程發(fā)出的命令。17行就是從子進(jìn)程中調(diào)用外部mplayer的實(shí)現(xiàn)。此時(shí),程序執(zhí)行后,mplayer就可以運(yùn)行起來(lái)。如果想在QT主程序中通過(guò)發(fā)送命令使mplayer退出,就在管道的寫(xiě)端寫(xiě)入命令"quit"就可以。實(shí)現(xiàn)語(yǔ)句為write(fd[1], "quit",strlen("quit"));
該例子說(shuō)明了QT通信方式運(yùn)用的靈活性,可以根據(jù)實(shí)際情況進(jìn)行應(yīng)用。同時(shí)該例子的實(shí)現(xiàn)方式正是利用了QProcess類(lèi)實(shí)現(xiàn)的機(jī)制,因此可以結(jié)合這個(gè)例子更加深刻的理解QProcess類(lèi)的實(shí)現(xiàn)機(jī)制。
小結(jié):QT進(jìn)程間通信 的內(nèi)容介紹完了,希望本文對(duì)你也剖幫助,其實(shí)里面有很多內(nèi)容是我們?cè)趯W(xué)習(xí)過(guò)程蘇接觸到的!















 
 
 




 
 
 
 