Linux通讯(共享内存)

共享内存

1、共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2、为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。

3、由于多个进程共享一段内存,因此也需要依靠某种同步机制。

在linux中我们可以通过下面的指令来查看共享内存

ipcs -m

也可以通过下面的命令删除共享内存

ipcrm -m 【shmid】

一、共享内存相关函数

1、shmget函数

函数原型

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

所需头文件

#include <sys/ipc.h>
#include <sys/shm.h>

返回值
成功:返回所创建的共享内存的文件描述符
失败:-1

参数
key标识共享内存的键值(就像文件的标识是文件名):0 / IPC_PRIVATE
size即为所创建共享内存的大小(字节)
shmflg即为对共享内存的权限还包含有效的标志,包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误

其中,当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信,非亲缘关系之间我们一般是通过ftok这个函数获取键值

(1)当key为ftok所获取的

ftok函数原型

key_t ftok(const char *pathname,int proj_id);

所需头文件

#include <sys/shm.h>
#include <sys/ipc.h>

返回值
成功:返回key
失败:-1

参数
pathname就是你指定的文件名(已经存在的文件名),一般使用当前目录
proj_id是子序号,它是一个8bit的整数。即范围是0~255

当key为ftok所获取时如:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main()
{
    key_t key = ftok(".",123);
    if(key<0)
    {
        printf("ftok fail\n");
        return key;
    }
    else
    {
        printf("ftok success\n");
    }

    int shmid = shmget(key,128,IPC_CREAT|0777);
    if(shmid < 0)
    {
        printf("creat share memory fail\n");
        return shmid;
    }
    else
    {
        printf("creat share memory success\n");
    }
    return 0;
}

(2)当key为IPC_PRIVATE时如

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main()
{
    int shmid = shmget(IPC_PRIVATE,128,0777);
    if(shmid < 0)
    {
        printf("creat share memory fail\n");
        return shmid;
    }
    else
    {
        printf("creat share memory success\n");
    }
    return 0;
}

2、shmat(共享内存的映射)

函数原型

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

所需头文件

#include <sys/types.h>
#include <sys/shm.h>

返回值
成功:调用成功放回映射后的地址
失败:出错放回(void 指针)-1

参数
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg:SHM_RDONLY : 共享内存只读默认 ; 0:共享内存可读写。

例如:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main()
{
    key_t key = ftok(".",123);
    if(key<0)
    {
        printf("ftok fail\n");
        return key;
    }
    else
    {
        printf("ftok success\n");
    }

    int shmid = shmget(key,128,IPC_CREAT|0777);
    if(shmid < 0)
    {
        printf("creat share memory fail\n");
        return shmid;
    }
    else
    {
        printf("creat share memory success\n");
    }

    char *adr = (char *)shmat(shmid,NULL,0);
    if(adr == NULL)
    {
        printf("shmat fail\n");
        return -2;
    }
    else
    {
        printf("shmat success , address = %x\n",adr);
    }
    
    printf("input:");
    fgets(adr,128,stdin);
    printf("output:%s",adr);
    return 0;
}

3、shmdt(取消共享内存与用户进程之间的映射)

函数原型

int *shmdt(const void *shmaddr);

所需头文件

#include <sys/types.h>
#include <sys/shm.h>

返回值
成功:0
失败:-1
注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段

参数
shmaddr是shmat映射成功放回的地址

例如:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>

int main()
{
    key_t key = ftok(".",123);
    if(key<0)
    {
        printf("ftok fail\n");
        return key;
    }
    else
    {
        printf("ftok success\n");
    }

    int shmid = shmget(key,128,IPC_CREAT|0777);
    if(shmid < 0)
    {
        printf("creat share memory fail\n");
        return shmid;
    }
    else
    {
        printf("creat share memory success\n");
    }

    char *adr = (char *)shmat(shmid,NULL,0);
    if(adr == NULL)
    {
        printf("shmat fail\n");
        return -2;
    }
    else
    {
        printf("shmat success , address = %x\n",adr);
    }
    
    printf("input:");
    fgets(adr,128,stdin);
    printf("output:%s",adr);

    shmdt(adr);
    return 0;
}

4、shmctl(控制共享内存)

函数原型

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

所需头文件

#include <sys/ipc.h>
#include <sys/shm.h>

返回值
成功:0
失败:-1

参数
shmid : 共享内存标识ID
cmd :
IPC_STAT得到共享内存的状态
IPC_SET改变共享内存的状态
IPC_RMID删除共享内存
buf :是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定,如果要删除共享内存则设置为NULL即可

