440 likes | 905 Views
架构平台部 IPC 通讯培训 [ 共享内存 , 管道 , 信号量 ]. 讲师: felixzhu 日期: 2009 年 08 月 12 日. 课程简介. 通过本课程,您将了解到 Linux IPC 通讯的相关技术:共享内存,管道,信号量。重点介绍了各个 IPC 技术的优缺点和应用场景, Linux 系统提供的 IPC 相关 API ,在课程的最后给出了一些 IPC 相关的习题和参考资料。. 目录. 进程间通讯 (IPC) 线程间同步 Linux IPC: 共享内存 (Shared Memory) Linux IPC: 管道 (Pipe)
E N D
架构平台部IPC通讯培训[共享内存,管道,信号量]架构平台部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共享内存区别: • 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 工具 felixzhu@172.25.38.174:~$ 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) 管道的读写 1、 写管道时,常数PIPE_BUF规定了内核中管道缓存器的大小 /usr/include/linux/limits.h:#define PIPE_BUF 4096 • 2、 管道破裂: 管道的一端关闭时, • a) 写端关闭,读该管道在所有数据都被读取后, • read返回0, 表示达到了文件结束 • b) 读端关闭,写该管道产生信号SIGPIPE
管道(pipe) 管道用于标准输入和标准输出 • 管道:shell中的形式 • cmd1 | cmd2 • 重定向 cmd > file • 实现代码 • 执行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);
管道:命名管道(FIFO) #include <sys/stat.h> int mkfifo (const char* path, mode_t mode); int unlink(const char* path) ; Shell命令操作: mkfifo命令 rm 命令
管道:命名管道(FIFO) FIFO的同步和读写 • 打开FIFO时的同步 • 一般情况下(没有说明O_NONBLOCK),只读打开要阻塞到某个其它进程为写打开此FIFO;类似的,为写打开一个FIFO要阻塞到某个其它进程为读而打开它。 • 如果指定了O_NONBLOCK,则只读打开立即返回;只写打开也立即返回,但如果没有进程已经为读而打开此FIFO,那么open将出错返回 -1,errno置为ENXIO。 • 读写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);
信号量的使用 • 使用System V的信号量集 可以方便多个信号量的管理,一个信号量集对应一个sem_key • semop 和 semtimedop 操作可能被信号中断,errno == EINTR, 在使用时注意判断下函数返回值并对EINTR的情况进行重试
课后作业: • 要求: 1. 课后作业可个人独立完成 也可 小组协作完成 (小组完成的请注明参与的小组成员) 2. 请注意 编程风格,清晰的实现思路 和 干净整洁的代码 均 会获得适当加分
课后作业1:基于共享内存的管道 • 请用C++封装一个类,该类在能够实现基于共享内存的管道功能 • 要求: 1. 当指定的共享内存不存在时,自动创建指定key 和大小的共享内存 当指定的共享内存存在时,能自动绑定并使用该共享内存 2. 接口
课后作业2:管道 • 编写一个程序(程序名:myls)包装下 ls 的功能,要求对ls的功能做一点 小调整: 当输入 ls –la 时将 total 统计行放到最好一行
课后作业3:程序设计类 编写一个 斗地主的 出牌仲裁器: • 规则参考 附件给定的规则 • 出牌仲裁器必须实现如下场景的功能: 玩家A出牌,当前轮到玩家B出牌,玩家B出牌后出牌仲裁器能够判断玩家B出的牌是否有效。 • 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