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中的数据。

到此为止关于共享内存的知识就说完了。

猜你喜欢

转载自blog.csdn.net/weixin_42994525/article/details/83048946