標籤:
目前有一個查詢程式 get_user_id 是用C寫的,python需要調用這個程式:使用 get_user_id "使用者名稱" 可以得到輸出: "ID0002451". 在python中使用管道可以方便的調用這個進程並得到它的標準輸出: get_user_id 是C寫的,程式大概的架構如下:得到一個命令列參數,然後根據參數列印其相應的id:
int main(int argc , char* args[])
{
char * name = args[1]
printf("%s",getid(name) );
}
python程式調用的片段如下: pipe = os.popen( "get_user_id " + "myname")
user_id = pipe.read() pipe.close()
python是這樣調用get_user_id程式的。 首先python會啟動一個子進程,然後讀入子進程的標準輸出,最後結束子進程。 這裡的主要的開銷是進程的啟動和撤銷,管道之間的通訊是非常快的。 如果get_user_id調用得不太頻繁,則這種調用方式沒有問題;若get_user_id調用得很頻繁,以致它的效能成為了系統的瓶頸,這就有必要最佳化了。最佳化的方法是使得get_user_id子進程常駐記憶體,python父進程可以使用write/flush和readline的方法和子進程通訊;程式結束時撤銷子進程。 進程常駐記憶體後,python只需要啟動一次子進程就可以滿足任意次的查詢。不過帶來的麻煩是get_user_id程式需要重寫:將它改為在迴圈中接收標準輸入,把結果送往標準輸出,另外約定一個特殊的輸入(如"EOF"),子進程收到這個輸入後退出迴圈。 get_user_id改後
int main(int argc , char* args[])
{ char name[512]; // buff
while( scanf("%s", name) ){ if (!strcmp(name,"EOF")) return 0;
printf("%s\n",getid(name) ); }
}
這個C程式先接受標準輸入,判斷輸入是否結束為結束標誌,如果是則返回,否則列印使用者ID。 Python應該如何調用這個進程呢?使用popen()得到的pipe對象只支援write或者read操作,而不能同時讀和寫。 需要同時允許讀寫操作,使用popen2()。該函數返回一個包涵兩個元素的tuple,這兩個元素依次是寫管道(即可向目標進程寫入標準輸入)和讀管道(即讀進程的標準輸出)。 如果需要和上述C程式打交道,則應該這樣開啟管道 pipe_out , pipe_in = popen2( " get_user_id " , "wr"); "wr"表示次管道需要先寫後讀。 具體例子如下:進行10000次查詢 import os
if __name__ == "__main__":
pipe_in , pipe_out = popen2("get_user_id", "wr");
for i in range(10000)
pipe_in.write("myname");
pipe_in.write("\n"); #需要分行符號
pipe_in.flush(); #需要清空緩衝區
userid = pipe_out.readline(); #讀入結果
上述情況需要注意的是,為了和進程順利互動,一般需要flush,防止io緩衝;盡量寫入和讀入整行,方便控制管道互動過程。 根據筆者實驗顯示:使用常駐記憶體的進程進行10000次調用,比不常駐記憶體的進程快得多,前者耗費的時間大約是後者的1/5,不過這個結果只是筆者的特例。請讀者分析自己的應用場合,看看如何使用python強大的管道工具。 ps,python還有popen3,可開啟進程的stdin,stdout和stderr,更好更強大。另外subprocess 模組下有更強大的進程管理介面。
Python和其他進程的管道通訊方式--popen和popen2的比較