Linux 環境下C編程指南,通過共享內存進行進程間通信的例子,進程間同步使用信號量來實現。
代碼 11-5
使用說明:這是一個簡單的服務器和客戶端程序,如果啓動程序時不帶參數,則執行服務器程序;
如果帶參數,則執行客戶端程序,所帶參數只有一個,就是服務器端所顯示的共享內存的引用ID。
實現原理:服務器端啓動後,創建信號量和共享內存,並將共享內存的引用ID顯示出來,將信號量
的引用ID存放在共享內存中。客戶端啓動後,利用服務器端提供的內存共享ID將共享內存附加到地址段,
讀取信號量以實現兩個進程之間的同步。之後,這兩個進程就可以利用共享內存進行進程間通信,客戶
端輸入的信息將在服務器端顯示出來。
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#define SHMDATASIZE 1000
#define BUFFERSIZE (SHMDATASIZE - sizeof(int))
#define SN_EMPTY 0
#define SN_FULL 1
int deleteSemid=0;
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
void server(void);
void client(int shmid);
void delete(void);
void sigdelete(int signum);
void locksem(int semid, int semnum);
void unlocksem(int semid, int semnum);
void waitzero(int semid, int semnum);
void clientwrite(int shmid, int semid, char *buffer);
int safesemget(key_t key, int nsems, int semflg);
int safesemctl(int semid, int semnum, int cmd, union semun arg);
int safesemop(int semid, struct sembuf *sops, unsigned nsops);
int safeshmget(key_t key, int size, int shmflg);
void *safeshmat(int shmid, const void *shmaddr, int shmflg);
int safeshmctl(int shmid, int cmd, struct shmid_ds *buf);
int main(int argc, char *argv[])
{
if ( argc < 2 )
{
server();
}
else
{
client(atoi(argv[1]));
}
return 0;
}
void server(void)
{
union semun sunion;
int semid,shmid;
void *shmdata;
char *buffer;
//1 創建信號量燈semget(key, nsems, semflg)
semid = safesemget(IPC_PRIVATE, 2, SHM_R|SHM_W);
deleteSemid = semid;
//2 當程序終止執行時,執行刪除信號量,semctl(deleteSemid, 0, IPC_RMID, 0)
atexit(&delete);
//3 接收到信號 SIGINT 則執行 sigdelete 函數 exit(0)
signal(SIGINT, &sigdelete);
//4 設置兩個信號燈,空與滿,val =1 semctl(semid, semnum, cmd, arg)
sunion.val = 1;
safesemctl(semid, SN_EMPTY, SETVAL, sunion);
sunion.val = 0;
safesemctl(semid, SN_FULL, SETVAL, sunion);
//5 創建共享內存
shmid = safeshmget(IPC_PRIVATE, SHMDATASIZE, IPC_CREAT|SHM_R|SHM_W);
//6 將共享內存加載到自己的地址空間,shmdata爲映射區的其始地址
shmdata = safeshmat(shmid, 0, 0);
//7 刪除共享內存,當所有附加該共享內存的進程結束或斷開與該共享內存的連接時才執行
safeshmctl(shmid, IPC_RMID, NULL);
//8 共享內存首地址爲信號燈id
*(int *)shmdata = semid;
//buffer指向共享內存+四個字節後
buffer = shmdata + sizeof(int);
printf("Server is running with SHM id ** %d **/n", shmid);
while(1)
{
printf("Waiting until full...");
fflush(stdout);
//申請1個信號燈資源,使信號燈1阻塞
locksem(semid, SN_FULL);
printf("done./n");
printf("Message received: %s./n", buffer);
//釋放1個信號燈資源,
unlocksem(semid, SN_EMPTY);
}
}
void client(int shmid)
{
int semid;
void *shmdata;
char *buffer;
//1 進程加載共享內存到自己地址空間
shmdata = safeshmat(shmid, 0, 0);
//2 共享內存中第一個int爲信號燈
semid = *(int *)shmdata;
//3 共享內存指針
buffer = shmdata + sizeof(int);
printf("Client operational: shm id is %d, sem id is %d/n", shmid, semid);
while(1)
{
char input[3];
printf("/n/nMenu/n1.send a message/n");
printf("2.Exit/n");
//輸入三個字符
fgets(input, sizeof(input), stdin);
switch(input[0])
{
case '1': clientwrite(shmid, semid, buffer);
break;
case '2': exit(0);
break;
}
}
}
//
void delete(void)
{
printf("/nMaster exiting; deleting semaphore %d./n", deleteSemid);
if (semctl(deleteSemid, 0, IPC_RMID, 0) == -1 )
{
printf("Error releasing semaphore./n");
}
}
//程序退出
void sigdelete(int signum)
{
exit(0);
}
//申請信號燈鎖sb.sem_op = -1;semnum第幾個信號燈
void locksem(int semid, int semnum)
{
//sem_op = -1申請一個資源
struct sembuf sb;
sb.sem_num = semnum;
sb.sem_op = -1;
sb.sem_flg = SEM_UNDO;
safesemop(semid, &sb, 1);
}
void unlocksem(int semid, int semnum)
{
struct sembuf sb;
sb.sem_num = semnum;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
safesemop(semid, &sb, 1);
}
void waitzero(int semid, int semnum)
{
struct sembuf sb;
sb.sem_num = semnum;
sb.sem_op = 0;
sb.sem_flg = 0;
safesemop(semid, &sb, 1);
}
void clientwrite(int shmid, int semid, char *buffer)
{
printf("Waiting until empty...");
fflush(stdout);
locksem(semid, SN_EMPTY);
printf("done./n");
printf("Enter Message: ");
fgets(buffer, BUFFERSIZE, stdin);
unlocksem(semid, SN_FULL);
}
//safeshmget
int safesemget(key_t key, int nsems, int semflg)
{
int retval;
if ( (retval=semget(key, nsems, semflg)) == -1)
{
printf("semget error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
int safesemctl(int semid, int semnum, int cmd, union semun arg)
{
int retval;
if ( (retval=semctl(semid, semnum, cmd, arg)) == -1)
{
printf("semctl error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
//信號燈操作設置
int safesemop(int semid, struct sembuf *sops, unsigned nsops)
{
int retval;
if ( (retval=semop(semid, sops, nsops)) == -1)
{
printf("semop error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
//IPC_PRIVATE, 2, SHM_R|SHM_W
/********************************************************************
函數名稱: safeshmget
函數功能: 從緩存中讀取nSize字節大小的數據到pBuf中
計算到緩存尾還能讀出緩存字節數,如果尾部大小夠讀,則從尾部直
接讀出,否則,則先從緩存尾讀,再從緩存頭讀取
輸入參數: No
輸出參數: No
輸入輸出: No
返回參數: No
*******************************************************************/
int safeshmget(key_t key, int size, int shmflg)
{
int retval;
if ( (retval=shmget(key, size, shmflg)) == -1)
{
printf("shmget error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
//將共享內存加載到自己的地址空間
void *safeshmat(int shmid, const void *shmaddr, int shmflg)
{
void *retval;
if ( (retval=shmat(shmid, shmaddr, shmflg)) == (void *)-1)
{
printf("shmat error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
int safeshmctl(int shmid, int cmd, struct shmid_ds *buf)
{
int retval;
if ( (retval=shmctl(shmid, cmd, buf)) == -1)
{
printf("shmctl error: %s./n", strerror(errno));
exit(254);
}
return retval;
}
--------------------------------
atexit
語法:
#include <stdlib.h> int atexit( void (*func)(void) );
功能: 當程序終止執行時,函數調用函數指針func所指向的函數。可以執行多重調用(至少32個),這些函數以其註冊的倒序執行。執行成功返回零值,失敗則返回非零值。