基本概念
:两个/多个进程之间的数据交互叫做进程间的通信
进程间的通信方式
:采用开放系统接口函数来实现进程间通信
(1)文件
(2)管道
(3)信号
(4)共享内存
(5)消息队列(重点)
(6)信号量集
(7)网络(重点)
…
其中(4)(5)(6)三种通信方式统称为XSI IPC通信
(X/open System Interface Inter-Process Commucation)
进程之间通过文件来通信
:比如一个进程通过write()函数去向一个文件写入数据,另一个进程从该文件通过read()函数读取文件中的数据实现通信
管道pipe(又称无名管道)
:pipe是一种特殊的文件类型,在应用层表现为两个打开的文件描述符
特点:
- 半双工,数据在同一时刻只能在一个方向上流动
- 管道不是普通文件,不属于某个文件系统,其只存在与内存中
- 管道没有名字,只能在具有公共祖先的进程之间使用
- 管道的缓冲区是有限的,管道是一个固定大小的缓冲区(在Linux系统中,该缓冲区大小为4Kbyte)
- 管道所传输的数据是无格式的,需要(发送/接收)双方约定数据格式
- 数据只能从管道的一端写入,从另一端读出
- 从管道读数据是一次操作,数据一旦被读走,数据从管道中丢弃,释放空间以便写更多的数据
pipe()函数
#include <unistd.h>
int pipe(int filedes[2]);
功能:经由参数filedes返回两个文件描述符
参数:
filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。
filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入。
返回值:
成功:返回 0
失败:返回-1
文件描述符概述
文件描述符是非负整数,是文件的标识。
:内核利用文件描述符(file descriptor)来访问文件。利用open打开一个文件时,内核会返回一个文件描
述符。
每个进程都有一张文件描述符的表,进程刚被创建时,其计录了默认打开的标准输入、标准输出、标准错误输出三个设备文件的文件描述符0、1、2。在进程中打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记
录在表中。
管道pipe示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int fd_pipe[2];//存放pipe创建的文件描述符
char buf[] = "hello world";
pid_t pid;//存放进程号
/*pipe()创建fd_pipe[0]为读而打开,fd_pipe[1]为写而打开管道,*/
if( pipe(fd_pipe) < 0 )
{
perror("pipe");//错误处理
}
pid = fork();
if( pid < 0)//错误处理
{
perror("fork");
exit(-1);
}
if(pid == 0)//子进程
{
write(fd_pipe[1],buf,strlen(buf));//向fd_pipe[1]写入
_exit(0);
}
else//父进程
{
wait(NULL);//等待子进程的退出
memset(buf,0,sizeof(buf));//清空buf
read(fd_pipe[0],buf,sizeof(buf));//从fd_pipe[0]读出
printf("buf = [%s]\n",buf);
}
return 0;
}
运行结果:
解析:
父进程创建子进程后,子进程使用write()函数向fd_pipe[1]文件描述符写入数据,然后退出。
父进程等待子进程的退出后,先把自己的buf区清空,然后使用read()函数从fd_pipe[0]文件描述符中读出数据,最后把读出的数据通过printf打印到终端上。
dup()和dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符。
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup和dup2经常用来重定向进程的stdin、stdout和 stderr。
dup()函数
#include <unistd.h>
int dup(int oldfd);
功能:
复制oldfd文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符。
参数:
oldfd:要复制的文件描述符oldfd。
返回值:
成功:新文件描述符。
失败:返回-1,错误代码存于errno中。
注意: 新的文件描述符和oldfd指向的是同一个文件,共 享oldfd所有的锁定、读写位置和各项权限或标志,但 文件描述符之间并不共享close-on-exec标志。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd1, fd2;//存放文件描述符
/*在当前目录下以只写的方式打开test这个文件,如果没有这个文件就创建,权限是777*/
fd1 = open("test",O_CREAT | O_WRONLY,0777); //成功:返回该文件描述符 fd = 3
if(fd1 < 0)//错误处理
{
perror("open");
exit(-1);
}
//分析文件描述符列表:0标准输入 1标准输出 2标准错误输出 3open后创建的文件描述符
close(1);//1:标准输出文件描述符 关闭1
//分析文件描述符列表:0标准输入 2标准错误输出 3open后创建的文件描述符
fd2 = dup(fd1);//复制新的文件描述符给fd2 = 1
printf("fd2 = %d\n",fd2);
return 0;
}
运行结果:
分析:在close(1)之前,存在1:标准输出文件描述符(输出到终端),在close(1)后,1的文件描述符关闭,此时该文件的文件描述符列表为 0 ,2 ,3
fd2通过dup()函数复制获得新的文件描述符,获得规则是该文件描述符列表中最小的非负整数(目前是 0 2 3 最小的非负整数不就是1嘛)
之前的1:标准输出是输出到终端,经过close,dup后使标准输出输出到了test文件 (重定向)
dup2()函数
#include <unistd.h>
int dup2(int oldfd, int newfd)
功能:
复制一份打开的文件描述符oldfd,并分配新的文
件描述符newfd,newfd也标识oldfd所标识的文件。
参数:
要复制的文件描述符oldfd
分配的新的文件描述符newfd
返回值:
成功:返回newfd
失败:返回-1,错误代码存于errno中
注意: newfd是小于文件描述符最大允许值的非负整数, 如果newfd是一个已经打开的文件描述符,则首先关闭 该文件,然后再复制。
示例代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 int main(int argc,char *argv[])
9
10 {
11 int fd1,fd2;
12
13 fd1=open("test.txt",O_CREAT|O_WRONLY,0777);//fd=3
14
15 if( fd1 < 0)
16 {
17 perror("open");
18 exit(-1);
19 }
20
21 fd2=dup2(fd1,1);//关闭1再复制 =》重定向
22
23 printf("fd2=%d\n",fd2);
24
25 return 0;
26 }
~
运行结果:
示例代码2:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 int main(int argc,char *argv[])
9
10 {
11 int fd1;
12 int fd2 = 3;
13 int err = 0;
14
15 err = dup2(1,fd2);//fd2 和 1 都指向标准输出
16 if(err < 0)
17 {
18 perror("dup2");
19 }
20 printf("fd2=%d,err=%d\n",fd2,err);
21 //此时文件列表:0 1 2 3
22 fd1=open("test.txt",O_CREAT|O_RDWR,S_IRWXU);
//此时文件列表:0 1 2 3 4
23 dup2(fd1,1);//先关闭1再复制 =》 标准输出-重定向-test.txt
24 printf("hello world\n");
25
26 dup2(fd2,1);//先关闭1再复制 =》重定向到标准输出(终端)
27
28 printf("I love you \n");
29
30 return 0;
31 }
运行结果:
命名管道(FIFO)
命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同
其特点是:
- FIFO在文件系统中作为一个特殊的文件而存在
- 虽然FIFO文件存在于文件系统中,但FIFO中的内容却存放在内存中,在Linux中,该缓冲区的大小为4Kbyte
- FIFO有名字,不同的进程可以通过该命名管道进行通信
- FIFO所传送的数据是无格式的
- 从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。
- 当共享FIFO的进程执行完所有的I/O操作以后,FIFO将继续保存在文件系统中以便以后使用。
FIFO文件的创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *pathname, mode_t mode);
参数:
- pathname:FIFO的路径名+文件名
- mode:mode_t类型的权限描述符
返回值:
- 成功:返回 0
- 失败:如果文件已经存在,则会出错且返回-1
FIFO文件的读写
因为使用pipe的进程通过继承获得了pipe的文件描述符,所以pipe仅需要创建而不需要打开。
但是FIFO则需要打开,因为使用它们的进程可以没有任何关系。
一般文件的I/O函数都可以作用于FIFO,如open、close、read、write等。
注意: 当打开FIFO时,非阻塞标志(O_NONBLOCK)产生下列影响:
不指定O_NONBLOCK(即不写|O_NONBLOCK):
- 只读open要阻塞到某个其他进程为写而打开此FIFO
- 只写open要阻塞到某个其他进程为读而打开此FIFO
fifo_read示例代码:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<sys/stat.h>
6 #include<fcntl.h>
7
8 int main(int argc,char *argv[])
9 {
10 int fd;
11 int ret;
12 char recv[100];
13
14 ret = mkfifo("my_fifo",S_IRUSR|S_IWUSR);
15
16 if(ret != 0)
17 {
18 perror("mkfifo");
19 }
20
21 printf("before open\n");
22
23 fd = open("my_fifo",O_RDONLY);
24 if(fd<0)
25 {
26 perror("open fifo");
27 }
28 printf("after open and before read\n");
29
30 bzero(recv,sizeof(recv));
31 read(fd,recv,sizeof(recv));
32
33 printf("read form my_fifo buf[%s]\n",recv);
34 return 0;
35 }
fifo_write示例代码:
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<sys/stat.h>
6 #include<fcntl.h>
7
8 int main()
9 {
10 int fd;
11 int ret;
12
13 char send[100] = "Hello I Love You !";
14
15 ret = mkfifo("my_fifo",S_IRUSR|S_IWUSR);
16
17 if(ret != 0)
18 {
19 perror("mkfifo");
20 }
21
22 printf("before open\n");
23
24 fd = open("my_fifo",O_WRONLY);
25 if(fd<0)
26 {
27 perror("open fifo");
28 }
29
30 printf("after open and before write \n");
31
32 write(fd,send,sizeof(send));
33
34 printf("wirte form my_fifo buf=[%s]\n",send);
35
36 return 0;
37 }
运行结果:
消息队列(message queue)
:消息队列是消息的链表,存放在内存中,由内核维护
消息队列的特点:
- 消息队列允许一个或多个进程向它写入或者读取消息,并且每条消息都有类型。
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
- 与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。
- 同样消息队列中的消息是有格式的。
- 只有内核重启或人工删除时,该消息才会被删除,若不人工删除消息队列,消息队列会一直存在于内存中
- 消息队列标识符,来标识消息队列。消息队列在整个系统中是唯一的。
- 在Linux操作系统中消息队列限制值如下:
消息队列个数最多为16个
消息队列总容量最多为16384字节
每个消息内容最多为8192字节
ftok ()函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
- 获得项目相关的唯一的IPC键值。
参数:
- pathname:路径名
- proj_id:项目ID,非0整数(只有低8位有效)
返回值:
4.成功返回key值,失败返回 -1。
创建消息队列msgget()函数:
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:
创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的key值就能得到同一个消息队列的ID。
参数:
- key:IPC键值
- msgflg:标识函数的行为:IPC_CREAT(创建)或IPC_EXCL(如果已经存在则返回失败)。
返回值:
成功:消息队列的标识符,失败:返回-1。
使用shell命令操作消息队列:
查看消息队列:ipcs -q
删除消息队列:ipcrm -q msqid
消息队列的消息的格式
typedef struct _msg
{
long mtype; /* 消息类型 * /
char mtext[100]; / * 消息正文 * /
… / * 消息的正文可以有多个成员 * /
}MSG;
:消息类型必须是长整型的,而且必须是结构体类型的第一个成员,类型下面是消息正文,正文可以有多个成员(正文成员可以是任意数据类型的)。
发送消息msgsnd()函数:
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
功能:
将新消息添加到消息队列。
参数:
msqid:消息队列的队列ID。
msgp:待发送消息结构体的地址。
msgsz:消息正文的字节数。
msgflg:函数的控制属性
- 0:msgsnd调用阻塞直到条件满足为止。
- IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。
返回值:成功:0;失败:返回-1。
接收消息msgrcv()函数:
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t
msgsz, long msgtyp, int msgflg);
功能:
从ID为msqid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除。
参数:
- msqid:消息队列的ID,代表要从哪个消息列中获取消息。
- msgp:存放消息结构体的地址。
- msgsz:消息正文的字节数。
- msgtyp:消息的类型、可以有以下几种类型
msgtyp = 0:返回队列中的第一个消息
msgtyp > 0:返回队列中消息类型为msgtyp的消息
msgtyp < 0:返回队列中消息类型值小于或等于
msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
注意:若消息队列中有多种类型的消息,msgrcv获取消息的时候按消息类型获取,不是先进先出的。
在获取某类型消息的时,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。
- msgflg:函数的控制属性
0:msgrcv调用阻塞直到接收消息成功为止。
MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1。
返回值:成功返回读取消息的长度,失败返回-1。
msgctl函数
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgctl(int msqid,int cmd,struct msqid_ds* buf);
函数功能:主要用于对指定的消息队列执行指定的操作
第一参数:消息队列的ID,msgget函数返回值
第二个参数:操作的命令
IPC_RMID–删除指定的消息队列,此时第三个参数给NULL
第三个参数:结构体指针
返回值:成功返回0,失败返回-1
message_queue_write示例代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/ipc.h>
6 #include<sys/msg.h>
7 #include<string.h>
8
9 typedef struct _msg
10 {
11 long mtype;
12 char mtext[50];
13 }MSG;
14
15 int main(int argc,char *argv[])
16 {
17 key_t key;
18 int msgqid;
19 MSG msg;
20
21 key = ftok(".",2020);
22
23 msgqid = msgget(key,IPC_CREAT|0666);
24 if(msgqid == -1)
25 {
26 perror("msgget");
27 exit(-1);
28 }
29
30 msg.mtype = 10;
31
32 strcpy(msg.mtext,"Hello world");
33
34 msgsnd(msgqid,&msg,sizeof(msg.mtext),0);
35
36 return 0;
37 }
message_queue_read示例代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 #include<sys/ipc.h>
6 #include<sys/msg.h>
7 #include<string.h>
8
9 typedef struct _msg
10 {
11 long mtype;
12 char mtext[50];
13 }MSG;
14
15 int main(int argc,char *argv[])
16 {
17 key_t key;
18 int msgqid;
19 MSG msg;
20
21 key = ftok(".",2020);
22
23 msgqid = msgget(key,IPC_CREAT|0666);
24 if(msgqid == -1)
25 {
26 perror("msgget");
27 exit(-1);
28 }
29
30 msgrcv(msgqid,&msg,sizeof(msg.mtext),10,0);
31
32 printf("msg.mtext=%s\n",msg.mtext);
33
34 msgctl(msgqid,IPC_RMID,NULL);
35
36 return 0;
37 }
运行结果:
示例代码2发送(重点):
//使用消息队列实现进程间的通信
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<signal.h>
//定义消息的数据类型
typedef struct
{
long mtype; //消息的类型
char buf[20]; //消息的内容
}Msg;
//定义全局变量,表示消息队列的ID
int msgid;
void fa(int signo)
{
printf("正在删除消息队列,请稍后...\n");
sleep(3);
int res = msgctl(msgid,IPC_RMID,NULL);
if(-1 == res)
{
perror("msgctl"),exit(-1);
}
printf("删除消息队列成功\n");
exit(0); //终止当前进程
}
int main(void)
{
//1.获取key值,使用ftok函数
key_t key = ftok(".",100); //100是随便给的,.是当前路径
if(-1 == key)
{
perror("ftok"),exit(-1);
}
printf("key = %#x\n",key);
//2.创建消息队列,使用msgget函数
msgid = msgget(key,IPC_CREAT|IPC_EXCL|0644);
if(-1 == msgid)
{
perror("msgget"),exit(-1);
}
printf("msgid = %d\n",msgid);
//3.发送消息,使用msgsnd函数
Msg m1,m2;
m1.mtype = 2;
strcpy(m1.buf,"hello2");
m2.mtype = 1;
strcpy(m2.buf,"hello1");
int res = msgsnd(msgid,&m1,sizeof(m1.buf),0); //第一条消息
if(-1 == res)
{
perror("msgsnd"),exit(-1);
}
msgsnd(msgid,&m2,sizeof(m2.buf),0); //第二条消息
printf("发送消息到消息队列成功\n");
//4.如果不再使用,删除消息队列,msgctl函数
printf("删除消息队列请按Ctrl+c...\n");
signal(SIGINT,fa);
while(1);
return 0;
}
示例代码2接收(重点):
//消息队列的消息读取
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
//定义消息的数据类型
typedef struct
{
long mtype; //消息类型
char buf[20]; //消息内容
}Msg;
int main(void)
{
//1.获取key值,使用ftok函数
key_t key = ftok(".",100); //实参必须和01msgA.c中一样
if(-1 == key)
{
perror("ftok"),exit(-1);
}
printf("key = %#x\n",key);
//2.获取消息队列,使用msgget函数
int msgid = msgget(key,0);
if(-1 == msgid)
{
perror("msggid"),exit(-1);
}
printf("msgid = %d\n",msgid);
//3.读取消息队列中消息,使用msgrcv函数
Msg msg = {};
//始终读取消息队列中的第一个消息
int res = msgrcv(msgid,&msg,sizeof(msg.buf),0,0); //最后一个参数0代表没有消息时默认阻塞
//始终读取消息队列中第一个类型为1的消息
//int res = msgrcv(msgid,&msg,sizeof(msg.buf),1,0);
//始终读取 <= 2的消息,优先读取类型为1的消息
//int res = msgrcv(msgid,&msg,sizeof(msg.buf),-2,0);
if(-1 == res)
{
perror("msgrcv"),exit(-1);
}
printf("读取到的消息是:%ld,%s\n",msg.mtype,msg.buf);
return 0;
}
运行结果:
共享内存(shared memory重点)
:共享内存允许两个或者多个进程共享给定的存储区域。
共享内存是进程间共享数据的一种最快的方法,一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
注意:多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
共享内存示意图:
shmget()函数
功能:获得一个共享存储标识符
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size,intshmflg)
功能:创建或打开一块共享内存区
参数:
- key:IPC键值
- size:该共享存储段的长度(字节)
- shmflg:用来标识函数的行为:
IPC_CREAT:如果不存在就创建
IPC_EXCL:如果已经存在则返回失败
IPC_NOWAIT:调用进程会立即返回。若发生错误则返回-1。
SHM_R:可读
SHM_W:可写
返回值:
4. 成功:返回共享内存标识符
5. 失败:返回-1。
使用shell命令操作共享内存:
查看共享内存:ipcs -m
删除共享内存:ipcrm -m shmid
共享内存映射(attach)
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr,int shmflg);
功能:将一个共享内存段映射到调用进程的数据段中。
参数:
- shmid:共享内存标识符。
- shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL。
- shmflg:共享内存段的访问权限和映射条件
0:共享内存具有可读可写权限。
SHM_RDONLY:只读。
SHM_RND:(shmaddr非空时才有效)
没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。
指定了SHM_RND则此段连接到shmaddr-shmaddr%SHMLAB 所表示的地址上。
返回值:
成功:返回共享内存段首地址
失败:返回 -1
注意: shmat函数使用的时候第二个和第三个参数一般设为NULL和0,即系统自动指定共享内存地址,并且共享内存可读可写。
解除共享内存映射(detach)
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:
将共享内存和当前进程分离(仅仅是断开联系并不删除共享内存)。
参数:
shmaddr:共享内存映射地址。
返回值:
成功返回 0,失败返回 -1。
共享内存控制
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd,struct shmid_ds *buf);
功能:共享内存空间的控制。
参数:
- shmid:共享内存标识符。
- cmd:函数功能的控制。
IPC_RMID:删除。
IPC_SET:设置shmid_ds参数。
IPC_STAT:保存shmid_ds参数。
SHM_LOCK:锁定共享内存段(超级用户)。
SHM_UNLOCK:解锁共享内存段。 - buf:shmid_ds数据类型的地址,用来存放或更改消息队列的属性。
返回值:成功返回 :0 错误返回:-1
注意:SHM_LOCK用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。
这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。
示例代码:
//shared_memory_write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 2048
int main(int argc,char *argv[])
{
int shmid;//共享内存id号
int ret;
key_t key;//key值
char *shmadd;
key = ftok(".",2012);//创建key值
if(key == -1)
{
perror("ftok");
}
/*创建共享内存*/
shmid = shmget(key,BUFSZ,SHM_R|SHM_W|IPC_CREAT);//可读 可写 创建
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
/*共享内存映射*/
shmadd = shmat(shmid,NULL,0);
if(shmadd < 0)
{
perror("shmat");
_exit(-1);
}
printf("copy data to shared-memory");
bzero(shmadd,BUFSZ);//清空共享内存
/*拷贝数据到共享内存区*/
strcpy(shmadd,"data in shared memory");
return 0;
}
//shared_memory_read.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define BUFSZ 2048
int main(int argc,char *argv[])
{
int shmid;//共享内存id号
int ret;
key_t key;//key值
char *shmadd;
key = ftok(".",2012);//创建key值
if(key == -1)
{
perror("ftok");
}
system("ipcs -m");//在系统查看共享内存信息
/*打开共享内存*/
shmid = shmget(key,BUFSZ,SHM_R|SHM_W);
if( shmid < 0)
{
perror("shmget");
exit(-1);
}
/*内存映射*/
shmadd = shmat(shmid,NULL,0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
/*映射*/
shmadd = shmat(shmid,NULL,0);
if(shmadd < 0)
{
perror("shmat");
exit(-1);
}
/*读共享内存区数据*/
printf("copy data form shared-memory\n");
printf("data = [%s]\n",shmadd);
/*分离共享内存和当前进程*/
ret = shmdt(shmadd);//分离并不删除
if(ret < 0)
{
perror("shmdt");
exit(1);
}
else
{
printf("deleted shared-memory\n");
}
/*删除共享内存*/
shmctl(shmid,IPC_RMID,NULL);
system("ipc -m");//在系统查看共享内存信息
return 0;
}