1. 程式人生 > >Linux學習之程序通訊(四)

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中的資料。

到此為止關於共享記憶體的知識就說完了。