免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1702 | 回复: 0
打印 上一主题 下一主题

Linux高级编程 - 进程间通信(Interprocess Communication) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-01-04 10:40 |只看该作者 |倒序浏览
ALP Chapter 5 进程间通信(Interprocess Communication)
  • 这一章就是著名的IPC,这个东西实际的作用和它的名字一样普及。例如我们浏览网页,打印文章,等等。
  • IPC总共有五种类型:
    • 共享内存(Shared Memory):最容易理解的一种,就像一个特工把情报放在特定地点(内存),另一个特工再过来取走一样。
    • 内存映射(Mapped Memory):和共享内存几乎相同,除了特工们把地点从内存改成了文件系统。
    • 管道(Pipes):从一个进程到另一个进程的有序通信,用电话来比喻再恰当不过了。
    • FIFOs:和管道和类似,唯一的区别是FIFOs比管道更神通一些,允许没有关系的进程之间的有序通信。
    • 套接字(Sockets):为什么说浏览网页也是IPC?就是因为它。

[color="#0000ff"]5.1 共享内存(Shared Memory)
  • 共享内存是最快捷的进程间通信方式。访问共享内存的效率和访问进程自己的非共享内存的效率是相同的,而且这种通信方式不需要任何额外的系统调用。
  • 系统不会自动为共享内存处理同步问题,这个问题必须由用户自己解决。
  • 共享内存的步骤通常是:
    • 一个进程申请一块共享内存,即在它的页表中加入新的一项
    • 所有进程Attach该共享内存,即从申请内存的进程中拷贝对应的页表
    • 使用该内存进行通讯
    • 结束后所有进程detach该共享内存
    • 申请共享内存的进程在确定所有进程都detach后,释放该内存

  • 由于共享内存是通过页表来实现的,我们可以得出一个结论:共享内存的大小是页面大小的整数倍,页面的大小可以通过getpagesize()来得到,通常在Linux下该值是4KB
  • 相关的API函数:
    • 申请共享内存:shmget,返回共享内存segment的id
    • Attach,Detach函数:shmat,shmdt。需要共享内存segment的id
    • 释放申请的内存:shmctl。一定要记得释放!调用exit和exec会自动detach,但不会自动释放。

  • 使用 ipcs -m来观看当前系统存在的共享内存

[color="#0000ff"]5.2 进程信号量
  • 信号量(Semaphore)的概念前面已经介绍过了。Linux对用来同步进程的信号量采取了一种特别的实现方式。这些信号量也就被称为进程信号量(Process Semaphore)。(这一节下面所提到的所有信号量默认都是指进程信号量)
  • 相关的API函数:
    • 申请:semget
    • 释放:semctl。需要注意的是信号量不会被自动释放,我们必须显式释放它。
    • Wait和Post:semop

  • 使用ipcs -s来观看当前系统存在的信号量

[color="#0000ff"]5.3 内存映射
  • 内存映射使得不同的进程可以通过一个共享文件来互相通信。
  • 相关的API函数:
    • 映射:mmap
    • 同步:msync。用来指定对文件的修改是否被buffer。
    • 释放:munmap。在程序结束的时候会自动unmap

  • mmap的其他用法:
    • 可以替代read和write,有时使用内存映射后的效率比单纯使用I/O操作来的更快
    • 在内存映射文件中构建structure,修改structure再次将文件映射到内存中可以快速的将structure恢复到原来的状态
    • 把/dev/zero文件映射到内存中。该文件可以提供无限的0,并且写到该文件的所有内容将被直接丢弃

