您的位置:首页 > 其它

进程间通信(IPC)(1)—— 管道、共享内存

2016-07-21 22:36 459 查看

一 有哪些进程间通信方式

IPC就是两个或者多个进程之间的数据交互。

IPC的方法:

1. 文件

2. 信号(signal)

3. 管道

4. 共享内存

5. 消息队列(会做一个综合练习)

6. 信号量集(与前面学的信号无关semaphore)

7. 网络编程(socket)

….



二 管道

  管道是Unix最古老的IPC方式,现在已很少使用。

  4 5 6 用法类似,统称为XSI IPC,遵守相同的规范。

  管道分为有名管道和无名管道。有名管道由程序建立管道文件,用于进程间的通信。而无名管道由内核建立管道文件,用于fork()创建的父子进程之间的通信。

  

  管道是通过管道文件(媒介) 进行交互的。管道文件和 普通文件有所区别。管道文件是 mkfifo命令创建的或者mkfifo()函数新建,管道文件的后缀 .pipe。

  管道文件只是数据的中转站,不存储数据。因此,只有读写都ok时,才能畅通。(echo cat)

  管道的使用和文件一样的,但管道文件的创建必须用mkfifo。

1 有名管道

pipea.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
int fd = open("a.pipe",O_WRONLY);
//O_CREAT用不到,因为它无法创建管道文件
//O_RDWR同时开通了读管道和写管道
if(fd==-1) perror("open"),exit(-1);
int i;
for(i=0;i<100;i++){
write(fd,&i,4);
}
close(fd);
}

pipeb.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
int fd = open("a.pipe",O_RDONLY);
if(fd==-1) perror("open"),exit(-1);
int i;
for(i=0;i<100;i++){
int x;
read(fd,&x,4);
printf("x=%d\n",x);
}
close(fd);
}


2 无名管道

  管道文件由内核管理,只能用于 fork()创建的父子进程之间。

  借助pipe() 创建一个读管道,一个写管道。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
int fd[2] = {};
pipe(fd);//创建了两个管道,fd[0] 读 fd[1] 写
pid_t pid = fork();
if(pid == 0){//子进程
close(fd[1]);//关闭写端
int i;
for(i=0;i<100;i++){
int x;
read(fd[0],&x,4);
printf("%d ",x);
fflush(0);
}
close(fd[0]);
exit(0);
}
close(fd[0]);
int i;
for(i=100;i<200;i++){
write(fd[1],&i,4);
usleep(100000); }
close(fd[1]);
}




三 XSI IPC结构

XSI IPC 包括共享内存、消息队列、信号量集,隶属于同一个规范,有着共同的特征。

每个XSI IPC结构 都是在内核中存储和维护的,用ipcs命令可以查看,用ipcrm命令可以删除。

  ipcs -a 查看全部IPC结构

  -m 查看共享内存

  -q 查看消息队列

  -s 查看信号量集

ipcrm 用法和ipcs 类似,但后面要跟上ID。

每个XSI IPC结构都有两个东西定位:外部到内核要用key,内核中使用ID标识。

key的生成:

key的类型key_t,其实是一个长整形,有3种方法得到key:

1 使用宏 IPC_PRIVATE 做key,但这种方式无法实现 进程间的通信(私有),极少使用。

2 把所有的key定义在一个头文件中,用宏定义。

3 使用ftok()函数生成key,参数:真实存在的路径和项目编号(0-255)。

ID的生成:

IPC结构在内核中都用ID做唯一标识,创建/获取ID都有对应的函数,比如:

  int shmid = shmget(key,…);

  int msgid = msgget(key,…);

调用XXXget()时,都有一个flags,创建时的值为

  权限|IPC_CREAT|IPC_EXCL

  IPC_CREAT - 创建

  IPC_EXCL - 如果存在会返回错误

和O_CREAT O_EXCL 类似。

IPC结构都有一个特殊的操作函数,提供查询、修改和删除的功能。

函数名:XXXctl(),比如:shmctl() msgctl()

参数cmd提供功能:

  IPC_STAT - 查询IPC结构的属性/状态

  IPC_SET - 修改IPC结构的相关属性,但只能修改权限

  IPC_RMID - 删除IPC结构,按ID删除

3.1 共享内存

  每个进程内存独立的,无法直接互访。共享内存就是内核管理一段内存(物理内存),这段物理内存允许 每个进程进行映射。

编程步骤:

1 系统创建/获取共享内存(拿到物理内存)

  ftok()-> key

  shmget() -> 创建/获取共享内存,返回ID

2 挂接共享内存(映射)  shmat()

3 使用共享内存

4 脱接共享内存(解除映射)  shmdt()

5 如果共享内存不再被使用,可以删除。 shmctl()

共享内存的优缺点:

  优点:速度最快的IPC,高效率

  缺点:如果有多个进程写数据,将会产生覆盖问题,导致数据的错误和不完整。

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

int main(){
key_t key = ftok(".",100);//生成key
if(key==-1) perror("ftok"),exit(-1);
int shmid = shmget(key,4,
0666|IPC_CREAT|IPC_EXCL);//创建shm
if(shmid==-1) perror("shmget"),exit(-1);
void* p = shmat(shmid,0,0);//挂接共享内存
int* pi = p;
*pi = 1000;
int res = shmdt(p);//脱接共享内存
if(res==-1) perror("shmdt"),exit(-1);
printf("all ok\n");
}//练习:写shmb.c,利用共享内存取出1000并打印

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

int main(){
key_t key = ftok(".",100);//生成key
if(key==-1) perror("ftok"),exit(-1);
int shmid = shmget(key,0,0);//获取shm
if(shmid==-1) perror("shmget"),exit(-1);
void* p = shmat(shmid,0,0);//挂接共享内存
int* pi = p;
printf("*pi=%d\n",*pi);
int res = shmdt(p);//脱接共享内存
if(res==-1) perror("shmdt"),exit(-1);
}


shmctl.c

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

int main(){
key_t key = ftok(".",100);
int shmid = shmget(key,0,0);
if(shmid==-1) perror("shmget"),exit(-1);
struct shmid_ds ds;
shmctl(shmid,IPC_STAT,&ds);//取shm的状态
printf("key=%x\n",ds.shm_perm.__key);
printf("mode=%o\n",ds.shm_perm.mode);
printf("size=%d\n",ds.shm_segsz);
printf("cpid=%d\n",ds.shm_cpid);
printf("nattch=%d\n",ds.shm_nattch);//挂接数
ds.shm_perm.mode = 0640;//修改状态 可以改
ds.shm_segsz = 40;//不可以修改
shmctl(shmid,IPC_SET,&ds);//在修改状态
//shmctl(shmid,IPC_RMID,0); //删除
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: