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中的数据。
到此为止关于共享内存的知识就说完了。