文章目录
进程间通讯
进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,**进程 1 把数据从用户空间拷到内核缓冲区,进程 2 再从内核缓冲区把数据读走,**内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
常用的进程间通信方式:
- 管道 (使用最简单)
- 信号 (开销最小)
- 共享映射区 (无血缘关系)
- 本地套接字 (最稳定)
管道
linux中7种文件类型
符号 | 类型 |
---|---|
- | 普通文件 |
d | 目录 |
c | 字符设备 |
s | 套接字文件 |
p | 管道文件 |
l | 链接文件 |
b | 块设备 |
管道的特质:
管道是进程间通信的一种方式,管道的本质其实就是内核中的一块内存 (或者叫内核缓冲区),这块缓冲区中的数据存储在一个环形队列中,因为管道在内核里边,因此我们不能直接对其进行任何操作。
-
其本质是一个伪文件(实为内核缓冲区) ,大小为4K
-
由两个文件描述符引用,一个表示读端,一个表示写端。
-
规定数据从管道的写端流入管道,从读端流出
管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限:
- 数据不能进程自己写,自己读。
- 管道中数据不可反复读取。一旦读走,管道中不再存在。
- 管道是单工的:数据只能单向流动,数据从写端流向读端。
- 只能在有公共祖先的进程间使用管道。(匿名管道)
管道的阻塞读写:
读管道:管道中没有数据,读操作被阻塞,当管道中有数据之后阻塞才能解除
写管道:管道被写满了,写数据的操作被阻塞,当管道变为不满的状态,写阻塞解除
常见的通信方式有,单工通信、半双工通信、全双工通信。
单工通讯:数据在传输过程中只能往一个方向传输,不能双向传输。
半双工通信:数据可以双向传输,但不能同时进行。
全双工通信:数据不仅可以双向传输,并且可以同时进行。
设置管道的非阻塞读写
1.命名管道打开时设置打开方式为非阻塞
读:fd = open("pipe", O_RDONLY|O_NONBLOCK);
写:fd = open("pipe", O_WRONLY|O_NONBLOCK);
2.匿名管道使用fcntl函数
以读端为例:
int flag = fcntl(fd[0],F_GETFL); //获得fd[0]的flag属性
flag |= O_NONBLOCK; //加入非阻塞属性
fcntl(fd[0],F_SETFL,flag); //将新的flag属性,设置给f[0]
匿名管道:pipe函数
创建并打开管道
int pipe(int fd[2]);
参数: fd[0]: 读端。
fd[1]: 写端。
返回值: 成功: 0
失败: -1 errno
管道的读写行为:
读管道:
1. 管道有数据,read返回实际读到的字节数。
2. 管道无数据: 1)无写端,read返回0 (类似读到文件尾)
2)有写端,read阻塞等待。
写管道:
1. 无读端, 异常终止。 (SIGPIPE导致的)
2. 有读端: 1) 管道已满, 阻塞等待
2) 管道未满, 返回写出的字节个数。
子进程执行netstat -tunlp 在父进程中输出
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2];
int ret;
ret = pipe(fd);
if(ret == -1){
perror("pipe error");
exit(1);
}
pid = fork();
if(pid == 0 )
{
//printf("我是子进程。\n");
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("netstat","netstat","-t","-u","-n","-l","-p",NULL);
perror("execlp error");
}
else if(pid > 0)
{
printf("我是父进程\n");
close(fd[1]);
char buf[4096];
int n;
while(1){
n = read(fd[0],buf,sizeof(buf));
if(n == 0)
{
break;
}
printf("%s\n",buf);
}
wait(NULL);
}
return 0;
}
兄弟进程实现netstat -tunlp | grep 3306
[lc@lc133 pipe]$ cat 2pipe.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2];
int ret,i;
ret = pipe(fd);
if(ret == -1){
perror("pipe error");
exit(1);
}
for(i = 0;i < 2; i++)
{pid = fork();
if(pid == 0) break;
}
if(i == 0 )
{
//printf("我是第%d个子进程。",i+1);
close(fd[0]);
dup2(fd[1],STDOUT_FILENO); //将netstat结果写入管道
execlp("netstat","netstat","-t","-u","-n","-l","-p",NULL);
perror("execlp error");
}
if(i == 1)
{
//printf("我是第%d个子进程。",i+1);
close(fd[1]);
dup2(fd[0],STDIN_FILENO); //管道中的数据传递给grep
execlp("grep","grep","3306",NULL);
perror("execlp error");
}
else if(i == 2)
{
sleep(1);
printf("我是父进程,我会回收子进程。\n");
close(fd[1]);
close(fd[0]);
int ii;
for(ii = 0; ii < 2; ii++){
wait(NULL);
}
}
return 0;
}
[lc@lc133 pipe]$ gcc 2pipe.c -g -Wall
[lc@lc133 pipe]$ ./a.out
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 1519/mysqld
我是父进程,我会回收子进程。
管道缓冲区的大小
[root@lc133 pipe]# ulimit -a
core file size (blocks, -c) 0 #core文件
data seg size (kbytes, -d) unlimited #数据节
scheduling priority (-e) 0
file size (blocks, -f) unlimited #文件大小
pending signals (-i) 7667
max locked memory (kbytes, -l) 8192
max memory size (kbytes, -m) unlimited #内存大小
open files (-n) 1024 #打开文件数目
pipe size (512 bytes, -p) 8 #管道缓冲区大小
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192 #栈区大小
cpu time (seconds, -t) unlimited #cpu时间
max user processes (-u) 7667 #用户最多可开启的程序数目。
virtual memory (kbytes, -v) unlimited #虚拟内存
file locks (-x) unlimited
命名管道fifo函数
命名管道(有名管道)fifo函数,主要解决无血缘关系的进程间通讯
函数创建命名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
例如,创建一个fifo管道,权限664((mode & ~umask))
int ret = mkfifo("fifo", 0664);
if (ret == -1)
perror("mkfifo error");
命令mkfifo创建命名管道
[lc@lc133 pipe]$ mkfifo pipe
[lc@lc133 pipe]$ ll
总用量 0
prw-rw-r-- 1 lc lc 0 12月 27 16:27 pipe
[lc@lc133 pipe]$ cat pipe #阻塞读,开启另一终端echo "hello world" >> pipe
hello world #管道中的数据一旦读走,数据就不在存在
fifo操作起来像文件
进程A往命名管道写数据
写端,open fifo O_WRONLY
或者直接echo往里追加数据
进程B从命名管道读数据
读端,open fifo O_RDONLY
或者使用cat直接读数据
有名管道文件大小永远为 0
一个进程往有名管道里写数据
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
char *str="hello world\n";
fd = open("pipe",O_WRONLY);
if(fd == -1 ){perror("open error");}
write(fd,str,strlen(str));
dup2(fd,STDOUT_FILENO);
execlp("route","route",NULL);
close(fd);
return 0;
}
[lc@lc133 pipe]$ gcc fifow.c -o fifow
[lc@lc133 pipe]$ ./fifow
一个进程从有名管道读数据
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd,ret;
char buf[4096];
fd = open("pipe",O_RDONLY);
if(fd == -1 ){perror("open error");}
while(1){
ret = read(fd,buf,sizeof(buf));
if(ret == 0 )break;
write(STDOUT_FILENO,buf,ret);
}
close(fd);
return 0;
}
[lc@lc133 pipe]$ gcc fifor.c -o fifor
[root@lc133 pipe]# ./fifor
hello world
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default bogon 0.0.0.0 UG 100 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
192.168.145.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
多个程序往有名管道写数据
两个bash都执行,
[lc@lc133 pipe]$ ./fifow
一个bash窗口读
[root@lc133 pipe]# ./fifor
hello world
hello world
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default bogon 0.0.0.0 UG 100 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
192.168.145.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default bogon 0.0.0.0 UG 100 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
192.168.145.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
多个程序从有名管道读数据
两个bash都执行程序读,
[lc@lc133 pipe]$ ./fifor
一个bash窗口写数据
[lc@lc133 pipe]$ ./fifow
只有一个fifor程序读到数据
内存映射(mmap)
如果想要实现进程间通信,可以通过函数创建一块内存映射区,和管道不同的是管道对应的内存空间在内核中,
而内存映射区对应的内存空间在进程的用户区(用于加载动态库的那个区域),
也就是说进程间通信使用的内存映射区不是一块,而是在每个进程内部都有一块。
由于每个进程的地址空间是独立的,各个进程之间也不能直接访问对方的内存映射区,
需要通信的进程需要将各自的内存映射区和同一个磁盘文件进行映射,
这样进程之间就可以通过磁盘文件这个唯一的桥梁完成数据的交互了。
mmap函数
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数:
addr: 指定映射区的首地址。通常传NULL,表示让系统自动分配
length:共享内存映射区的大小。(<= 文件的实际大小)
prot: 共享内存映射区的读写属性。PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标注共享内存的共享属性。MAP_SHARED、MAP_PRIVATE
fd: 用于创建共享内存映射区的那个文件的 文件描述符。
offset:默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍。
返回值:
成功:映射区的首地址。
失败:MAP_FAILED (void*(-1)), errno
flags里面的 shared意思是修改会反映到磁盘上,共享属性
private表示修改不反映到磁盘上,不能同步给其他进程,私有属性
int munmap(void *addr, size_t length); 释放映射区。
addr:mmap 的返回值
length:大小
父子进程利用mmap映射通讯
[lc@lc133 mmap]$ cat 1mmap.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
void *p;
pid_t pid;
int arr[5] = {1,2,3,4,5};
fd = open("./mmap.txt",O_RDWR|O_CREAT|O_TRUNC,0664); //打开文件mmap.txt
if(fd < 0){perror("open mmap.txt error");exit(1);}
ftruncate(fd,10);
p = mmap(NULL,10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //mmap内存映射与mmap.txt关联
if(p == MAP_FAILED){perror("mmap error");exit(1);}
close(fd); //关闭文件
pid = fork();
if(pid == 0)
{
char str[256] = "hello word";
strcpy(p,str); //将str传递进内存,磁盘文件mmap.txt也会变化(MAP_SHARED)
arr[4] = 100;
printf("str:%s,arr[4]=%d\n",str,arr[4]);
}
if(pid > 0)
{
sleep(1); //子进程先写,父进程后读
printf("str:%s,arr[4]:%d\n",(char *)p,arr[4]); //读mmap映射内存中的数据
wait(NULL);
}
munmap(p,10); //释放映射区
return 0;
}
[lc@lc133 mmap]$ gcc 1mmap.c
[lc@lc133 mmap]$ ./a.out
str:hello word,arr[4]=100
str:hello word,arr[4]:5
[lc@lc133 mmap]$ cat mmap.txt
hello word[lc@lc133 mmap]$
mmap注意事项
打开文件尽量以读写方式,fd = open("文件名", O_RDWR);
偏移位置0或者4k(4096)的整数倍,mmap(NULL, 有效文件大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
1. 用于创建映射区的文件大小为 0,实际指定非0大小创建映射区,出 “总线错误”。当映射文件大小为0时,不能创建映射区。用于映射的文件必须要有实际大小。
2. 用于创建映射区的文件大小为 0,实际制定0大小创建映射区, 出 “无效参数”。
3. 用于创建映射区的文件读写属性为,只读。映射区属性为 读、写。 出 “无效参数”。
4. 创建映射区,需要read权限。当访问权限指定为 “共享”MAP_SHARED是, mmap的读写权限,应该 <=文件的open权限。 只写不行。MAP_PRIVATE则无所谓,因 为mmap中的权限是对内存的限制。
5. 文件描述符fd,在mmap创建映射区完成即可关闭。后续访问文件,用 地址访问。
6. offset 必须是 4096的整数倍。(MMU 映射的最小单位 4k )
7. 对申请的映射区内存,不能越界访问。
8. munmap用于释放的 地址,必须是mmap申请返回的地址。
9. 映射区访问权限为 “私有”MAP_PRIVATE, 对内存所做的所有修改,只在内存有效,不会反应到物理磁盘上。
10. 映射区访问权限为 “私有”MAP_PRIVATE, 只需要open文件时,有读权限,用于创建映射区即可。
无血缘关系进程利用mmap映射通讯
通讯的进程必须mmap映射到同一磁盘文件。
先写内存
[lc@lc133 mmap]$ cat 2mmap.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
void *p;
fd = open("./mmap.txt",O_RDWR|O_CREAT|O_TRUNC,0664);
if(fd < 0){perror("open mmap.txt error");exit(1);}
ftruncate(fd,256);
p = mmap(NULL,256,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED){perror("mmap error");exit(1);}
close(fd);
char str[256] = "heppy new year to one and all";
strcpy(p,str);
printf("str:%s\n",str);
munmap(p,256);
return 0;
}
[lc@lc133 mmap]$ gcc 2mmap.c -o 2mmap
[lc@lc133 mmap]$ ./2mmap
str:heppy new year to one and all
读内存
[lc@lc133 mmap]$ cat 3mmap.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;
void *p;
int size;
fd = open("./mmap.txt",O_RDWR);
if(fd < 0){perror("open mmap.txt error");exit(1);}
size = lseek(fd, 0,SEEK_END );
p = mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED){perror("mmap error");exit(1);}
close(fd);
printf("str:%s\n",(char *)p);
munmap(p,size);
return 0;
}
[lc@lc133 mmap]$ gcc 3mmap.c -o 3mmap
[lc@lc133 mmap]$ ./3mmap
str:heppy new year to one and all
匿名映射
只能在有血缘关系进程间通讯
创建子进程会发生虚拟地址空间的复制,那么在父进程中创建的内存映射区也会被复制到子进程中,这样在子进程里边就可以直接使用这块内存映射区了.
无需打开文件
void *p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
文件大小size,根据需要填写
共享属性:MAP_ANONYMOUS 或 MAP_ANON,
文件描述符:-1
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
void *p;
pid_t pid;
p = mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
if(p == MAP_FAILED){
perror("mmap error");
exit(1);}
pid = fork();
if(pid == 0){strcpy(p, "heppy new year");};
if(pid > 0){sleep(1);printf("%s\n",p) ;wait(NULL); }
munmap(p,4096);
return 0;
}
完成文件拷贝
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd,size,fd2;
void *p;
fd = open("/etc/passwd",O_RDONLY); //打开原始文件必须有读的权限
if(fd < 0){perror("open mmap.txt error");exit(1);}
size = lseek(fd,0,SEEK_END); //获得文件大小
p = mmap(NULL,size,PROT_READ,MAP_SHARED,fd,0); //mmap内存映射,注意读写权限
if(p == MAP_FAILED){perror("mmap error");exit(1);}
close(fd);
fd2 = open("passwd.bak",O_RDWR|O_CREAT|O_TRUNC,0644); //创建文件必须有写的权限
if(fd2 < 0){perror("open mmap.txt error");exit(1);}
ftruncate(fd2,size); //拓展文件大小
void *q;
q = mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //mmap内存映射,注意读写权限
if(q == MAP_FAILED){perror("mmap error");exit(1);}
close(fd2);
memcpy(q,p,size); //完成内存数据的拷贝
munmap(p,size); //释放映射区
munmap(q,size);
return 0;
}
共享内存
共享内存:不属于任何进程,并且不受进程生命周期的影响,通过系统调用的到共享内存的地址,与进程关联,进程就可以对内存进行读写了。一块共享内存可以被多个进程关联,进程也可以和这块共享内存解除关联,解除关联之后就不能操作这块共享内存了。在所有进程间通信的方式中共享内存的效率是最高的。
共享内存操作默认不阻塞,多个进程同时读写共享内存,可能出现数据混乱,共享内存需要借助其他机制来保证进程间的数据同步,比如:信号量.
共享内存shm与内存映射mmap的区别
shm: 多个进程只需要一块共享内存就够了,共享内存不属于进程,需要和进程关联才能使用
内存映射区:位于每个进程的虚拟地址空间中,并且需要关联同一个磁盘文件才能实现进程间数据通信
shm: 直接对内存操作,效率高
内存映射区:需要内存和文件之间的数据同步,效率低
内存映射区:进程退出,内存映射区也就没有了
shm:进程退出对共享内存没有影响,调用相关函数 命令 关机才能删除共享内存,如果一个进程退出,会自动和共享内存进行取消关联。
内存映射区:可以完整的保存数据,内存映射区数据会同步到磁盘文件
shm:数据存储在物理内存中,断电之后系统关闭,内存数据也就丢失了
创建打开共享内存shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
作用:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识。新创建的内存段中的数据都会被初始化为 0
参数:
key :key_t 类型是一个整形,通过这个找到或者创建一个共享内存。一般使用 16 进制表示,非 0 值
size:共享内存的大小,不能为 0
shmflg:属性,包括了访问权限、附加属性(创建/判断共享内存是不是存在)
例如:IPC_CREAT | IPC_EXCL | 0664 如果共享内存已经存在, 共享内存创建失败, 返回-1,
返回:
成功:> 0 返回共享内存的引用的 ID,后面操作共享内存都是通过这个值。
失败:-1 并设置错误号
ftok函数生成一个共享内存的 key
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:指定一个存在的路径
proj_id:int 类型的值,但是这系统调用只会使用其中的 1 个字节,传参的时候要将其作为 char 进行操作,取值范围: 1-255,一般传字符'a'
返回值:成功则返回生成的key值,失败则返回-1;
关联shmat函数
void *shmat(int shmid, const void *shmaddr, int shmflg);
作用:和当前的进程进行关联
参数:
shmid:共享内存的标识(ID),由 shmget 返回值获取
shmaddr:申请的共享内存的起始地址,通常传入 NULL,由内核指定
shmflg:对共享内存的操作
读 : SHM_RDONLY,必须要有读权限
读写: 0
返回:成功返回共享内存的首(起始)地址。 失败返回 (void *) -1
解除关联shmdt函数
int shmdt(const void *shmaddr);
作用:解除当前进程和共享内存的关联
参数:shmat () 函数的返回值,共享内存的起始地址
返回值:关联解除成功返回 0,失败返回 - 1
删除共享内存shmctl函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
作用:对共享内存进行操作。删除共享内存,共享内存要删除才会消失,创建共享内存的进程被销毁了对共享内存是没有任何影响。
参数:
shmid:共享内存的 ID
cmd:要做的操作
IPC_STAT:获取共享内存的当前的状态
IPC_SET:设置共享内存的状态
IPC_RMID:标记共享内存被销毁
buf:需要设置或者获取的共享内存的属性信息
IPC_STAT:buf 存储数据
IPC_SET:buf 中需要初始化数据,设置到内核中
IPC_RMID:没有用,NULL
参数 struct shmid_ds 结构体
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
返回值:函数调用成功返回值大于等于 0,调用失败返回 - 1
shmctl () 函数是一个多功能函数,可以设置、获取共享内存的状态也可以将共享内存标记为删除状态。
当共享内存被标记为删除状态之后,并不会马上被删除,直到所有的进程全部和共享内存解除关联,共享内存才会被删除。
struct shmid_ds 的结构体中,shm_nattch,在这个成员变量里边记录着当前共享内存关联的进程的个数,一般将其称之为引用计数。当共享内存被标记为删除状态,并且这个引用计数变为 0 之后共享内存才会被真正的被删除掉。
当共享内存被标记为删除状态之后,共享内存内部维护的 key 从一个正整数变为 0,只有已经关联成功的进程才允许继续访问共享内存,不再允许新的进程和这块共享内存进行关联了。
进程间通讯
创建并往共享内存写
[lc@lc133 shm]$ cat write.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
key_t key;
int shmid;
key = ftok(".",'a');
if(key < 0){perror("ftok error");exit(1);}
shmid = shmget(key,4096,IPC_CREAT|IPC_EXCL|0644);
if(shmid < 0){perror("shmget error");exit(1);}
void *p = shmat( shmid,NULL,0);
if( p == (void *)-1 ){perror("shmat error");exit(1);}
char *str = "heppy new year to one and all";
memcpy(p,str,strlen(str)+1);
printf("按任意键解除关联共享内存");
getchar();
int ret = shmdt(p);
if(ret == -1){perror("shmdt error");exit(1);}
printf("按任意键删除关联共享内存");//必须在删除之前,进行读共享内存。否则可以将删除共享内存注释掉
getchar();
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
[lc@lc133 shm]$ gcc write.c -o write
[lc@lc133 shm]$ ./write
按任意键解除关联共享内存
按任意键删除关联共享内存
读
[lc@lc133 shm]$ cat read.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
key_t key;
int shmid;
key = ftok(".",'a');
if(key < 0){perror("ftok error");exit(1);}
shmid = shmget(key,0,0);
if(shmid < 0){perror("shmget error");exit(1);}
void *p = shmat( shmid,NULL,0);
if( p == (void *)-1 ){perror("shmat error");exit(1);}
printf("读共享内存:%s\n",(char *)p);
printf("按任意键解除关联共享内存");
getchar();
int ret = shmdt(p);
if(ret == -1){perror("shmdt error");exit(1);}
printf("按任意键删除关联共享内存");
getchar();
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
[lc@lc133 shm]$ gcc read.c -o read
[root@lc133 shm]# ./read
读共享内存:heppy new year to one and all
按任意键解除关联共享内存
按任意键删除关联共享内存
查看共享内存的shell命令
ipcs 用法
ipcs -a // 打印当前系统中所有的进程间通信方式的信息
ipcs -m // 打印出使用共享内存进行进程间通信的信息
ipcs -q // 打印出使用消息队列进行进程间通信的信息
ipcs -s // 打印出使用信号进行进程间通信的信息
ipcrm 用法
ipcrm -M shmkey // 移除用shmkey创建的共享内存段
ipcrm -m shmid // 移除用shmid标识的共享内存段
ipcrm -Q msgkey // 移除用msqkey创建的消息队列
ipcrm -q msqid // 移除用msqid标识的消息队列
ipcrm -S semkey // 移除用semkey创建的信号
ipcrm -s semid // 移除用semid标识的信号
[lc@lc133 shm]$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 10 lc 777 16384 1 目标
0x00000000 11 lc 777 2129920 2 目标
0x00000000 17 lc 600 524288 2 目标
0x00000000 18 lc 777 2129920 2 目标
0x00000000 21 lc 600 524288 2 目标
0x00000000 22 lc 600 524288 2 目标
0x1f190679 23 root 600 1000 6
[lc@lc133 shm]$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 10 lc 777 16384 1 目标
0x00000000 11 lc 777 2129920 2 目标
0x00000000 17 lc 600 524288 2 目标
0x00000000 18 lc 777 2129920 2 目标
0x00000000 21 lc 600 524288 2 目标
0x00000000 22 lc 600 524288 2 目标
0x1f190679 23 root 600 1000 6
0x610043b1 27 lc 644 4096 0
[lc@lc133 shm]$ ipcrm -m 27 #删除指定id的共享内存
[lc@lc133 shm]$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 10 lc 777 16384 1 目标
0x00000000 11 lc 777 2129920 2 目标
0x00000000 17 lc 600 524288 2 目标
0x00000000 18 lc 777 2129920 2 目标
0x00000000 21 lc 600 524288 2 目标
0x00000000 22 lc 600 524288 2 目标
0x1f190679 23 root 600 1000 6
在写读过程中共享内存的变化
[lc@lc133 shm]$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 10 lc 777 16384 1 目标
0x00000000 11 lc 777 2129920 2 目标
0x00000000 17 lc 600 524288 2 目标
0x00000000 18 lc 777 2129920 2 目标
0x00000000 21 lc 600 524288 2 目标
0x00000000 22 lc 600 524288 2 目标
0x1f190679 23 root 600 1000 6
0x610043b1 33 lc 644 4096 1 #执行写操作
[lc@lc133 shm]$ ipcs -m
0x610043b1 33 lc 644 4096 2 #再执行读操作 关联数为2
0x610043b1 33 lc 644 4096 1 #写操作解除关联,关联数-1
0x00000000 33 lc 644 4096 1 目标 #写操作删除共享内存,key值变为0
[lc@lc133 shm]$ ipcs -m
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 10 lc 777 16384 1 目标
0x00000000 11 lc 777 2129920 2 目标
0x00000000 17 lc 600 524288 2 目标
0x00000000 18 lc 777 2129920 2 目标
0x00000000 21 lc 600 524288 2 目标
0x00000000 22 lc 600 524288 2 目标
0x1f190679 23 root 600 1000 6
#读操作操作解除关联,关联数-1为0,共享内存被删除