linux進程通信之CD程序

linux進程通信之CD程序linux

CD數據庫程序 web

如今咱們可使用咱們在這一章所瞭解的IPC工具來修改咱們的CD數據庫程序。數據庫


咱們可使用三種IPC工具的多種不一樣組,可是由於咱們須要傳送的信息不多,直接使用消息隊列實現請求的傳遞是一個很明顯的選擇。服務器


若是咱們須要傳遞的數據量很大,咱們能夠考慮使用共享內存傳遞實際的數據,而且使用信號量或是消息來傳遞一個標記通知其餘的進程在共享內存中有數據可用。併發


消息隊列接口解決咱們了在第11章所遇到的問題,即當數據傳遞時咱們須要兩個進程使得管道打開。使用消息隊列可使得一個進程將消息放入隊列,儘管這個進程是當前隊列的惟一用戶。app


咱們須要考慮的一個重要決定就是將答案返回給客戶。一個簡單的選擇就是使得一個隊列用於服務器並且每一個客戶有一個隊列。若是有大量的併發客戶,因爲須要大量的消息隊列就會引發問題。經過使用消息中的消息ID域,咱們可使得全部的用戶使用一個隊列,而且經過在消息中使用客戶端進程ID來將響應發送給指定的客戶端進程。從而每個客戶能夠接收只屬於他本身的消息,而將其餘客戶的消息留在隊列中。函數


要轉換咱們的CD程序來使用IPC工具,咱們只須要替換pipe_imp.c文件。在下面的部分,咱們將會描述替換文件ipc_imp.c的原則部分。工具


試驗--修改服務器函數 spa

1 首先,咱們包含正確的頭文件,聲明消息隊列鍵值,而且定義一個保存咱們消息數據的結構:命令行

1
2
3
4
5
6
7
8
9
10
11
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23include" class = "bdcs-inlinelink"   target= "_blank" >#include</a> 「cd_data.h」
#include 「cliserv.h」
#include <<a href="http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=sys%2Ftypes.h" class="bdcs-inlinelink" target="_blank">sys/types.h</a>>
#include <<a href="http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=sys%2Fipc.h" class="bdcs-inlinelink" target="_blank">sys/ipc.h</a>>
#include <sys/msg.h>
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23define" class = "bdcs-inlinelink"   target= "_blank" >#define</a> SERVER_MQUEUE 1234
#define CLIENT_MQUEUE 4321
struct  msg_passed {
     long  int  msg_key;  /* used for client pid */
     message_db_t real_message;
};

2 兩個全局變量保存由msgget函數所返回的兩個隊列標識符:

1
2
static  int  serv_qid = -1;
static  int  cli_qid = -1;

3 咱們使得服務器負責建立兩個消息隊列:

1
2
3
4
5
6
7
8
9
10
11
int  server_starting()
{
     # if  DEBUG_TRACE
         printf (「%d :- server_starting()/n」,  getpid());
     <a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23endif" class = "bdcs-inlinelink"   target= "_blank" >#endif</a>
     serv_qid = msgget((key_t)SERVER_MQUEUE, 0666 | IPC_CREAT);
     if  (serv_qid == -1)  return (0);
     cli_qid = msgget((key_t)CLIENT_MQUEUE, 0666 | IPC_CREAT);
     if  (cli_qid == -1)  return (0);
     return (1);
}


4 服務器同時負責退出時的清理工做。當服務器結束時,咱們設置咱們的全局變量爲非法值。這就會捕獲服務器嘗試在調用server_ending以後發送消息的bug。

1
2
3
4
5
6
7
8
9
10
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=void"   class = "bdcs-inlinelink"   target= "_blank" > void </a> server_ending()
{
     # if  DEBUG_TRACE
         printf (「%d :- server_ending()/n」, getpid());
     #endif
     ( void )<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=msgctl" class = "bdcs-inlinelink"   target= "_blank" >msgctl</a>(serv_qid, IPC_RMID, 0);
     ( void )msgctl(cli_qid, IPC_RMID, 0);
   serv_qid = -1;
   cli_qid = -1;
}

5 服務器read函數由隊列中讀取一條任意的消息,而且返回消息的數據部分。

1
2
3
4
5
6
7
8
9
10
11
12
int  read_request_from_client(message_db_t *rec_ptr)
{
     struct  msg_passed my_msg;
     # if  DEBUG_TRACE
         printf (「%d :- read_request_from_client()/n」,  getpid());
     #endif
     if  (msgrcv(serv_qid, ( void  *)&my_msg,  sizeof (*rec_ptr), 0, 0) == -1) {
         return (0);
     }
     *rec_ptr = my_msg.real_message;
     return (1);
}

6 使用存儲在清求中標識消息的客戶進程ID來發送響應:

1
2
3
4
5
6
7
8
9
10
11
12
13
int  send_resp_to_client( const  message_db_t mess_to_send)
{
     struct  msg_passed my_msg;
     # if  DEBUG_TRACE
         printf (「%d :- send_resp_to_client()/n」, getpid());
     #endif
     my_msg.real_message = mess_to_send;
     my_msg.msg_key = mess_to_send.client_pid;
     if  (msgsnd(cli_qid, ( void  *)&my_msg,  sizeof (mess_to_send), 0) == -1) {
         return (0);
     }
     return (1);
}


試驗--修改客戶端函數 

1 當客戶端啓動時,他須要發現服務器與客戶端隊列標識符。客戶端並不建立隊列。若是服務器沒有運行,這個函數就會失敗,由於消息隊列並不存在。

