架构平台部IPC 通讯培训
[ 共享内存 , 管道 , 信号量 ]
讲师: felixzhu日期: 2009 年 08 月 12 日
课程简介
通过本课程,您将了解到 Linux IPC 通讯的相关技术:共享内存,管道,信号量。重点介绍了各个 IPC 技术的优缺点和应用场景, Linux 系统提供的 IPC 相关 API ,在课程的最后给出了一些 IPC 相关的习题和参考资料。
目录
进程间通讯 (IPC)线程间同步Linux IPC: 共享内存 (Shared Memory)Linux IPC: 管道 (Pipe)Linux IPC: 信号量 (Semaphore)Linux IPC 实践 (Ready & Rock)参考资料 (Reference)
自我简介
felixzhu ( 朱建平)1999 ~ 2006 WuHan University
Intrested Area:
Win32 开发 (GUI,COM/AtiveX,Network)
P2P Streaming, P2P VOD, Distributed System
Win32 Reverse Enginering
Linux Server Programming
RIA :Flash ActionScript
…
进程间通讯 (IPC)
共享内存 信号量(集) 消息队列管道(匿名 & 命名)信号量Socket( Unix domain socket)
文件( Shell 中用得比较多)
线程间同步
Linux
pthread 库提供的三种线程同步机制: 互斥锁 : pthread_mutex_t
条件变量 : pthread_cond_t
读写锁 : pthread_rwlock_t
Win32
事件 : Event
临界区 : CRITICAL_SECTION
POSIX
POSIX : Portable Operating System Interface of Unix
POSIX 包含了众多的扩展: POSIX:SEM 匿名信号量和命名信号量 POSIX:XSI 共享内存,信号量集和消息队列 , 间隔定时器 ... POSIX:TMR 定时器 …
POSIX 扩展不仅定义了 API 接口,还规范了相关的工具和命令,如 POSIX:XSI 中就提供了列举和删除相关实体的命令解释程序命令
ipcs 和 ipcrm
共享内存
共享内存共享内存是内核为进程创建的一个特殊内存段,它可连接(attach) 到自己的地址空间,也可以连接到其它进程的地址空间最快的进程间通信方式不提供任何同步功能
共享内存原理图
共享内存的应用场景
进程退出后,共享内存仍然保留,下次进程启动后 Attach 到共享内存继续使用比如:基于共享内存的 Cache 系统,
TFS 的内存 Cache 实现: CacheAccess
共享内存的应用场景
进程间通讯,一般和信号量结合使用比如: TFS 的队列 : tfc::net::CFifoSyncMQ
共享内存 :POSIX vs System V
Posix 共享内存与 System V 共享内存区别 :
1. Posix 共享内存区对象的大小可在任何时刻通过调用 ftruncate 修改,而 System V 共享内存区对象的大小是在调用 shmget 创建时固定下来。
System V 共享内存后来被 POSIX 采纳,即现在的 POSIX:XSI 共享内
存
POSIX 共享内存 API
#include <sys/types.h>#include <sys/mman.h>#include <fcntl.h>int shm_open(const char *name, int oflag, mode_t mode);int shm_unlink(const char *name);
#include <sys/mman.h>void* mmap (void* addr, size_t len, int prot, int flags, int fildes, off_t off);
#include <unistd.h>int ftruncate (int fildes, off_t length);
System V 共享内存 POSIX:XSI API
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
void* shmat(int shmid, const void* shmaddr, int shmflg);
int shmdt(const void* shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds* buf);
共享内存使用经验
单进程一般配置使用小于 1.5GB 的共享内存(为什么?)
共享内存一般需要和其它 IPC 配合使用 基于共享内存的 HashMap
基于共享内存的 FIFO 队列 …
共享内存在进程退出后不会丢失,仅在重启后共享内存的数据会丢失( Dump + Binlog 机制可用于备份共享内存中的数据)
POSIX:XSI 工具
ipcs 命令: 显示与 POSIX:XSI 进程间通讯资源有关的信息 .
ipcs [-qms] [-a | -bcopt]
ipcrm 命令 : 删除 POSIX:XSI 进程间通讯的资源 .
ipcrm [-q msgid | -Q msgkey |
-s semid | -S semkey |
-m shmid| -M shmkey]
POSIX:XSI 工具
[email protected]:~$ ipcs
------ Shared Memory Segments --------key shmid owner perms bytes nattch status 0x00005feb 0 root 666 12000 4 0x4000910a 18350083 felixzhu 600 33554432 0 ------ Semaphore Arrays --------key semid owner perms nsems 0x00008708 0 root 666 1 0x4000910b 4292618 felixzhu 600 2 0x40009102 4325387 felixzhu 600 2 ------ Message Queues --------key msqid owner perms used-bytes messages
管道 :Pipe
两种类型的管道: 匿名管道: 在创建进程及其 fork 的后代进程中使用 命名管道: FIFO, 像普通文件一样,有名字和访问权限,而且 会出现在 ls 列出的目录列表中。 任何具有恰当权限的进程都可以访问 FIFO.
管道 : 匿名管道 (Pipe)
#include <unistd.h>
int pipe(int fildes[2]);
单工模式的管道:fildes[0] 用于从管道中读取数据fildes[1] 用于将数据写入管道
管道 : 匿名管道 (Pipe) if(pipe(fdes)<0) return -1;
if((pid=fork())==0) { // 子进程 close(fdes[1]); // 关闭写端 r_num=read(fdes[0],r_buf,sizeof(r_buf)-1); // 向读端读取数据 printf( "read num is %d the data read from the pipe is d\n",r_num,atoi(r_
buf)); close(fdes[0]); exit(); } else if(pid>0) { // 父进程 close(fdes[0]);// 关闭读端 strcpy(w_buf,"111"); if(write(fdes[1],w_buf,4)!=-1) // 向写端写数据 printf("parent write over\n"); close(fdes[1]);//write }
管道 : 匿名管道 (Pipe)
当进程调用了 pipe
管道 : 匿名管道 (Pipe)
fork 被调用后
管道 : 匿名管道 (Pipe)
两个进程分别关闭一个端
管道 : 匿名管道 (Pipe)
2 、 管道破裂 : 管道的一端关闭时,
a) 写端关闭,读该管道在所有数据都被读取后, read 返回 0 , 表示达到了文件结束
b) 读端关闭,写该管道产生信号 SIGPIPE
管道的读写
1 、 写管道时,常数 PIPE_BUF 规定了内核中管道缓存器的大小
/usr/include/linux/limits.h:#define PIPE_BUF 4096
管道: shell 中的形式cmd1 | cmd2
重定向 cmd > file
管道用于标准输入和标准输出
管道( pipe)
实现代码执行 cmd1前
if (fd[1] != STDOUT_FILENO) {if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
err_sys(“dup2 error to stdout);}
执行 cmd2前if (fd[0] != STDIN_FILENO) {
if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) err_sys(“dup2 error to stdin);}
管道( pipe)
popen, pclose: process I/O
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
管道( pipe)
管道 : 命名管道 (FIFO)
#include <sys/stat.h>
int mkfifo (const char* path, mode_t mode);
int unlink(const char* path) ;
Shell 命令操作: mkfifo 命令 rm 命令
管道 : 命名管道 (FIFO)
打开 FIFO 时的同步一般情况下(没有说明 O_NONBLOCK ),只读打开要阻塞到某个其它进程为写打开此 FIFO;类似的,为写打开一个 FIFO 要阻塞到某个其它进程为读而打开它。如果指定了 O_NONBLOCK ,则只读打开立即返回;只写打开也立即返回,但如果没有进程已经为读而打开此 FIFO ,那么 open 将出错返回 -1 , errno 置为 ENXIO 。
FIFO 的同步和读写
读写 FIFO 时的同步same as pipe
管道 : 命名管道 (FIFO)C/S 应用程序
例: client.c, server.c
信号量的原理
信号量
POSIX:SEM 信号量 #include <semaphore.h>
匿名信号量 命名信号量
POSIX:XSI 信号量集 #include <sys/sem.h>
ipcs & ipcrm 工具
POSIX:SEM 匿名信号量
一般用于 父子进程 之间的同步#include <semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned value);int sem_destroy(sem_t* sem);
int sem_post(sem_t* sem);int sem_trywait(sem_t* sem);int sem_wait(sem_t* sem);int sem_getvalue(sem_t* restrict sem, int* restrict sval);
POSIX:SEM 命名信号量
可用于任意进程间的同步#include <semaphore.h>
sem_t* sem_open(const char* name, int oflag,…);int sem_close(sem_t* sem);int sem_unlink(const char* name);
int sem_post(sem_t* sem);int sem_trywait(sem_t* sem);int sem_wait(sem_t* sem);int sem_getvalue(sem_t* restrict sem, int* restrict sval);
信号量集 POSIX:XSI API
#include <sys/sem.h>
int semget (key_t key, int nsems, int semflg);
int semctl (int semid, int semnum,int cmd, …);
int semop (int semid, struct sembuf* sops, size_t nsops);
int semtimedop(int semid, struct sembuf* sops, unsigned nsops,
struct timespec* timeout);
信号量的使用
1. 使用 System V 的信号量集 可以方便多个信号量的管理,一个信号量集对应一个 sem_key
1. semop 和 semtimedop 操作可能被信号中断, errno == EINTR , 在使用时注意判断下函数返回值并对 EINTR 的情况进行重试
课后作业:
要求: 1. 课后作业可个人独立完成 也可 小组协作完成 (小组完成的请注明参与的小组成员)
2. 请注意 编程风格,清晰的实现思路 和 干净整洁的代码 均 会获得适当加分
课后作业 1 :基于共享内存的管道
请用 C++ 封装一个类,该类在能够实现基于共享内存的管道功能要求:
1. 当指定的共享内存不存在时,自动创建指定 key 和大小的共享内存 当指定的共享内存存在时,能自动绑定并使用该共享内存 2. 接口
课后作业 2 :管道
编写一个程序 ( 程序名 :myls) 包装下 ls 的功能,要求对 ls 的功能做一点
小调整: 当输入 ls –la 时将 total 统计行放到最好一行
课后作业 3 :程序设计类
编写一个 斗地主的 出牌仲裁器:1. 规则参考 附件给定的规则
2. 出牌仲裁器必须实现如下场景的功能: 玩家 A 出牌 , 当前轮到玩家 B 出牌,玩家 B 出牌后出牌仲裁器能够判断玩家 B 出的牌是否有效。
3. C++语言实现
推荐书目:
1. Linux 环境高级编程 ( 第二版 ) APUE(Second Edition)
2. Unix 系统编程 (Unix Systems Programming)
3. 郑彦兴的“ Linux IPC 通信机制”文章 http://blog.chinaunix.net/u2/86340/showart_1673177.html
4.Google –Linux IPC
Q&A