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

来源:互联网 发布:mac系统画流程图 编辑:程序博客网 时间:2024/06/04 03:40

一 有哪些进程间通信方式

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); //删除}
0 0
原创粉丝点击