1
2
3
4
5
6
7
8
9
10
11
int  client_starting()
{
     # if  DEBUG_TRACE
         printf (「%d :- client_starting/n」,  getpid());
     #endif
     serv_qid = msgget((key_t)SERVER_MQUEUE, 0666);
     if  (serv_qid == -1)  return (0);
   cli_qid = msgget((key_t)CLIENT_MQUEUE, 0666);
   if  (cli_qid == -1)  return (0);
   return (1);
}


2 與服務器同樣,當客戶端結束時,咱們設置咱們的全局變量爲非法值。這將會捕獲當客戶端嘗試在調用client_ending以後發送消息的bug。

1
2
3
4
5
6
7
8
void  client_ending()
{
     # if  DEBUG_TRACE
         printf (「%d :- client_ending()/n」, getpid());
     #endif
     serv_qid = -1;
     cli_qid = -1;
}

3 要向服務器發送消息,咱們在咱們的結構中存儲數據。注意,咱們必須設置消息鍵值。由於0做爲鍵值是非法的,保留鍵值未定義就意味着他可使用一個隨機值,因此若是這個值剛好爲0時函數就會失敗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int  send_mess_to_server(message_db_t mess_to_send)
{
     struct  msg_passed my_msg;
     # if  DEBUG_TRACE
         printf (「%d :- send_mess_to_server()/n」, getpid());
     #endif
     my_msg.real_message = mess_to_send;
     my_msg.msg_key = mess_to_send.client_pid;
     if  (msgsnd(serv_qid, ( void  *)&my_msg,  sizeof (mess_to_send), 0) == -1) {
         perror (「Message send failed」);
         return (0);
     }
     return (1);
}

4 當客戶端服務器接收消息時,他會使用其進程ID來接收只發送給他的消息,而忽略其餘進程的消息。

1
2
3
4
5
6
7
8
9
10
11
12
int  read_resp_from_server(message_db_t *rec_ptr)
{
     struct  msg_passed my_msg;
     # if  DEBUG_TRACE
         printf (「%d :- read_resp_from_server()/n」,  getpid());
     #endif
     if  (msgrcv(cli_qid, ( void  *)&my_msg,  sizeof (*rec_ptr), getpid(), 0) == -1) {
         return (0);
     }
   *rec_ptr = my_msg.real_message;
   return (1);
}

5 要得到與pipe_imp.c的徹底兼容,咱們須要定義另外四個函數。然而,在咱們的新程序中,這個函數是空的。當使用管道時他們所實現的操做也再也不須要了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int  start_resp_to_client( const  message_db_t mess_to_send)
{
     return (1);
}
void  end_resp_to_client( void )
{
}
int  start_resp_from_server( void )
{
     return (1);
}
void  end_resp_from_server( void )
{
}


此程序到消息隊列的轉換演示了IPC消息隊列的強大。咱們須要更少的函數,而咱們所須要要比之前的實現少得多。

IPC狀態函數 


儘管X/Open並無要求,大多數的Linux提供了一個命令集合來容許命令行訪問IPC信息,而且清理無關聯的IPC工具。這就是ipcs與ipcrm命令,當咱們開發程序時,這是很是有用的。


編寫糟糕的程序或是由於某些緣由失敗的程序會遺留其IPC資源。這會使得新的程序調用失敗,由於程序指望以一個乾淨的系統開始,可是卻發現一些遺留的資源。狀態(ipcs)與清除(ipcrm)命令提供了一個檢測與清除IPC遺留資源的一種方法。


信號量

要檢測系統中信息量的狀態,可使用ipcs -s命令。若是存在一些信號量,輸出就會有以下的形式:

1
2
3
4
$ ./ipcs -s
——— Semaphore Arrays ————
semid     owner     perms nsems status
768       rick      666   1


咱們可使用ipcrm命令來移除由程序偶然留下的信號量。要刪除前面的信號量,可使用下面的命令:

1
$ ./ipcrm -s 768

一些較老的Linux系統使用一些略微不一樣的語法:

1
$ ./ipcrm sem 768

可是這種風格再也不推薦使用。查看咱們系統的手冊頁來肯定在咱們的系統上是哪一種格式。

共享內存

與信號量相似,許多系統提供了命令行程序用於訪問共享內存的詳細信息。命令爲ipcs -m與ipcrm -m <id>。

以下面的例子輸出:

1
2
3
4
$ ipcs -m
——— Shared Memory Segments ————
shmid     owner     perms     bytes nattch status
384       rick      666       4096  2


這顯示一個4KB的共享內存段與兩個進程相關聯。

ipcrm -m <id>命令能夠移除共享內存。當一個程序清理共享內存失敗時,這會十分有用。


消息隊列 

對於消息隊列的命令爲ipcs -q與ipcrm -q <id>。


以下面的例子輸出:

1
2
3
4
$ ipcs -q
——— Message Queues ————
msqid     owner     perms used-bytes messages
384       rick      666   2048       2


這顯示了在消息隊列中有兩個消息,共計2048字節。

ipcrm -q <id>命令能夠移除消息隊列。


總結 

在這一章,咱們瞭解了首次在UNIX Systme V.2中普遍使用而且在Linux中可用的三種進程間交互工具。他們是信號量,共享內存與消息隊列。咱們瞭解了他們所提供的高級功能以及如何提供這些功能,一旦理解了這些函數,他們就會爲須要進程間通訊的程序提供強大的解決方案。

若是想深刻體驗LINUX系統的新手,也能夠先下載一個方德Linux軟件中心試用一下。
免費下載地址:http://www.nfs-cloud.cn:81/appCenter/open/softcenter

相關文章
相關標籤/搜索