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

一 有哪些进程间通信方式

IPC就是两个或者多个进程之间的数据交互。
IPC的方法:
1. 文件
2. 信号(signal)
3. 管道
4. 共享内存
5. 消息队列(会做一个综合练习)
6. 信号量集(与前面学的信号无关semaphore)
7. 网络编程(socket)
….
IPC


二 管道

  管道是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); //删除
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值