共享内存通信

共享内存通信方式是一种最高效的通信方式,是直接通过开辟内存空间进行映射后用于直接读写的

内存块,大小由用户自己固定开辟(在系统可行范围内),可用于亲缘间的通信,也可用于非亲缘间的通信,比较灵活,缺点是只仅限与在同一个操作系统内进行通信。

所以就需要某些函数进行对共享内存的申请,使用以及删除等操作,我们利用亲缘间 的通信举例子。

步骤:

1:申请共享内存

函数原型如下:

int shmget(key_t key, size_t size, int shmflg);

参数解释

key: 可以通过ftok()函数进行获得,也可以直接使用IPC_PRIVATE作为参数,默认创建一个键值固定的私有空间作为映射内存,

size :用户自定义申请映射空间的大小,单位为字节

shmflg :代表权限,一般为 IPC_CREAT | 0664,创建成功后返回共享内存的ID号,shmid

2:创建好映射的内存后进行地址的映射

函数原型如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数解释:

shmid: 在上一步创建内存后被返回,直接使用

shmaddr : 一般默认为NULL,表示系统自动映射的地址,若需自定义,放入需要映射的地址即可

shmflg :  代表权限,0为读写权限,1为写权限,这里选0

正确返回映射后的用户空间首地址,错误返回(void*)-1,

3:使用fork函数创建子进程

函数原型:

pid_t fork(void);

成功创建返回子进程的 pid  ,失败返回-1

重点:

共享内存的代码需要放在创建子进程代码段之前,由于利用fork 函数创建的进程会拷贝原有的代码,变量等等,所以需在创建共享内存之后创建子进程。

接下来我们自己规定利用子进程进行内存的写入,父进程对内存进行读取,达到通信的目的。

子进程主要代码

char buf[20]={0};
     while(1)
     {
     fgets(buf,sizeof(buf),stdin);
     if(strncmp(buf,"quit",4)==0)
         break;
     strcpy(q,buf);
     memset(buf,0,sizeof(buf));
     }

这里利用fgets函数从终端读取字符到buf中,再使用strcpy函数对共享内存进行写入,如果写入quit代表结束写入,退出程序。

父进程主要代码

while(1)
        {
            if(strncmp(q,"quit",4)==0)
                break;
        printf("get------%s\n",q);
        sleep(1);
        }
        wait(NULL);
        exit(0);

使用strcmp函数判断是否读取到quit字符串,读取到就退出,并对子进程进行回收后退出。

我们上述只是完成了对共享内存的读写操作,由于共享内存是由内存映射到用户区后进行使用的,所以为了不占用内存资源还需要在对共享内存进行写入读取操作后进行回收操作

4:使用shmdt函数取消共享内存的映射

int shmdt(const void *shmaddr);

参数解释:

shmaddr : 映射到用户区的首地址

成功取消返回0,错误返回-1;

5 :使用shmctl 函数对共享内存进行删除操作

函数原型:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数解释:

shmid :共享内存ID号

cmd :选择所需要的操作,删除操作使用 IPC_RMID

*buf :NULL

以上步骤便是对共享内存的创建,使用,删除操作,全部代码如下:

#include <strings.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{ 
    pid_t p;
    key_t key;
    if(-1==(key=ftok("1.txt", 21)))                 //获取key值
    {
        perror("ftok failed\n");
        exit(-1);
    }
    printf("key=%#x\n",key);
    int shmid = shmget(key, 1024, 0644 | IPC_CREAT);
	if(shmid == -1)
	{
		perror("shmget");
		return-1;
	}

    printf("shmid=%d\n",shmid);
    char *q=NULL;
    if((void*)-1==(q=shmat(shmid,NULL,0)))   //映射内存
    {
        perror("shmat failed\n");
        exit(-1);
    }
    p=fork();
    if(p==0)                                                  //子进程
    {
     char buf[20]={0};
     while(1)
     {
     fgets(buf,sizeof(buf),stdin);
     if(strncmp(buf,"quit",4)==0)
         break;
     strcpy(q,buf);
     memset(buf,0,sizeof(buf));
     }
    if(-1==shmdt(q))                        //取消映射
    {
        perror("shmdt failed\n");
        exit(-1);
    }
    if(-1==shmctl(shmid,IPC_RMID,NULL))      //删除映射
    {
        perror("shmctl failed\n");
        exit(-1);
    }
    exit(0);
    }
    else                                //父进程
    {
        while(1)
        {
            if(strncmp(q,"quit",4)==0)
                break;
        printf("get------%s\n",q);
        sleep(1);
        }
        wait(NULL);
       exit(0);
    }
    return 0;
}