1. 程式人生 > >linux程序通訊IPC之IPC_PRIVATE與ftok比較

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)刪除是立即刪除,不同於共享記憶體