linux命令ipcrm -m 【shmid】就为shmctl实现
例如:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>

int main(int argc,char *argv[])
{
    if(argc<3)
    {
        printf("input two parameters\n");
        return argc-1;
    }
    int shmid;
    if(strcmp(argv[1],"-m") == 0)
    {
        printf("delete share memory\n");
    }
    shmid = atoi(argv[2]);
    shmctl(shmid,IPC_RMID,NULL);
    system("ipcrm -m");
    return 0;
}

二、进程间通讯

1、有亲缘关系通讯:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ipc.h>

void myfun10(int signum);  //accept 10 sig

void myfun11(int signum);  //accept 11 sig

int main()
{
    //creat share memory
    int shmid = shmget(IPC_PRIVATE,128,0777);
    if(shmid == -1)
    {
        printf("creat share memory fail\n");
        return shmid;
    }
    
    //creat fork
    pid_t pid = fork();
    if(pid < 0)
    {
        printf("creat fork fail\n");
        return pid;
    }
    
    //son process
    if(pid == 0)
    {
        char *p = shmat(shmid,NULL,0);
        signal(11,myfun11);
        while(1)
        {
            pause();
            printf("son process read : %s", p);
            shmdt(p);
            kill(getppid(),10);
        }
    }

    //parent process
    if(pid > 0)
    {
        char *p = (char *)shmat(shmid,NULL,0);
        signal(10,myfun10);
        while(1)
        {
            printf("parent process write : ");
            fgets(p,128,stdin);
            kill(pid,11);
            pause();
        }
    }

    return 0;
}

void myfun11(int signum)
{
    return ;
}

void myfun10(int signum)
{
    return ;
}

2、无亲缘关系的进程通讯

客户端和服务端的通讯

服务端:

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>

void myfun(int signum)    //accept SIGSUR2 signal's function
{
    return ;
}

typedef struct shmbuf     //struct
{
    pid_t pid;
    char buf[124];
}shmbuf;

int main()
{
    //creat key
    key_t key = ftok(".",123);
    if(key<0)
    {
        printf("creat key fail\n");
        return -1;
    }
    else
    {
        printf("creat key success\n");
    }
    
    //creat share memory
    int shmid = shmget(key,128,IPC_CREAT|0777);
    if(shmid == -1)
    {
        printf("shmget fail\n");
        return -2;
    }
    else
    {
        printf("shmget success , shmid = %d\n",shmid);
    }
    
    //creat shmat
    struct shmbuf *p = (struct shmbuf *)shmat(shmid,NULL,0);
    if(p == NULL)
    {
        printf("shmat fail\n");
        return -3;
    }
    else
    {
        printf("shmat success\n");
    }
    
    //accept SIGUSR2 signal
    signal(SIGUSR2,myfun);

    //write server's pid to client
    p->pid = getpid();

    //sleep
    pause();

    //read client's pid
    pid_t pid = p->pid;
    
    //prompt connect success
    printf("server connect client success\n");

    while(1)
    {
        printf("server process write :");
        fgets(p->buf,124,stdin);
        kill(pid,SIGUSR1);
        pause();
    }

    return 0;
}

客户端

#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <signal.h>

void myfun(int signum)
{
    return ;
}

typedef struct shmbuf
{
    pid_t pid;
    char buf[124];
}shmbuf;

int main()
{
    key_t key = ftok(".",123);
    if(key<0)
    {
        printf("creat key fail\n");
        return -1;
    }
    else
    {
        printf("creat key success\n");
    }
    
    int shmid = shmget(key,128,IPC_CREAT|0777);
    if(shmid == -1)
    {
        printf("shmget fail\n");
        return -2;
    }
    else
    {
        printf("shmget success , shmid = %d\n",shmid);
    }

    struct shmbuf *p = (struct shmbuf *)shmat(shmid,NULL,0);
    if(p == NULL)
    {
        printf("shmat fail\n");
        return -3;
    }
    else
    {
        printf("shmat success\n");
    }

    pid_t pid = p->pid;

    p->pid = getpid();

    kill(pid,SIGUSR2);

    signal(SIGUSR1,myfun);

    printf("client connect server success\n");

    while(1)
    {
        pause();
        printf("client process read : %s",p->buf);
        kill(pid,SIGUSR2);
    }

    return 0;
}

发布了28 篇原创文章 · 获赞 0 · 访问量 996

猜你喜欢

转载自blog.csdn.net/wfea_lff/article/details/104054133