[color="#0000ff"]5.4 管道(Pipes)
  • 管道是单向的,即一个线程写,另一个线程读,无法互换
  • 如果写的速度太快,造成管道满了,那么写的线程就会被block;如果读的速度太快,造成管道空了,那么读的进程就会被block。因此事实上我们可以说管道自动实现了同步机制
  • 我们可以通过调用pipe函数来生成一对pipe file
    description。(为什么是一对?因为一个读一个写)。可是,生成的pipe file
    description无法传送给不相关的进程(因为做为file
    descriptor即使它拿到了也没法用)。但是我们注意到fork之后父进程所有的file
    descriptor在子进程中依然有效,因此管道最大的作用是在父子进程之间通信。或者更确切的说,是在有共同祖先的进程之间通信。
  • 典型的创建管道的流程如下:
    • 用pipe生成2个pipe file description(简称fds)。然后调用fork
    • 在父进程关闭fds[0](或fds[1]),并以只读(或只写)方式打开fds[1](或fds[0])。在子进程中关闭fds[1](或fds[0]),并以只写(或只读)方式打开fds[0](或fds[1])。打开的函数是fdopen。
    • 开始通信。结束后用close函数关闭剩下的fds。

  • 这里有一个技巧:可以利用管道来达成重定向stdin, stdout和stderr。注意到dup2这个API可以把一个file descriptor复制到另一个上。
  • 事实上,我们有一对更为简洁的函数popen/pclose来完成上面的一系列复杂的操作。popen有两个参数:
    • 第一个参数接受一个exec,子进程将执行这个exec
    • 第二个参数为”w”或者”r”,”w”表示父进程写子进程读,”r”则反之
    • 返回值为管道的一端,也就是一个file descriptor
    • pclose用来关闭popen返回的file descriptor

  • FIFO(First In First Out)文件事实上是一个有名字的管道,换句话说,他可以用来让“不相干”的程序互相通信。
    • 我们使用mkfifo函数来创建一个FIFO文件
    • 我们可以使用任何的低级I/O函数(open, write, read, close等)以及C库I/O函数(fopen, fprintf, fscanf, fclose等)来操作FIFO文件。

  • Linux的管道和Windows下的命名管道(Named Pipes)的区别
    • Windows的命名管道更像一个套接字(sockets),它可以通过网络让不同主机上的程序进行通信
    • Linux的管道允许有多个reader和writer,每个reader和writer进行读/写的最大容量为
      PIPE_BUF(4KB),如果有多个writer同时写,他们写的东西会被分为一个一个的chunk(每个4KB)并允许交错写。(例如进程A有两个
      chunk,A1,A2。进程B也有两个chunk,B1,B2。A和B同时写,则顺序可能为A1,B1,A2,B2)
      Windows的管道允许在同一个管道上有多个reader/writer对,他们之间读写的数据没有交叉。

[color="#0000ff"]5.5 套接字(Sockets)
  • 套接字的特点:
    • 它是双向通信的
    • 它是进程间通信的,包括其他机器上的进程

  • 套接字有三个参数:
    • 通讯类型(communication style)
      • 连接(connection)类型:保证所有的包按发送的顺序到达接受方。(类似于电话)如果包丢失或者抵达顺序错误,会自动重发。
      • datagram类型:所有包单独发送,可能会出现丢失或者晚发早到的现象。(类似于邮寄)

    • 命名空间(namespace):描述套接字的地址是如何表示的,例如本地就是文件名,internet上就是ip地址。
    • 协议(protocol):通讯协议,常用的有TCI/IP,AppleTalk等。

  • 相关的API(套接字也是通过file descriptor来表示的):
    • socket:创建一个socket
    • closes:销毁一个socket
    • connect:在两个socket之间创建一个连接。这个API通常由客户端调用。
    • bind:给服务器的一个套接字绑定一个地址,服务器端调用。
    • listen:让一个套接字开始侦听,准备接受请求,服务器端调用。
    • accept:接受一个连接请求,并且为该连接创建一个新的套接字,服务器端调用。

  • 服务器端的生命流程:
    • 创建一个connection类型的socket
    • 给该socket绑定一个地址
    • 调用listen来enable该socket(listen可以指定最多有多少个请求在等待队列中,如果等待队列满了,又有新的请求到达的时候,则该请求被拒绝)
    • 对于收到的连接请求调用accept来接受
    • 关闭socket

  • 本地socket(local socket)
    • 如果是同一台电脑上的两个进程需要通信的话,可以使用本地socket。这种情况下socket的地址是文件路径。注意进程必须对该路径拥有可写权限,否则无法建立连接
    • 完成之后使用unlink来关闭一个socket

               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/75758/showart_1772359.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP