Unix系統下程式間通訊方式及比較

cyxlzzs發表於2014-08-27

本文轉載自: http://cqgw2.blog.163.com/blog/static/2352470201032210542930/


程式間的通訊方式:

<wbr><wbr> 1.管道(pipe)及有名管道(named pipe):</wbr></wbr>

<wbr><wbr><wbr><wbr><wbr>管道可用於具有親緣關係程式間的通訊,有名管道除了具有管道所具有的功能外,它還允許無親緣關係程式間的通訊。</wbr></wbr></wbr></wbr></wbr>

2.訊號(signal):

<wbr><wbr><wbr><wbr> 訊號是在軟體層次上對中斷機制的一種模擬,它是比較複雜的通訊方式,用於通知程式有某事件發生,一個程式收到一個訊號與處理器收到一箇中斷請求效果上可以說是一致得。</wbr></wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr> 3.訊息佇列(message queue):</wbr></wbr>

<wbr><wbr><wbr><wbr> 訊息佇列是訊息的連結表,它克服了上兩種通訊方式中訊號量有限的缺點,具有寫許可權得程式可以按照一定得規則向訊息佇列中新增新資訊;對訊息佇列有讀許可權得程式則可以從訊息佇列中讀取資訊。</wbr></wbr></wbr></wbr>

訊息緩衝通訊技術是由Hansen首先提出的,其基本思想是:根據”生產者-消費者”原理,利用記憶體中公用訊息緩衝區實現程式之間的資訊交換.

記憶體中開闢了若干訊息緩衝區,用以存放訊息.每當一個程式向另一個程式傳送訊息時,便申請一個訊息緩衝區,並把已準備好的訊息送到緩衝區,然後把該訊息緩衝區插入到接收程式的訊息佇列中,最後通知接收程式.接收程式收到傳送里程發來的通知後,從本程式的訊息佇列中摘下一訊息緩衝區,取出所需的資訊,然後把訊息緩衝區不定期給系統.系統負責管理公用訊息緩衝區以及訊息的傳遞.

一個程式可以給若干個程式傳送訊息,反之,一個程式可以接收不同程式發來的訊息.顯然,程式中關於訊息佇列的操作是臨界區.當傳送程式正往接收程式的訊息佇列中新增一條訊息時,接收程式不能同時從該訊息佇列中到出訊息:反之也一樣.

訊息緩衝區通訊機制包含以下列內容:

(1) 訊息緩衝區,這是一個由以下幾項組成的資料結構:
1、 訊息長度
2、 訊息正文
3、 傳送者
4、 訊息佇列指標

(2)訊息佇列首指標m-q,一般儲存在PCB中。
(1) 互斥訊號量m-mutex,初值為1,用於互斥訪問訊息佇列,在PCB中設定。
(2) 同步訊號量m-syn,初值為0,用於訊息計數,在PCB中設定。
(3) 傳送訊息原語send
(4) 接收訊息原語receive(a)

<wbr></wbr>

<wbr><wbr> 4.共享記憶體(shared memory):</wbr></wbr>

<wbr><wbr><wbr><wbr> 可以說這是最有用的程式間通訊方式。它使得多個程式可以訪問同一塊記憶體空間,不同程式可以及時看到對方程式中對共享記憶體中資料得更新。這種方式需要依靠某種同步操作,如互斥鎖和訊號量等。</wbr></wbr></wbr></wbr>

這種通訊模式需要解決兩個問題:第一個問題是怎樣提供共享記憶體;第二個是公共記憶體的互斥關係則是程式開發人員的責任。<wbr></wbr>

<wbr><wbr> 5.訊號量(semaphore):</wbr></wbr>

<wbr><wbr><wbr><wbr> 主要作為程式之間及同一種程式的不同執行緒之間得同步和互斥手段。</wbr></wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr> 6.套接字(socket);</wbr></wbr>

<wbr><wbr><wbr><wbr> 這是一種更為一般得程式間通訊機制,它可用於網路中不同機器之間的程式間通訊,應用非常廣泛。</wbr></wbr></wbr></wbr>

http://blog.csdn.net/eroswang/archive/2007/09/04/1772350.aspx

linux下的程式間通訊-詳解

詳細的講述程式間通訊在這裡絕對是不可能的事情,而且筆者很難有信心說自己對這一部分內容的認識達到了什麼樣的地步,所以在這一節的開頭首先向大家推薦著 名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文譯本《UNIX環境高階程式設計》已有機械工業出版社出版,原文精彩,譯文同樣地道,如果你的確對在Linux下程式設計有濃 厚的興趣,那麼趕緊將這本書擺到你的書桌上或計算機旁邊來。說這麼多實在是難抑心中的景仰之情,言歸正傳,在這一節裡,我們將介紹程式間通訊最最初步和最 最簡單的一些知識和概念。
   首先,程式間通訊至少可以通過傳送開啟檔案來實現,不同的程式通過一個或多個檔案來傳遞資訊,事實上,在很多應用系統裡,都使用了這種方法。但一般說來, 程式間通訊(IPC:InterProcess Communication)不包括這種似乎比較低階的通訊方法。Unix系統中實現程式間通訊的方法很多,而且不幸的是,極少方法能在所有的Unix系 統中進行移植(唯一一種是半雙工的管道,這也是最原始的一種通訊方式)。而Linux作為一種新興的作業系統,幾乎支援所有的Unix下常用的程式間通訊 方法:管道、訊息佇列、共享記憶體、訊號量、套介面等等。下面我們將逐一介紹。

   2.3.1 管道
   管道是程式間通訊中最古老的方式,它包括無名管道和有名管道兩種,前者用於父程式和子程式間的通訊,後者用於執行於同一臺機器上的任意兩個程式間的通訊
   無名管道由pipe()函式建立:
   #include <unistd.h>
   int pipe(int filedis[2]);
   引數filedis返回兩個檔案描述符:filedes[0]為讀而開啟,filedes[1]為寫而開啟。filedes[1]的輸出是filedes[0]的輸入。下面的例子示範瞭如何在父程式和子程式間實現通訊。

#define INPUT 0
#define OUTPUT 1

void main() {
int file_descriptors[2];
/*定義子程式號 */
pid_t pid;
char buf[256];
int returned_count;
/*建立無名管道*/
pipe(file_descriptors);
/*建立子程式*/
if((pid = fork()) == -1) {
printf("Error in fork\n");
exit(1);
}
/*執行子程式*/
if(pid == 0) {
printf("in the spawned (child) process...\n");
/*子程式向父程式寫資料,關閉管道的讀端*/
close(file_descriptors[INPUT]);
write(file_descriptors[OUTPUT], "test da<wbr>ta", strlen("test da<wbr>ta")); <br> exit(0); <br> } else { <br> /*執行父程式*/ <br> printf("in the spawning (parent) process...\n"); <br> /*父程式從管道讀取子程式寫的資料,關閉管道的寫端*/ <br> close(file_descriptors[OUTPUT]); <br> returned_count = read(file_descriptors[INPUT], buf, sizeof(buf)); <br> printf("%d bytes of da<wbr>ta received from spawned process: %s\n", <br> returned_count, buf); <br> } <br> } <br>    在Linux系統下,有名管道可由兩種方式建立:<span style="color:#99330">命令列方式mknod系統呼叫和函式mkfifo</span>。下面的兩種途徑都在當前目錄下生成了一個名為myfifo的有名管道: <br>      方式一:mkfifo("myfifo","rw"); <br>      方式二:mknod myfifo p <br>    <span style="color:#99330">生成了有名管道後,就可以使用一般的檔案I/O函式如open、close、read、write等來對它進行操作</span>。下面即是一個簡單的例子,假設我們已經建立了一個名為myfifo的有名管道。 <br>   /* 程式一:讀有名管道*/ <br> #include &lt;stdio.h&gt; <br> #include &lt;unistd.h&gt; <br> void main() { <br> FILE * in_file; <br> int count = 1; <br> char buf[80]; <br> in_file = fopen("mypipe", "r"); <br> if (in_file == NULL) { <br> printf("Error in fdopen.\n"); <br> exit(1); <br> } <br> while ((count = fread(buf, 1, 80, in_file)) &gt; 0) <br> printf("received from pipe: %s\n", buf); <br> fclose(in_file); <br> } <br>   /* 程式二:寫有名管道*/ <br> #include &lt;stdio.h&gt; <br> #include &lt;unistd.h&gt; <br> void main() { <br> FILE * out_file; <br> int count = 1; <br> char buf[80]; <br> out_file = fopen("mypipe", "w"); <br> if (out_file == NULL) { <br> printf("Error opening pipe."); <br> exit(1); <br> } <br> sprintf(buf,"this is test da<wbr>ta for the named pipe example\n"); <br> fwrite(buf, 1, 80, out_file); <br> fclose(out_file); <br> } <br><br>    2.3.2 訊息佇列 <br>    訊息佇列用於執行於同一臺機器上的程式間通訊,它和管道很相似,是一個在系統核心中用來儲存訊息的佇列,它在系統核心中是以訊息連結串列的形式出現。訊息連結串列中節點的結構用msg宣告。<br> 事實上,它是一種<span style="color:#99330">正逐漸被淘汰</span>的通訊方式,我們可以用流管道或者套介面的方式來取代它,所以,我們對此方式也不再解釋,也建議讀者忽略這種方式。 <br><br>    2.3.3 共享記憶體 <br>     共享記憶體是執行在同一臺機器上的<span style="color:#00ff">程式間通訊最快的方式</span>,因為資料不需要在不同的程式間複製。通常由一個程式建立一塊共享記憶體區,其餘程式對這塊記憶體區進行 讀寫。得到共享記憶體有兩種方式:<span style="color:#99330">對映/dev/mem裝置</span>和<span style="color:#99330">記憶體映像檔案</span>。<span style="color:#99330">前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的將是 實際的實體記憶體</span>,在Linux系統下,這隻有通過限制Linux系統存取的記憶體才可以做到,這當然不太實際。常用的方式是通過shmXXX函式族來實現利 用共享記憶體進行儲存的。 <br>    首先要用的函式是shmget,它獲得一個共享儲存識別符號。 <br><br>      #include &lt;sys/types.h&gt; <br>      <span style="color:#99330">#include &lt;sys/ipc.h&gt; </span><br style="color:rgb(153,51,0)"><span style="color:#99330">     #include &lt;sys/shm.h&gt; <br><br></span>      int shmget(key_t key, int size, int flag); <br>    <span style="color:#ff660"> 這個函式有點類似大家熟悉的malloc函式,系統按照請求分配size大小的記憶體用作共享記憶體</span>。Linux系統核心中每個IPC結構都有的一個非負整數 的識別符號,這樣對一個訊息佇列傳送訊息時只要引用識別符號就可以了。<span style="color:#0080">這個識別符號是核心由IPC結構的關鍵字得到的,這個關鍵字,就是上面第一個函式的 key。</span>資料型別key_t是在標頭檔案sys/types.h中定義的,它是一個長整形的資料。在我們後面的章節中,還會碰到這個關鍵字。 <br>    <br><span style="color:#99330">當共享記憶體建立後,其餘程式可以呼叫shmat()將其連線到自身的地址空間中</span>。 <br>    void *shmat(int shmid, void *addr, int flag); <br>    shmid為shmget函式返回的共享儲存識別符號,addr和flag引數決定了以什麼方式來確定連線的地址,函式的返回值即是該程式資料段所連線的實際地址,程式可以對此程式進行讀寫操作。 <br>     使用共享儲存來實現程式間通訊的注意點是對資料存取的同步,必須確保當一個程式去讀取資料時,它所想要的資料已經寫好了。通常,<span style="color:#0080">訊號量被要來實現對共享存 儲資料存取的同步</span>,另外,<span style="color:#0080">可以通過使用shmctl函式設定共享儲存記憶體的某些標誌位如</span><span style="color:#0080; font-weight:bold">SHM_LOCK、SHM_UNLOCK</span><span style="color:#0080">等來實現</span>。 <br><br>    2.3.4 訊號量 <br>    訊號量又稱為訊號燈,它是用來協調不同程式間的資料物件的,而最主要的應用是前一節的共享記憶體方式的程式間通訊。本質上,<span style="color:#0080">訊號量是一個計數器,它用來記錄對某個資源(如共享記憶體)的存取狀況</span>。一般說來,為了獲得共享資源,程式需要執行下列操作: <br>    (1) 測試控制該資源的訊號量。 <br>    (2) 若此訊號量的值為正,則允許進行使用該資源。<span style="color:#99330">程式將訊號量減1</span>。 <br>    (3) 若此訊號量為0,則該資源目前不可用,程式進入睡眠狀態,直至訊號量值大於0,程式被喚醒,轉入步驟(1)。 <br>    (4) 當程式不再使用一個訊號量控制的資源時,訊號量值加1。如果此時有程式正在睡眠等待此訊號量,則喚醒此程式。 <br>    <span style="color:#99330"> 維護訊號量狀態的是Linux<span style="color:#00ff">核心</span>作業系統而不是使用者程式</span>。我們可以從標頭檔案/usr/src/linux/include /linux /sem.h 中看到核心用來維護訊號量狀態的各個結構的定義。<span style="color:#99330">訊號量是一個資料集合,使用者可以單獨使用這一集合的每個元素。</span>要呼叫的第一個函式是semget,用以獲 得一個訊號量ID。 <br><br> struct sem {<br> short sempid;/* pid of last operaton */<br> ushort semval;/* current value */<br> ushort semncnt;/* num procs awaiting increase in semval */<br> ushort semzcnt;/* num procs awaiting semval = 0 */<br> }<br><br>    #include &lt;sys/types.h&gt; <br>    #include &lt;sys/ipc.h&gt; <br>    #include &lt;sys/sem.h&gt; <br>    int semget(key_t key, int nsems, int flag); <br><br>   <span style="color:#99330">key是前面講過的IPC結構的關鍵字,<span style="color:#0080"><span style="color:#333333">flag</span>將來決定是建立新的訊號量集合,還是引用一個現有的訊號量集合。</span></span>nsems是該集合中的訊號量數。如果是建立新 集合(一般在伺服器中),則必須指定nsems;如果是引用一個現有的訊號量集合(一般在客戶機中)則將nsems指定為0。 <br><br>    semctl函式用來對訊號量進行操作。 <br>    int semctl(int semid, int semnum, int cmd, union semun arg); <br>    不同的操作是通過cmd引數來實現的,在標頭檔案sem.h中定義了7種不同的操作,實際程式設計時可以參照使用。 <br>    <br> semop函式<span style="color:#99330">自動執行訊號量集合上的運算元組</span>。 <br>    int semop(int semid, struct sembuf semoparray[], size_t nops); <br>    semoparray是一個指標,它指向一個訊號量運算元組。nops規定該陣列中操作的數量。 <br><br>    下面,我們看一個具體的例子,它建立一個特定的IPC結構的關鍵字和一個訊號量,建立此訊號量的索引,修改索引指向的訊號量的值,最後我們清除訊號量。在下面的程式碼中,函式ftok生成我們上文所說的唯一的IPC關鍵字。 <br><br> #include &lt;stdio.h&gt; <br> #include &lt;sys/types.h&gt; <br> #include &lt;sys/sem.h&gt; <br> #include &lt;sys/ipc.h&gt; <br> void main() { <br> key_t unique_key; /* 定義一個IPC關鍵字*/ <br> int id; <br> struct sembuf lock_it; <br> union semun options; <br> int i; <br><br> unique_key = ftok(".", 'a'); /* 生成關鍵字,字元'a'是一個隨機種子*/ <br> /* 建立一個新的訊號量集合*/ <br> id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666); <br> printf("semaphore id=%d\n", id); <br> options.val = 1; /*設定變數值*/ <br> semctl(id, 0, SETVAL, options); /*設定索引0的訊號量*/ <br><br> /*列印出訊號量的值*/ <br> i = semctl(id, 0, GETVAL, 0); <br> printf("value of semaphore at index 0 is %d\n", i); <br><br> /*下面重新設定訊號量*/ <br> lock_it.sem_num = 0; /*設定哪個訊號量*/ <br> lock_it.sem_op = -1; /*定義操作*/ <br> lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/ <br> if (semop(id, &amp;lock_it, 1) == -1) { <br> printf("can not lock semaphore.\n"); <br> exit(1); <br> } <br><br> i = semctl(id, 0, GETVAL, 0); <br> printf("value of semaphore at index 0 is %d\n", i); <br><br> /*清除訊號量*/ <br> semctl(id, 0, IPC_RMID, 0); <br> } <br></wbr></wbr></wbr></wbr>

semget()


可以使用系統呼叫semget()建立一個新的訊號量集,或者存取一個已經存在的訊號量集:

系統呼叫:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,則返回訊號量集的IPC識別符號。如果失敗,則返回-1:errno=EACCESS(沒有許可權)
EEXIST(訊號量集已經存在,無法建立)
EIDRM(訊號量集已經刪除)
ENOENT(訊號量集不存在,同時沒有使用IPC_CREAT)
ENOMEM(沒有足夠的記憶體建立新的訊號量集)
ENOSPC(超出限制)
系統呼叫semget()的第一個引數是關鍵字值(一般是由系統呼叫ftok()返回的)。系統核心將此值和系統中存在的其他的訊號量集的關鍵字值進行比較。開啟和存取操作與引數semflg中的內容相關。IPC_CREAT如果訊號量集在系統核心中不存在,則建立訊號量集。IPC_EXCL當和 IPC_CREAT一同使用時,如果訊號量集已經存在,則呼叫失敗。如果單獨使用IPC_CREAT,則semget()要麼返回新建立的訊號量集的識別符號,要麼返回系統中已經存在的同樣的關鍵字值的訊號量的識別符號。如果IPC_EXCL和IPC_CREAT一同使用,則要麼返回新建立的訊號量集的識別符號,要麼返回-1。IPC_EXCL單獨使用沒有意義。引數nsems指出了一個新的訊號量集中應該建立的訊號量的個數。訊號量集中最多的訊號量的個數是在linux/sem.h中定義的:
#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一個開啟和建立訊號量集的程式:
intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
};
==============================================================
semop()


系統呼叫:semop();
呼叫原型:int semop(int semid,struct sembuf*sops,unsign ednsops);
返回值:0,如果成功。-1,如果失敗:errno=E2BIG(nsops大於最大的ops數目)
EACCESS(許可權不夠)
EAGAIN(使用了IPC_NOWAIT,但操作不能繼續進行)
EFAULT(sops指向的地址無效)
EIDRM(訊號量集已經刪除)
EINTR(當睡眠時接收到其他訊號)
EINVAL(訊號量集不存在,或者semid無效)
ENOMEM(使用了SEM_UNDO,但無足夠的記憶體建立所需的資料結構)
ERANGE(訊號量值超出範圍)

第一個引數是關鍵字值。第二個引數是指向將要操作的陣列的指標。第三個引數是陣列中的操作的個數。引數sops指向由sembuf組成的陣列。此陣列是在linux/sem.h中定義的:
/*semop systemcall takes an array of these*/
structsembuf{
ushortsem_num;/*semaphore index in array*/
shortsem_op;/*semaphore operation*/
shortsem_flg;/*operation flags*/
sem_num將要處理的訊號量的個數。
sem_op要執行的操作。
sem_flg操作標誌。
如果sem_op是負數,那麼訊號量將減去它的值。這和訊號量控制的資源有關。如果沒有使用IPC_NOWAIT,那麼呼叫程式將進入睡眠狀態,直到訊號量控制的資源可以使用為止。如果sem_op是正數,則訊號量加上它的值。這也就是程式釋放訊號量控制的資源。最後,如果sem_op是0,那麼呼叫程式將呼叫sleep(),直到訊號量的值為0。這在一個程式等待完全空閒的資源時使用。
===============================================================
semctl()


系統呼叫:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,則為一個正數。
如果失敗,則為-1:errno=EACCESS(許可權不夠)
EFAULT(arg指向的地址無效)
EIDRM(訊號量集已經刪除)
EINVAL(訊號量集不存在,或者semid無效)
EPERM(EUID沒有cmd的權利)
ERANGE(訊號量值超出範圍)

系統呼叫semctl用來執行在訊號量集上的控制操作。這和在訊息佇列中的系統呼叫msgctl是十分相似的。但這兩個系統呼叫的引數略有不同。因為訊號量一般是作為一個訊號量集使用的,而不是一個單獨的訊號量。所以在訊號量集的操作中,不但要知道IPC關鍵字值,也要知道訊號量集中的具體的訊號量。這兩個系統呼叫都使用了引數cmd,它用來指出要操作的具體命令。兩個系統呼叫中的最後一個引數也不一樣。在系統呼叫msgctl中,最後一個引數是指向核心中使用的資料結構的指標。我們使用此資料結構來取得有關訊息佇列的一些資訊,以及設定或者改變佇列的存取許可權和使用者。但在訊號量中支援額外的可選的命令,這樣就要求有一個更為複雜的資料結構。
系統呼叫semctl()的第一個引數是關鍵字值。第二個引數是訊號量數目。
引數cmd中可以使用的命令如下:
·IPC_STAT讀取一個訊號量集的資料結構semid_ds,並將其儲存在semun中的buf引數中。
·IPC_SET設定訊號量集的資料結構semid_ds中的元素ipc_perm,其值取自semun中的buf引數。
·IPC_RMID將訊號量集從記憶體中刪除。
·GETALL用於讀取訊號量集中的所有訊號量的值。
·GETNCNT返回正在等待資源的程式數目。
·GETPID返回最後一個執行semop操作的程式的PID。
·GETVAL返回訊號量集中的一個單個的訊號量的值。
·GETZCNT返回這在等待完全空閒的資源的程式數目。
·SETALL設定訊號量集中的所有的訊號量的值。
·SETVAL設定訊號量集中的一個單獨的訊號量的值。
引數arg代表一個semun的例項。semun是在linux/sem.h中定義的:
/*arg for semctl systemcalls.*/
unionsemun{
intval;/*value for SETVAL*/
structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/
ushort*array;/*array for GETALL&SETALL*/
structseminfo*__buf;/*buffer for IPC_INFO*/
void*__pad;
val當執行SETVAL命令時使用。buf在IPC_STAT/IPC_SET命令中使用。代表了核心中使用的訊號量的資料結構。array在使用GETALL/SETALL命令時使用的指標。
下面的程式返回訊號量的值。當使用GETVAL命令時,呼叫中的最後一個引數被忽略:
intget_sem_val(intsid,intsemnum)
{
return(semctl(sid,semnum,GETVAL,0));
}
下面是一個實際應用的例子:
#defineMAX_PRINTERS5
printer_usage()
{
int x;
for(x=0;x<MAX_PRINTERS;x++)
printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));
}
下面的程式可以用來初始化一個新的訊號量值:
void init_semaphore(int sid,int semnum,int initval)
{
union semunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
}
注意系統呼叫semctl中的最後一個引數是一個聯合型別的副本,而不是一個指向聯合型別的指標。


   2.3.5 套介面
    套介面(socket)程式設計是實現Linux系統和其他大多數作業系統中程式間通訊的主要方式之一。我們熟知的WWW服務、FTP服務、TELNET服務 等都是基於套介面程式設計來實現的。除了在異地的計算機程式間以外,套介面同樣適用於本地同一臺計算機內部的程式間通訊。關於套介面的經典教材同樣是 Richard Stevens編著的《Unix網路程式設計:聯網的API和套接字》,清華大學出版社出版了該書的影印版。它同樣是Linux程式設計師的必備書籍之一。
    關於這一部分的內容,可以參照本文作者的另一篇文章《設計自己的網路螞蟻》,那裡由常用的幾個套介面函式的介紹和示例程式。這一部分或許是Linux程式 間通訊程式設計中最須關注和最吸引人的一部分,畢竟,Internet 正在我們身邊以不可思議的速度發展著,如果一個程式設計師在設計編寫他下一個程式的時候,根本沒有考慮到網路,考慮到Internet,那麼,可以說,他的設 計很難成功。

3 Linux的程式和Win32的程式/執行緒比較
   熟悉WIN32程式設計的人一定知道,WIN32的程式管理方式與Linux上有著很大區別,在UNIX裡,只有程式的概念,但在WIN32裡卻還有一個"執行緒"的概念,那麼Linux和WIN32在這裡究竟有著什麼區別呢?
    WIN32裡的程式/執行緒是繼承自OS/2的。在WIN32裡,"程式"是指一個程式,而"執行緒"是一個"程式"裡的一個執行"線索"。從核心上講, WIN32的多程式與Linux並無多大的區別,在WIN32裡的執行緒才相當於Linux的程式,是一個實際正在執行的程式碼。但是,WIN32裡同一個進 程裡各個執行緒之間是共享資料段的。這才是與Linux的程式最大的不同。
   下面這段程式顯示了WIN32下一個程式如何啟動一個執行緒。

int g;
DWORD WINAPI ChildProcess( LPVOID lpParameter ){
int i;
for ( i = 1; i <1000; i ++) {
g ++;
printf( "This is Child Thread: %d\n", g );
}
ExitThread( 0 );
};

void main()
{
int threadID;
int i;
g = 0;
CreateThread( NULL, 0, ChildProcess, NULL, 0, &threadID );
for ( i = 1; i <1000; i ++) {
g ++;
printf( "This is Parent Thread: %d\n", g );
}
}

    在WIN32下,使用CreateThread函式建立執行緒,與Linux下建立程式不同,WIN32執行緒不是從建立處開始執行的,而是由 CreateThread指定一個函式,執行緒就從那個函式處開始執行。此程式同前面的UNIX程式一樣,由兩個執行緒各列印1000條資訊。 threadID是子執行緒的執行緒號,另外,全域性變數g是子執行緒與父執行緒共享的,這就是與Linux最大的不同之處。大家可以看出,WIN32的程式/執行緒 要比Linux複雜,在Linux要實現類似WIN32的執行緒並不難,只要fork以後,讓子程式呼叫ThreadProc函式,並且為全域性變數開設共享 資料區就行了,但在WIN32下就無法實現類似fork的功能了。所以現在WIN32下的C語言編譯器所提供的庫函式雖然已經能相容大多數 Linux/UNIX的庫函式,但卻仍無法實現fork。
   對於多工系統,共享資料區是必要的,但也是一個容易引起混亂的問題,在WIN32下,一個程式設計師很容易忘記執行緒之間的資料是共享的這一情況,一個執行緒修 改過一個變數後,另一個執行緒卻又修改了它,結果引起程式出問題。但在Linux下,由於變數本來並不共享,而由程式設計師來顯式地指定要共享的資料,使程式變 得更清晰與安全。
至於WIN32的"程式"概念,其含義則是"應用程式",也就是相當於UNIX下的exec了。
   Linux也有自己的多執行緒函式pthread,它既不同於Linux的程式,也不同於WIN32下的程式,關於pthread的介紹和如何在Linux環境下編寫多執行緒程式我們將在另一篇文章《Linux下的多執行緒程式設計》中講述。

相關文章