Linux學習之程序通訊(四)
言之者無罪,聞之者足以戒。 ——《詩序》
IPC通訊
IPC通訊有三種:共享記憶體、訊息佇列、訊號燈
這個IPC物件,是存在於核心中的。而且使用者空間的檔案系統中沒有IPC檔案型別
IPC物件
IPC和檔案IO函式的比較:
檔案I/O |
IPC |
open |
Msg_get (建立訊息佇列) Shm_get(建立共享記憶體) Sem_get(建立訊號燈) |
read write |
msgsnd msgrecv shmat shmdt semop |
close |
msgctrl shmctrl semctrl |
一、共享記憶體:
1、shmget函式:建立共享記憶體
所需標頭檔案 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> |
函式原型 |
int shmget(key_t key, int size, int shmflg); |
函式引數 |
key:IPC_PRIVATE 或 ftok的返回值 |
size:共享記憶體區大小 |
|
shmflg:同open函式的許可權位,也可以用8進製表示法 |
|
函式返回值 |
成功:共享記憶體段識別符號---ID---檔案描述符 |
出錯:-1 |
檢視IPC物件命令:ipcs -m(檢視共享記憶體) 、ipcs -q(檢視訊息佇列) 、ipcs -s(檢視訊號燈)
刪除IPC物件命令:ipcrm -m id(刪除共享記憶體) 、ipcrm -q id(刪除訊息佇列) 、ipcrm -s id(刪除訊號燈)
返回值:共享記憶體段識別符號 IPC的ID號
開啟或建立一個共享記憶體物件,共享記憶體在核心中是一塊快取,類似於使用者空間的陣列或malloc函式分配的空間一樣
下面給出一段程式碼來學習一下共享記憶體的建立:
include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
int main()
{
int shmid;
shmid=shmget(IPC_PRIVATE,128,0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sycess shmid=%d\n",shmid);
system("ipcs -m");
// system("ipcrm -m shmid");
return 0;
}
system("ipcs -m")語句的意思是通過系統呼叫ipcs -m 這條語句,我們也可以直接在命令視窗寫命令:ipcs -m
2、ftok函式:建立key值
char ftok(const char *path,char key)
引數:第一個引數:檔案路徑和檔名
第二個引數:一個字元
返回值:正確返回一個key值,出錯返回-1
IPC_PRIVATE操作時,共享記憶體的key值都一樣,都是0,而使用ftok函式來建立key值則是不一樣的。只要key值是一樣的,使用者空間的程序通過這個函式開啟,則會對核心的同一個IPC物件操作。
下面來看一下程式:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
int main()
{
int shmid;
int key;
key=ftok("./a.c",'b');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
printf("creat key sucess key=%x\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sycess shmid=%d\n",shmid);
system("ipcs -m");
// system("ipcrm -m shmid");
return 0;
}
IPC_PRIVATE用來實現有親緣關係的程序之間的通訊,ftok函式可以實現無親緣關係的程序之間的通訊
3、shmat函式:將共享記憶體對映到使用者空間的地址中
為了方便使用者空間對共享記憶體的操作,使用地址對映的方式。
void *shmat(int shmid,const void *shmaddr,int shmflg)
第一個引數:ID號
第二個引數:對映到的地址,NULL為系統自動完成的對映
第三個引數:shmflg :SHM_RDONLY共享記憶體只讀;預設是0,表示共享記憶體可讀寫
返回值:成功就是對映後的地址;失敗返回NULL
下面來看一下程式:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
printf("creat key sucess key=%x\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sycess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p==NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("first read share memory data:%s",p);
printf("second read share memory data:%s",p);
// system("ipcrm -m shmid");
return 0;
}
共享記憶體的特點:
(1)共享記憶體建立之後,一直存在於核心中,直到被刪除或關閉系統
(2)共享記憶體和管道不一樣,讀取後,內容仍在其共享記憶體中
4、shmdt:將使用者空間程序裡的地址對映刪除
int shmdt(const void *shmaddr)
引數:shmaddr共享記憶體對映後的地址
返回值:成功:0
出錯:-1
5、shmctl:刪除共享記憶體物件
int shmctl(int shmid ,int cmd ,struct shmid ds *buf)
shmid:要操作的共享記憶體識別符號
cmd :IPC_STAT(獲取物件屬性) ----實現了命令 ipcs -m
IPC_SET(設定物件屬性)
IPC_RMID(刪除物件) ---實現了命令 ipcrm -m
buf:指定IPC_STAT/IPC_SET時用以儲存/設定屬性
返回值:成功:0
出錯:-1
下面看一下程式:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
printf("creat key sucess key=%x\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sycess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p==NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("first read share memory data:%s",p);
printf("second read share memory data:%s",p);
// system("ipcrm -m shmid");
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
上面的程式我們就把剛剛說的兩個函式都用了一下
之前我們說過IPC_PRIVATE用來實現有親緣關係的程序之間的通訊,ftok函式可以實現無親緣關係的程序之間的通訊
那麼我們就來實現一下這兩種方式:
(1)IPC_PRIVATE實現父子程序的通訊:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
void myfun(int signum)
{
return;
}
int main()
{
int shmid;
int key;
char *p;
int pid;
shmid=shmget(IPC_PRIVATE,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sycess shmid=%d\n",shmid);
pid=fork();
if(pid > 0)
{
signal(SIGUSR2,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p==NULL)
{
printf("paretn process: shmat function failure\n");
return -2;
}
while(1)
{
printf("parent process start write share memory:\n");
fgets(p,128,stdin);
kill(pid,SIGUSR1);
pause();
}
}
if(pid==0)
{
signal(SIGUSR1,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p==NULL)
{
printf("child process shmat function failure\n");
return -3;
}
while(1)
{
pause();
printf("share memory data:%s",p);
kill(getppid(),SIGUSR2);
}
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
(2)ftok函式可以實現無親緣關係的程序之間的通訊
實現無親緣關係的通訊需要兩個獨立的程序: sever程式:
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int client_pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key scuess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("shmat function failure\n");
return -3;
}
//sent pid to share memory
p->pid=getpid();
pause();
client_pid=p->pid;
while(1)
{
printf("start share memory:\n");
fgets(p->buf,128,stdin);
kill(client_pid,SIGUSR1);
pause();
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m");
return 0;
}
client程式:
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int server_pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key scuess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid < 0)
{
printf("creat share memory failure\n");
return -3;
}
server_pid=p->pid;
p->pid=getpid();
kill(server_pid,SIGUSR2);
while(1)
{
pause();
printf("read share memory:%s",p->buf);
kill(server_pid,SIGUSR2);
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m");
return 0;
}
下面我們來說一下上面的程式:
第一來說一下server中的程式:首先使用ftok()函式獲得key值,然後建立共享記憶體,在將記憶體對映到使用者空間,緊接著將自己的pid傳送出去,然後等待接收另一個程序的pid,最後開始寫資料,寫完之後告訴給另一個程序傳送訊號。
第二說一下client中的程式:首先使用ftok()函式獲得key值,然後建立共享記憶體,在將記憶體對映到使用者空間,緊接著讀取server的pid,然後將自己的pid傳送出去,並告訴server已經接收和傳送pid,最後就是讀取buf中的資料。
到此為止關於共享記憶體的知識就說完了。