程序通訊簡單程式碼示例-(無名管道,有名管道,共享記憶體,訊息佇列,訊號量)
目錄
程序間通訊技術包括訊息傳遞、同步、共享記憶體和遠端過程呼叫。IPC是一種標準的Unix通訊機制。IPC的方式通常有管道(包括無名管道和有名管道)、訊息佇列,訊號量、共享儲存、Socket、Streams等,其中Socket和Streams支援在不同主機上的兩個程序IPC
無名管道
特點:
1) 半雙工的(即資料只能在一個方向上流動),具有固定的讀端和寫端;
2) 只能用於具有親緣關係的程序之間的通訊(也是父子程序或者兄弟程序之間);
3) 它可以看成是一種特殊的檔案,對於它的讀寫也可以使用普通的read、write 等函式。但是它不是普通的檔案,並不屬於其他任何檔案系統,並且只存在於記憶體中。
4)資料儲存屬於佇列的性質,資料讀完之後就沒有了。管道有快取大小,當寫入大於快取會發生寫阻塞,當資料被讀取後才能繼續寫入。
原型:
#include<unistd.h>
int pipe(int fd[2]);// 返回值:若成功返回0,失敗返回-1
程式碼:
#include<stdio.h> #include<unistd.h> int main() { int fd[2]; // 兩個檔案描述符 pid_t pid; char buff[20]; int ret=pipe(fd); // 建立管道 if(ret < 0) printf("Create Pipe Error!\n"); pid=fork(); // 建立子程序 if( pid < 0) printf("Fork Error!\n"); if(pid > 0) // 父程序 { close(fd[0]); // 關閉讀端 write(fd[1], "hello world\n", 12); printf("this is parent process ,and pipe write is finished.\n"); } if(pid==0) { close(fd[1]); // 關閉寫端 read(fd[0], buff, 20); printf("%s", buff); printf("this is child process ,and pipe read is %s.\n",buff); } return 0; }
有名管道
有名管道(FIFO)是一種檔案型別,可以在無關的程序之間交換資料,通過一種特殊裝置檔案形式存在於檔案系統中。
原型:
#include<stdio.h>
int mkfifo(const char *pathname, mode_t_Mode);
mode_t_Mode和open函式的mode一樣,見下文。其中O_NONBLOCK是指非阻塞模式。
以只讀和只寫方式 open 管道,不指定非阻塞模式。特點:
- open() 以只讀方式開啟 FIFO 時,要阻塞到另一個程序為寫而開啟此 FIFO;
- open() 以只寫方式開啟 FIFO 時,要阻塞到另一個程序為讀而開啟此 FIFO。
- 通訊過程中若寫程序先退出了,就算命名管道里沒有資料,呼叫 read() 函式從 FIFO 裡讀資料時不阻塞;若寫程序又重新執行,則呼叫 read() 函式從 FIFO 裡讀資料時又恢復阻塞。
以只讀和只寫方式 open 管道,指定非阻塞模式。特點:
- 先以只讀方式開啟,如果沒有程序已經為寫而開啟一個 FIFO, 只讀 open() 成功,並且 open() 不阻塞。
- 先以只寫方式開啟,如果沒有程序已經為讀而開啟一個 FIFO,只寫 open() 將出錯返回 -1 。
- read()、write() 讀寫命名管道中讀資料時不阻塞。
以可讀可寫方式 open 管道,則 open() 函式不會阻塞。特點:
- read() 仍會阻塞,緩衝區滿時,write() 也會阻塞;
- 通訊過程中,讀程序退出後,寫程序向命名管道內寫資料時,寫程序不會退出。
- 通訊過程中若寫程序先退出了,如果名管道里沒有資料,呼叫 read() 函式從 FIFO 裡讀資料時會阻塞,與第一種情況不同。
mode_t_Mode引數所能使用的標記:
O_RDONLY 以只讀方式開啟檔案
O_WRONLY 以只寫方式開啟檔案
O_RDWR 以可讀寫方式開啟檔案. 上述三種旗標是互斥的, 也就是不可同時使用, 但可與下列的旗標利用OR(|)運算子組合.
O_CREAT 若欲開啟的檔案不存在則自動建立該檔案.
O_EXCL 如果O_CREAT 也被設定, 此指令會去檢查檔案是否存在. 檔案若不存在則建立該檔案, 否則將導致開啟檔案錯誤. 此外, 若O_CREAT 與O_EXCL 同時設定, 並且欲開啟的檔案為符號連線, 則會開啟檔案失敗.
O_NOCTTY 如果欲開啟的檔案為終端機裝置時, 則不會將該終端機當成程序控制終端機.
O_TRUNC 若檔案存在並且以可寫的方式開啟時, 此旗標會令檔案長度清為0, 而原來存於該檔案的資料也會消失.
O_APPEND 當讀寫檔案時會從檔案尾開始移動, 也就是所寫入的資料會以附加的方式加入到檔案後面.
O_NONBLOCK 以不可阻斷的方式開啟檔案, 也就是無論有無資料讀取或等待, 都會立即返回程序之中.
O_NDELAY 同O_NONBLOCK.
O_SYNC 以同步的方式開啟檔案.
O_NOFOLLOW 如果引數pathname 所指的檔案為一符號連線, 則會令開啟檔案失敗.
O_DIRECTORY 如果引數pathname 所指的檔案並非為一目錄, 則會令開啟檔案失敗。注:此為Linux2. 2 以後特有的旗標, 以避免一些系統安全問題.
程式碼:
server_write.c:從管道寫入資料
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
//訊號處理函式。
void fun(int sig)
{
printf("sig == %d\n",sig);
}
int main()
{
signal(SIGPIPE,fun);
int fd = open("/tmp/fifo.tmp",O_WRONLY);
assert(fd != -1);//fd==-1則建立失敗
char buff[128] = {0};
while(1)
{
printf("input:\n");
fgets(buff,128,stdin);//服務端寫入內容
write(fd,buff,strlen(buff));
if(strncmp(buff,"end",3)==0) //當輸入end時候,退出程式
{
break;
}
}
close(fd);
exit(0);
}
client_read.c:從管道讀取資料
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
int main()
{
int fd = open("fifo",O_RDONLY);
assert(fd != -1);
char buff[128] = {0};
int n = 0;
while((n = read(fd,buff,127))>0)//n是讀入內容的size,當n==0時候表示無讀入內容
{
printf("read:(n = %d)%s\n",n,buff);
memset(buff,0,128);//將buff中的資料清空
}
close(fd);
exit(0);
}
訊息佇列
訊息佇列指存放訊息的佇列。是訊息的連結表,存放在核心中。一個訊息佇列由一個識別符號(即佇列ID)來標識。
特點:
-
訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序。
-
訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容並不會被刪除。
-
訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取。
原型:
#include <sys/msg.h>
int msgget(key_t key, int flag);// 建立或開啟訊息佇列:成功返回佇列ID,失敗返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);// 新增訊息:成功返回0,失敗返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);// 讀取訊息:成功返回訊息資料的長度,失敗返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);// 控制訊息佇列:成功返回0,失敗返回-1
、
在以下兩種情況下,msgget
將建立一個新的訊息佇列:
- 如果沒有與鍵值key相對應的訊息佇列,並且flag中包含了
IPC_CREAT
標誌位。 - key引數為
IPC_PRIVATE
。
函式msgrcv
在讀取訊息佇列時,type引數有下面幾種情況:
type == 0
,返回佇列中的第一個訊息;type > 0
,返回佇列中訊息型別為 type 的第一個訊息;type < 0
,返回佇列中訊息型別值小於或等於 type 絕對值的訊息,如果有多個,則取型別值最小的訊息。
可以看出,type值非 0 時用於以非先進先出次序讀訊息。也可以把 type 看做優先順序的權值。
程式碼:
服務端程式一直在等待特定型別的訊息,當收到該型別的訊息以後,傳送另一種特定型別的訊息作為反饋,客戶端讀取該反饋並打印出來。
msg_server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define MSG_FILE "/etc/passwd"// 用於建立一個唯一的key
struct msg_form { // 訊息結構
long mtype;
char mtext[256];
};
int main()
{
int msqid;
key_t key;
struct msg_form msg;
// 獲取key值
if((key = ftok(MSG_FILE,'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 列印key值
printf("Message Queue - Server key is: %d.\n", key);
// 建立訊息佇列
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
// 列印訊息佇列ID及程序ID
printf("My msqid is: %d.\n", msqid);
printf("My pid is: %d.\n", getpid());
// 迴圈讀取訊息
for(;;)
{
msgrcv(msqid, &msg, 256, 888, 0);// 返回型別為888的第一個訊息
printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
msg.mtype = 999; // 客戶端接收的訊息型別
sprintf(msg.mtext, "hello, I'm server %d", getpid());
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
}
return 0;
}
msg.client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define MSG_FILE "/etc/passwd"// 用於建立一個唯一的key
struct msg_form { // 訊息結構
long mtype;
char mtext[256];
};
int main()
{
int msqid;
key_t key;
struct msg_form msg;
// 獲取key值
if ((key = ftok(MSG_FILE, 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 列印key值
printf("Message Queue - Client key is: %d.\n", key);
// 開啟訊息佇列
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
// 列印訊息佇列ID及程序ID
printf("My msqid is: %d.\n", msqid);
printf("My pid is: %d.\n", getpid());
// 新增訊息,型別為888
msg.mtype = 888;
sprintf(msg.mtext, "hello, I'm client %d", getpid());
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
// 讀取型別為777的訊息
msgrcv(msqid, &msg, 256, 999, 0);
printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
return 0;
}
訊號量
訊號量:是一個含有整數值的資源,程序通過檢測該整數值,來保證其他程序在某個時間不會進行類似的操作。訊號量用於實現程序間的互斥與同步,而不是用於儲存程序間通訊資料。
特點:
-
訊號量用於程序間同步,若要在程序間傳遞資料需要結合共享記憶體。
-
訊號量基於作業系統的 PV 操作,程式對訊號量的操作都是原子操作。
-
每次對訊號量的 PV 操作不僅限於對訊號量值加 1 或減 1,而且可以加減任意正整數。
-
支援訊號量組。
原型:
#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);// 建立或獲取一個訊號量組:若成功返回訊號量集ID,失敗返回-1
int semop(int semid, struct sembuf semoparray[], size_t numops);// 對訊號量組進行操作,改變訊號量的值:成功返回0,失敗返回-1
int semctl(int semid, int sem_num, int cmd, ...);// 控制訊號量的相關資訊
當semget
建立新的訊號量集合時,必須指定集合中訊號量的個數(即num_sems
),通常為1; 如果是引用一個現有的集合,則將num_sems
指定為 0 。
在semop
函式中,sembuf
結構的定義如下:
struct sembuf
{
short sem_num; // 訊號量組中對應的序號,0~sem_nums-1
short sem_op; // 訊號量值在一次操作中的改變數
short sem_flg; // IPC_NOWAIT, SEM_UNDO
}
其中 sem_op 是一次操作中的訊號量的改變數:
-
若
sem_op > 0
,表示程序釋放相應的資源數,將 sem_op 的值加到訊號量的值上。如果有程序正在休眠等待此訊號量,則換行它們。 -
若
sem_op < 0
,請求 sem_op 的絕對值的資源。- 如果相應的資源數可以滿足請求,則將該訊號量的值減去sem_op的絕對值,函式成功返回。
- 當相應的資源數不能滿足請求時,這個操作與
sem_flg
有關。- sem_flg 指定
IPC_NOWAIT
,則semop函數出錯返回EAGAIN
。 - sem_flg 沒有指定
IPC_NOWAIT
,則將該訊號量的semncnt值加1,然後程序掛起直到下述情況發生:- 當相應的資源數可以滿足請求,此訊號量的semncnt值減1,該訊號量的值減去sem_op的絕對值。成功返回;
- 此訊號量被刪除,函式smeop出錯返回EIDRM;
- 程序捕捉到訊號,並從訊號處理函式返回,此情況下將此訊號量的semncnt值減1,函式semop出錯返回EINTR
- sem_flg 指定
-
若
sem_op == 0
,程序阻塞直到訊號量的相應值為0:- 當訊號量已經為0,函式立即返回。
- 如果訊號量的值不為0,則依據
sem_flg
決定函式動作:- sem_flg指定
IPC_NOWAIT
,則出錯返回EAGAIN
。 - sem_flg沒有指定
IPC_NOWAIT
,則將該訊號量的semncnt值加1,然後程序掛起直到下述情況發生:- 訊號量值為0,將訊號量的semzcnt的值減1,函式semop成功返回;
- 此訊號量被刪除,函式smeop出錯返回EIDRM;
- 程序捕捉到訊號,並從訊號處理函式返回,在此情況將此訊號量的semncnt值減1,函式semop出錯返回EINTR
- sem_flg指定
在semctl
函式中的命令有多種,這裡就說兩個常用的:
SETVAL
:用於初始化訊號量為一個已知的值。所需要的值作為聯合semun的val成員來傳遞。在訊號量第一次使用之前需要設定訊號量。IPC_RMID
:刪除一個訊號量集合。如果不刪除訊號量,它將繼續在系統中存在,即使程式已經退出,它可能在你下次執行此程式時引發問題,而且訊號量是一種有限的資源。
程式碼:
該例子如果不加訊號量,則父程序會先執行完畢。這裡加了訊號量讓父程序等待子程序執行完以後再執行。
#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
// 聯合體,用於semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// 初始化訊號量
int init_sem(int sem_id, int value)
{
union semun tmp;
tmp.val = value;
if(semctl(sem_id, 0, SETVAL, tmp) == -1)
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作:
// 若訊號量值為1,獲取資源並將訊號量值-1
// 若訊號量值為0,程序掛起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = -1; /*P操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 釋放資源並將訊號量值+1
// 如果有程序正在掛起等待,則喚醒它們
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
// 刪除訊號量集
int del_sem(int sem_id)
{
union semun tmp;
if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
int main()
{
int sem_id; // 訊號量集ID
key_t key;
pid_t pid;
// 獲取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 建立訊號量集,其中只有一個訊號量
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(1);
}
// 初始化:初值設為0資源被佔用
init_sem(sem_id, 0);
if((pid = fork()) == -1)
perror("Fork Error");
else if(pid == 0) /*子程序*/
{
sleep(2);
printf("Process child: pid=%d\n", getpid());
sem_v(sem_id); /*釋放資源*/
}
else /*父程序*/
{
sem_p(sem_id); /*等待資源*/
printf("Process father: pid=%d\n", getpid());
sem_v(sem_id); /*釋放資源*/
del_sem(sem_id); /*刪除訊號量集*/
}
return 0;
}
共享記憶體
共享記憶體:在Linux中,每個程序使用獨立的程序地址空間。程序間是不能訪問其他程序的地址空間。共享記憶體間通過建立一段允許其他程序使用過的記憶體段,實現資源和資料的共享。
特點:
-
共享記憶體是最快的一種 IPC,因為程序是直接對記憶體進行存取。
-
因為多個程序可以同時操作,所以需要進行同步。
-
訊號量+共享記憶體通常結合在一起使用,訊號量用來同步對共享記憶體的訪問。
原型:
#include <sys/shm.h>
int shmget(key_t key, size_t size, int flag);// 建立或獲取一個共享記憶體:成功返回共享記憶體ID,失敗返回-1
void *shmat(int shm_id, const void *addr, int flag);// 連線共享記憶體到當前程序的地址空間:成功返回指向共享記憶體的指標,失敗返回-1
int shmdt(void *addr);// 斷開與共享記憶體的連線:成功返回0,失敗返回-1
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);// 控制共享記憶體的相關資訊:成功返回0,失敗返回-1
當key引數為IPC_PRIVATE時候,shmid=0,每次都會重新建立一個共享記憶體。當用ftok建立k時候,則shmid不等於0。這兩種方式的區別是IPC_PRIVATE類似有親緣關係的程序共享記憶體,fotk類似無親緣關係的程序的共享記憶體。
當用shmget
函式建立一段共享記憶體時,必須指定其 size;而如果引用一個已存在的共享記憶體,則將 size 指定為0 。
當一段共享記憶體被建立以後,它並不能被任何程序訪問。必須使用shmat
函式連線該共享記憶體到當前程序的地址空間,連線成功後把共享記憶體區物件對映到呼叫程序的地址空間,隨後可像本地空間一樣訪問。
shmdt
函式是用來斷開shmat
建立的連線的。注意,這並不是從系統中刪除該共享記憶體,只是當前程序不能再訪問該共享記憶體而已。
shmctl
函式可以對共享記憶體執行多種操作,根據引數 cmd 執行相應的操作。常用的是IPC_RMID
(從系統中刪除該共享記憶體)。
程式碼:
使用了【共享記憶體+訊號量+訊息佇列】的組合來實現伺服器程序與客戶程序間的通訊。
- 共享記憶體用來傳遞資料;
- 訊號量用來同步;
- 訊息佇列用來 在客戶端修改了共享記憶體後 通知伺服器讀取。
server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h> // shared memory
#include<sys/sem.h> // semaphore
#include<sys/msg.h> // message queue
#include<string.h> // memcpy
// 訊息佇列結構
struct msg_form {
long mtype;
char mtext;
};
// 聯合體,用於semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// 初始化訊號量
int init_sem(int sem_id, int value)
{
union semun tmp;
tmp.val = value;
if(semctl(sem_id, 0, SETVAL, tmp) == -1)
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作:
// 若訊號量值為1,獲取資源並將訊號量值-1
// 若訊號量值為0,程序掛起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = -1; /*P操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 釋放資源並將訊號量值+1
// 如果有程序正在掛起等待,則喚醒它們
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
// 刪除訊號量集
int del_sem(int sem_id)
{
union semun tmp;
if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
// 建立一個訊號量集
int creat_sem(key_t key)
{
int sem_id;
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(-1);
}
init_sem(sem_id, 1); /*初值設為1資源未佔用*/
return sem_id;
}
int main()
{
key_t key;
int shmid, semid, msqid;
char *shm;
char data[] = "this is server";
struct shmid_ds buf1; /*用於刪除共享記憶體*/
struct msqid_ds buf2; /*用於刪除訊息佇列*/
struct msg_form msg; /*訊息佇列用於通知對方更新了共享記憶體*/
// 獲取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 建立共享記憶體
if((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1)
{
perror("Create Shared Memory Error");
exit(1);
}
// 連線共享記憶體
shm = (char*)shmat(shmid, 0, 0);
if((int)shm == -1)
{
perror("Attach Shared Memory Error");
exit(1);
}
// 建立訊息佇列
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
// 建立訊號量
semid = creat_sem(key);
// 讀資料
while(1)
{
msgrcv(msqid, &msg, 1, 888, 0); /*讀取型別為888的訊息*/
if(msg.mtext == 'q') /*quit - 跳出迴圈*/
break;
if(msg.mtext == 'r') /*read - 讀共享記憶體*/
{
sem_p(semid);
printf("%s\n",shm);
sem_v(semid);
}
}
// 斷開連線
shmdt(shm);
/*刪除共享記憶體、訊息佇列、訊號量*/
shmctl(shmid, IPC_RMID, &buf1);
msgctl(msqid, IPC_RMID, &buf2);
del_sem(semid);
return 0;
}
client.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h> // shared memory
#include<sys/sem.h> // semaphore
#include<sys/msg.h> // message queue
#include<string.h> // memcpy
// 訊息佇列結構
struct msg_form {
long mtype;
char mtext;
};
// 聯合體,用於semctl初始化
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf;
unsigned short *array;
};
// P操作:
// 若訊號量值為1,獲取資源並將訊號量值-1
// 若訊號量值為0,程序掛起等待
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = -1; /*P操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作:
// 釋放資源並將訊號量值+1
// 如果有程序正在掛起等待,則喚醒它們
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; /*序號*/
sbuf.sem_op = 1; /*V操作*/
sbuf.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuf, 1) == -1)
{
perror("V operation Error");
return -1;
}
return 0;
}
int main()
{
key_t key;
int shmid, semid, msqid;
char *shm;
struct msg_form msg;
int flag = 1; /*while迴圈條件*/
// 獲取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 獲取共享記憶體
if((shmid = shmget(key, 1024, 0)) == -1)
{
perror("shmget error");
exit(1);
}
// 連線共享記憶體
shm = (char*)shmat(shmid, 0, 0);
if((int)shm == -1)
{
perror("Attach Shared Memory Error");
exit(1);
}
// 建立訊息佇列
if ((msqid = msgget(key, 0)) == -1)
{
perror("msgget error");
exit(1);
}
// 獲取訊號量
if((semid = semget(key, 0, 0)) == -1)
{
perror("semget error");
exit(1);
}
// 寫資料
printf("***************************************\n");
printf("* IPC *\n");
printf("* Input r to send data to server. *\n");
printf("* Input q to quit. *\n");
printf("***************************************\n");
while(flag)
{
char c;
printf("Please input command: ");
scanf("%c", &c);
switch(c)
{
case 'r':
printf("Data to send: ");
sem_p(semid); /*訪問資源*/
scanf("%s", shm);
sem_v(semid); /*釋放資源*/
/*清空標準輸入緩衝區*/
while((c=getchar())!='\n' && c!=EOF);
msg.mtype = 888;
msg.mtext = 'r'; /*傳送訊息通知伺服器讀資料*/
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
break;
case 'q':
msg.mtype = 888;
msg.mtext = 'q';
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
flag = 0;
break;
default:
printf("Wrong input!\n");
/*清空標準輸入緩衝區*/
while((c=getchar())!='\n' && c!=EOF);
}
}
// 斷開連線
shmdt(shm);
return 0;
}