[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > 其它 > 正文
Unix中多程序間共享內存
http://www.ccw.com.cn 北京網擎科技 許楊春 (2001-04-19 15:15:09)
共享內存 (shared memory)是 Unix下的多進程之間的通信方法 ,這種方法通常用一個程序的多進程間通信,實際上多個程序間也可以通過共享內存來傳遞信息。本文介紹如何在 Client/Server方式下實現多個程序間共享內存。  
問題分析 
多個程序之間共享內存 ,首先要解決的問題是怎樣讓各個程序能夠訪問同一塊內存和相同的信號量。共享內存的 id可以通過調用 shmget(key_t key, size_t size, int shmflg)函數取得;信號量的 id可以通過調用 semget(key_t key, int nsems, int semflg)函數取得。實際上,只要在調用這兩個函數時使用相同的 key值,各程序之間就能達到共享內存的目的。 Unix通過調用 key_t ftok(const char* path, int id)函數來產生 key值 ,如果各程序都用同樣的參數來調用此函數,自然也就得到相同的 key值了。例子中各個程序都使用 key=ftok(" /", 0)得到相同的 key值 ,再進而由 key值得到相同的共享內存 id和信號量 id。  
第二個要解決的問題是如何控制多個程序並發訪問共享內存。本文的例子模擬在 Client/Server方式下,由一個 Server產生數據,多個 Client去讀取數據的操作。常規的方法是設一個信號量,將訪問共享內存的程序作為臨界區來處理。程序進入時用 p()操作取得鎖,退出時用 v()操作釋放鎖。但這樣做有兩個問題:一是這樣各個程序就處平等的地位,而實際中往往 Server的優先級應該比 Client更高。比如,在股票行情應用程序中 ,共享內存裡存放行情信息, Server負責定時更新; Client是 CGI程序,負責按客戶要求讀取共享內存中的數據,然再反饋給客戶。在這種情況下, Server就不能等所有 Client進程都讀完了才開始寫,因為這樣 Client取得的數據反而是過時的。二是各個 Client之間由都是讀操作,所以沒有必要互斥。  
本文對這兩個問題的解決方案是:只有 Server進行 p()、 v()操作,信號量初始值設為 0, p()操作將它加一, v()操作將它減一; Client讀共享內存之前要先等待信號量的值為 0,這樣 Server的 p()操作總是成功,而 Server的 p()操作,尚未進入臨界區的 Client只能等到 Server執行 v()操作才能讀。這樣 Server比 Client優先, Client之間不互斥。但這樣又產生另一個問題:一個 Server開始寫時,部分 Client可能已經進入臨界區,有可能出現讀不完整的問題。因此,例子基這樣一個前提: Client程序比較簡單,不會被阻塞,並且能夠在一個時間片內執行完讀取操作。本例中處臨界區中的 Client數目是有限的,如果 Server等待一個時間片 (例子中是等待一分鐘 ), Client就能全部退出臨界區,這個問題就能排除。很多 CGI程序能夠滿足這個假設條件,如果 Client確實不滿足條件,可以生成訪問共享內存的子進程,它的執行時間應該滿足上述要求。  
應用實例 
下面給出實現多程序間共享內存的例子程序的部分代碼:  
1.Server端程序  
# define SEGSIZE 1024  
# define READTIME 1  
union semun {  
int val;  
struct semid_ds* buf;  
ushort_t* array;  
} ;  
//生成信號量  
int sem(key_t key){  
union semun sem ;  
int semid;  
sem.val=0;  
semid=semget(key,1,IPC_CREAT|0666);  
if (semid ==- 1){  
printf(" create semaphore error\n" );  
exit(- 1);  
}  
//初始化信號量  
semctl(semid,0,SETVAL,sem);  
return semid;  
}  
//刪除信號量  
void d_sem(int semid){  
union semun sem ;  
sem.val=0;  
semctl(semid,0,IPC_RMID,0);  
}  
int p(int semid){  
struct sembuf sops={0,+ 1,IPC_NOWAIT};  
return(semop(semid,& sops,1));  
}  
int v(int semid){  
struct sembuf sops={0,- 1,IPC_NOWAIT};  
return(semop(semid,& sops,1));  
}  
int main(){  
key_t key;  
int shmid,semid;  
char* shm;  
char msg[7]=" data " ;  
char i;  
struct shmid_ds buf;  
key=ftok(" /" , 0);  
shmid=shmget(key,SEGSIZE,IPC_CREAT|0604);  
if(shmid ==- 1){  
printf(" create shared momery error\n" );  
return- 1;  
}  
shm=(char* )shmat(shmid,0,0);  
if((int)shm ==- 1){  
printf(" attach shared momery error\n" );  
return- 1;  
}  
semid=sem(key);  
for(i=0;i<=3;i++ ){  
sleep(1);  
p(semid);  
sleep(READTIME);  
msg[5]=' 0'+ i;  
memcpy(shm,msg,sizeof(msg));  
sleep(58);  
v(semid);  
}  
shmdt(shm);  
shmctl(shmid,IPC_RMID,& buf);  
d_sem(semid);  
return 0;  
}  
2.Client端程序# define SEGSIZE 1024  
union semun {  
int val;  
struct semid_ds* buf;  
ushort_t* array;  
} ;  
//打印程序執行時間  
void secondpass(){  
static long start=0;  
time_t timer;  
if (start == 0){  
timer=time(NULL);  
start=(long)timer;  
printf(" now start \n" );  
}  
printf(" second:% ld \n" ,(long)(time(NULL))- start);  
}  
int sem(key_t key){  
union semun sem ;  
int semid;  
sem.val=0;  
semid=semget(key,0,0);  
if (semid ==- 1){  
printf(" get semaphore error\n");  
exit(- 1);  
}  
return semid;  
}  
//等待信號量變成 0  
void waitv(int semid){  
struct sembuf sops={0,0,0};  
semop(semid,& sops,1);  
}  
int main(){  
key_t key;  
int shmid,semid;  
char* shm;  
char msg[100];  
int i;  
key=ftok(" /" , 0);  
shmid=shmget(key,SEGSIZE,0);  
if(shmid ==- 1){  
printf(" get shared momery error\n" );  
return- 1;  
}  
shm=(char* )shmat(shmid,0,0);  
if((int)shm ==- 1){  
printf(" attach shared momery error\n" );  
return- 1;  
}  
semid=sem(key);  
for(i=0;i< 3;i++ ){  
sleep(2);  
waitv(semid);  
printf(" the msg get is \n% s\n" ,shm+ 1);  
secondpass();  
}  
shmdt(shm);  
return 0;  
}  
本程序在 Solaris 7下編譯通過。 
(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
 

★  樊強制作 歡迎分享  ★