linux程序通訊IPC之IPC_PRIVATE與ftok比較
在linux中,可以使用IPC物件來進行程序間通訊。IPC物件存在於核心中,多程序可以操作同一個IPC物件。
每個IPC物件都有一個唯一的編號,該編號是由系統分配的。那麼不同的程序如何知道這個編號,進而通過它進行通訊呢?下面以共享記憶體為例,進行分析。
方法一:通過ftok函式,產生相同的鍵值。
假設,程序p1建立了共享記憶體。可以在建立時,呼叫ftok函式,得到一個key值,呼叫shmget函式,該函式會返回所建立共享記憶體的編號,並將key和編號關聯起來。若程序p2想利用這個共享記憶體和p1程序通訊,也可以呼叫ftok函式,得到同樣的key,再根據key值,呼叫shmget函式,就可以獲得該共享記憶體的編號。該過程可以通過下面的圖來表達。
ftok函式原型如下:
#include < sys/types.h>
#include < sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
第一個引數pathname,是一個存在的檔案或目錄名;
第二個引數proj_id,是非0整數(一般用i節點號)
該函式會返回一個key值,先執行的程序根據key來建立物件,後執行的程序根據key來開啟物件。示意圖如下:
使用 ftok建立共享記憶體,毫無關係的程序,可以通過得到同樣的key,來操作同一個共享記憶體,對共享記憶體進行讀寫時,需要利用訊號量進行同步或互斥。
方法二:使用IPC_PRIVATE物件
使用IPC_PRIVATE建立的IPC物件, key值屬性為0,和IPC物件的編號就沒有了對應關係。這樣毫無關係的程序,就不能通過key值來得到IPC物件的編號(因為這種方式建立的IPC物件的key值都是0)。因此,這種方式產生的IPC物件,和無名管道類似,不能用於毫無關係的程序間通訊。但也不是一點用處都沒有,仍然可以用於有親緣關係的程序間通訊。示例程式如下:
#include < stdio.h>
#include < stdlib.h>
#include < errno.h>
#include < sys/ipc.h>
#include < sys/types.h>
#include < sys/shm.h>
#include < string.h>
#define MAXSIZE 1024
int main()
{
int shmid;
char *p = NULL;
pid_t pid;
#if 0
key_t key;
if ((key = ftok(".", 'a')) == -1)
{
perror("ftok");
exit(-1);
}
#endif
if ((shmid = shmget(IPC_PRIVATE, MAXSIZE, 0666)) == -1)
{
perror("shmget");
exit(-1);
}
if ((pid = fork()) == -1)
{
perror("fork");
exit(-1);
}
if (pid == 0)
{
if ((p = shmat(shmid, NULL, 0)) == (void *)-1)
{
perror("shmat");
exit(-1);
}
strcpy(p, "hello\n");
system("ipcs -m");
if (shmdt(p) == -1)
{
perror("shmdt");
exit(-1);
}
system("ipcs -m");
}
else
{
getchar();
if ((p = shmat(shmid, NULL, 0)) == (void *)-1)
{
perror("shmat");
exit(-1);
}
printf("%s\n", (char *)p);
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("RM");
exit(-1);
}
}
return 0;
}
該程式中,父程序使用IPC_PRIVATE方式建立了共享記憶體,然後fork產生了子程序,由於子程序是複製父程序的方式產生的,因此,子程序也可以操作共享記憶體。子程序往共享記憶體裡寫了內容後,父程序可以讀到。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.使用IPC_PRIVATE方式注意
(1)int shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);需要在父子程序都可見的地方呼叫(即在建立子程序之前),否則不能實現記憶體的共享
因為通過IPC_PRIVATE這個key獲得的id不一樣,其他通過ftok獲得的key來shmget獲得的id在程式每次執行中是一樣的。ftok引數一樣的話每次程式執行中返回值都一樣。4.ipcs -m status 欄
nattch 是連線數目,dest 表示共享記憶體段已經被刪除,但是仍然有程式在連線著它。
“status欄中列出當前共享記憶體的狀態,當該段記憶體的mode欄位設定了SHM_DEST位時就會顯示"dest"字樣,
當用戶呼叫shmctl的IPC_RMID時,核心首先看有多少個程序還和這段記憶體關聯著,如果關聯數為0,就會銷燬(釋放)這段記憶體,否則就設定這段記憶體的mode位SHM_DEST,”
5.呼叫shmctl(shmID,IPC_RMID,NULL)或者shell 命令 ipcrm -m 不是會立刻刪除共享記憶體,是向上面那樣先置dest,並且此時共享記憶體中的資料仍然可以使用(即不影響正在使用共享記憶體的部分,但是若置為dest後再通過shmget通過同樣的key來獲取shmID時,shmID和刪除前就不一樣了),然後等待關聯數為0才刪除。shmdt是用來釋放共享記憶體連結的,程序退出會呼叫shmdt。
並且共享記憶體被置dest後可以成功呼叫shmctl(shmID,IPC_RMID,NULL)或者shell 命令 ipcrm -m ,但是當關聯數為0,共享記憶體被刪除後,再呼叫 這些命令就會出錯。
shmctl(shmID,IPC_RMID,NULL)可以被每個使用共享記憶體的程序內呼叫多次,但至少由一個程序來呼叫一次,否則不能刪除共享的記憶體,但是shmdt只能對應相應的shmget呼叫一次。
3.4.5部分的程式碼如下:
//shmem.cpp
//g++ shmem.cpp -lpthread -o shmem
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<unistd.h>
#include<iostream>
#include<cstdlib>
#include<pthread.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
struct argv_t{
int shmID;
};
void * thread(void * argv){
argv_t tmp=*(argv_t*)argv;
char * addr=(char*)shmat(tmp.shmID,0,0);
for(int i=0;i<5;i++){
//sleep(2);
std::cout<<"thread sending"<<std::endl;
strcpy(addr,"Hello");
}
std::cout<<"thread :threadID="<<pthread_self()<<",pid="<<getpid()<<",parent pid ="<<getppid()<<std::endl;
/*if(-1==shmctl(tmp.shmID,IPC_RMID,NULL)){
std::cout<<"remove shared memory error:"<<errno<<std::endl;//error<<std::endl;
perror(strerror(errno));
}else{
std::cout<<"remove shared memory ok"<<std::endl;
}*/
}
int main(int argc,char** argv){
pthread_t thid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
argv_t arg;
pid_t pid;
int len=5;
int shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);
if((pid=fork())==0){
sleep(5);
char * addr=(char *)shmat(shmID,0,SHM_RDONLY);
for(int i=0;i<5;i++){
std::cout<<"sub process pid="<<getpid()<<",parent pid"<<getppid()<<std::endl;
std::cout<<"begin read sharedMem"<<std::endl;
std::cout<<"s1"<<std::endl;
std::cout<<"s2"<<std::endl;
//if(i<=2)
system("ipcs -m");
std::cout<<"-----"<<i<<std::endl;
if(i==2){
char *cmd=new char[50];
sprintf(cmd,"ipcrm -m %d\0",shmID);
system(cmd);
}
std::cout<<addr<<std::endl;
//shmdt(addr);
sleep(3);
}//for
std::cout<<"reveive end"<<std::endl;
if(-1==shmctl(shmID,IPC_RMID,NULL)){
std::cout<<"remove shared memory error:"<<errno<<std::endl;//error<<std::endl;
perror(strerror(errno));
}else{
std::cout<<"remove shared memory ok"<<std::endl;
}
}else if(pid>0){
//shmID=shmget(IPC_PRIVATE,len,IPC_CREAT|0600);
std::cout<<"shmID="<<shmID<<std::endl;
arg.shmID=shmID;
std::cout<<"I am the parent ,pid="<<getpid()<<",parent pid"<<getppid()<<std::endl;
if((pthread_create(&thid,&attr,thread,(void*)&arg))!=0)
std::cout<<"create thread failed"<<std::endl;
else
std::cout<<"create thread successed"<<std::endl;
sleep(10);
pthread_exit(0);//in main for wait
}else{
std::cout<<"fork sub process failed"<<std::endl;
}
return EXIT_SUCCESS;
}
部分輸出結果:
6.訊號量中IPC_PRIVATE類似於共享記憶體中,同樣semget(IPC_PRIVATE,1,0666 | IPC_CREAT)需要在父子程序都可見的地方呼叫(即在建立子程序之前),否則不能實現記憶體的共享。
訊號量中的semctl(semID[index],0,IPC_RMID,sem_union)刪除是立即刪除,不同於共享記憶體