进程间通信

select 同时监视多个文件描述符。在指定情况发生的时候,函数返回。详细一点说,
select 监昕在三组文件描述符上发生的事件:检查第一组是否可以读取,检查第二组是否可以写入,检查第三组是否有异常发生。每一组的文件描述符被记录到一个二进制位的数组中。这里的 numfds 恰好等于需要监昕的最大的文件描述符加 1 。
使用文件的IPC:
访问控制:客户端必须能够读取文件。通过使用标准文件访问权限,可以给予服务器写权限并且限制客户端只有读权限。
多客户端:任意数目的客户可以同时从文件中读取数据。 Unix 并不限制同时打开同一文件的进程数目。
竞态条件:服务器通过清空内容再重写的方法来更新文件 。 如果某客户恰好在清空和重写之间读取文件,那么它得到的将是一个空的或只有部分的内容。
避免竞态条件 : 服务器和客户端可以使用某种类型的互斥量来避免竞态条件。如果服务器在程序中使用 lseek 和 wnte 函数来
替换 create ,这样文件永远都不可能为空,因为 write 是一个原子操作,它不会在执行中被打断 。
命名管道:
通常的管道只能连接相关的进程。常规管道由进程创建,并由最后一个进程关闭
使用命名管道可以连接不相关的进程,并且可以独立于进程存在。 称这样的命名管道为 FIFO( 先进先出队列)。
1、使用FIFO
(1)创建FIFO
库函数 mkfifo( char * name , mode_t mode) 使用指定的权限模式来创建 FIFO 。mkfifo命令通常调用这个函数。
(2)删除FIFO
类似于删除文件, unlink (fifoname) 函数可以用来删除 FIFO 。
(3)监听FOFO
使用 open(fifoname , O_RDONLY) 函数。 open 函数阻塞进程直到某一进程打开 FIFO进行写操作。
(4)通过FIFO会话
使用 open(fifoname , O_ WRONLY) 函数。此时 open 函数阻塞进程直到某一进程打开FIFO 进行读取操作。
(5)进程通过FIFO通信
发送进程用 write 调用,而监昕进程使用 read 调用。写进程调用 close 来通知读进程通信结束。
FIFO:总结
访问: FIFO 使用与通常文件相同的文件访问。服务器有写权限,而客户端只限于读权限。
多个客户端:命名管道是一个队列而不是常规文件。写者将字节写入队列,而读者从队列头部移出字节。
竞态条件:在信息的长
度不超过管道的容量的情况下, read 和 write 系统调用只是原子操作。 读取操作将管道清空而写入操作又将管道塞满。在读者和写者连通之前,系统内核将进程挂起。
共享内存:
同一个系统里的两个进程通过使用共享的内存段来交换数据 。 共享的内存段是用
户内存的一部分。每一个进程都有一个指向 此内存段的指针,依靠访问权
限的设置,所有进程都可以读取这一块空间中的数据 。 因此进程间的资源是共亭的,而不是被复制来复制去的 。 共享内存段对于进程而言,就类似于共享变量对于线程一样 。
1.共享内存段的一些基本概念
· 共享内存段在内存中不依赖于进程的存在而存在。
. 共享内存段有自己的名字,称为关键字 (key) 。
· 关键字是一个整型数 。
· 共享内存段有自己的拥有者以及权限位。
· 进程可以连接到某共享内存段,并且获得指向此段的指针 。
2、使用共享内存段
(1)找到共享内存

in t seg_id=shmget(key , size-of-segment , flags)

如果内存段存在,函数 shmget 找到它的位置 。 如果不存在,可以通过在 flags 值中指定一 个创建此段和初始化权限模式的请求 。
(2)连接到共享内存

void ptr=* shmat(seg_id ,NULL , flags)

shmat在进程的地址空间中创建共享内存段的部分,并返回一个指向此段的指针, flags参数用来指定此内存段是否为只读。
(3)与共享内存段进行读写交互
strcpy( ptr , "hello ") ;memcpy() 、 ptr [ i] 及其他一些通用的指针操作。
(4)与共享内存分离

int shmdt(const void *shmaddr);
从调用进程的地址空间中分离位于shmaddr指定地址的共享内存段。 待分离的段必须当前使用与附加的shmat()调用返回的值相等的shmaddr。

共享内存段类型的 IPC 小结:
a.访问:客户端必须有对共享内存段的读权限。共享内存段拥有一个权限系统,它的工作原理和文件权限系统类似。共享内存段有自己的拥有者并且为用户、组或其他成员设置了权限位,来控制他们各自的访问权限。正因为有如此特性,才可以让服务器只有写权限而客户端只有读权限。

b.多个客户:任意数目的客户都可以同时从共享内存段读数据。

c.竞争条件:服务器通过调用一个运行在用户空间的库函数 strcpy 来更新共享的内存段。如果客户端正好在服务器向内存段中写入新数据的时候来访问内存段,那么它可能既读到新数据也读到老数据。

d.避免竞态条件:服务器和客户段必须使用相同的系统来对资源加锁。内核提供了一种进程间加锁的机制,称为信号量机制。

各种进程间通信方法的比较:
(1)速度
通过文件或命名管道来传输数据需要更多的操作,系统内核将数据复制到内核空间中,然后再切换回用户空间。对于利用文件进行传输来说,内核将数据复制到磁盘上,然后将数据再从磁盘上复制出去。实际上,在存储器中存储数据比想象中要复杂的多。虚拟内存系统允许用户空间中的段交换到磁盘上,因此就是共享内存段机制同样也包括了对磁盘的读写操作。
(2) 连接和无连接
文件和共享内存段就像公告牌一样。数据产生者将信息贴在公告牌上,多个消费者可以同时从公告牌上阅读信息。 FIFO 要求建立连接,因为在内核转换数据之前,读者和写者都必须等待着 FIFO 被打开,并且也只有一个客户可以阅读此消息。流 socket 是面向连接的,而数据报 socket 则不是
(3) 范围
共享内存和命名管道只允许本机上的进程之间通信。通过文件进行传输可以允许不同机器上的进程进行通信。使用 IP 地址的 socket可以与不同机器上的进程进行通信,而使用 Unix 地址的 socket 却不能。而使用 Unix 地址的 socket 却不能.
(4) 访问限制
文件、 FIFO 、共享内存以及 Unix 地址 socket 都提供标准的 Unix 文件系统权限。而 Internet socket 则不行。
(5) 竞态条件:
使用共享内存和共享文件要比使用管道和 socket 麻烦。管道和 socket 是由内核来管理的队列。写者将数据放进一端,而读者则从另一端将数据读出,进程并不需要考虑其内部结构。然而对于共享文件和共享内存的访问却不是由内核进行管理的。如果某进程在读文件的过程中,另一个进程正在对文件进行重写,读进程读到的很可能就是不完整或不一致的数据。
进程之间的分工合作
文件锁
1.两种类型的锁
(1)、使用文件锁进行编程
Unix 提供了 3 种方法锁住打开的文件: flock 、 lockf 和 fcntl 。三者中最灵活和移植性最好的应该是 fcntl 。
a.给已经打开的文件加读数据锁,使用 fcntI(fd , F_SETLKW , &lockinfo),第一个参数是该文件对应的文件描述符。第二个参数 F_SETLKW 说明若必要的话,可
以等待其他的进程释放锁。第三个参数指向一个 struct flock 类型的变量。
b.在打开的文件上加写数据锁
使用 fcntl(fd , F _SETLKW , &lockinfo) ,并将lockinfo.l_typeF_WRLCK .
(3)解锁
使用 fcntl(fd , F _SETLKW , &lockinfo) ,并将lockinfo.l_typeF_UNLCK
(4)只锁住文件的一部分
使用fcntl(fd , F _SETLKW , &lockinfo) ,并将lockinfo.l_start 置为开始位置的偏移量,同时将 `lockinfo. l_len 置为区域的长度。

文件锁:小结
使用 F_SETLKW 参数调用 fcntl 可以使进程挂起直到内核允许进程设置指定的锁。在读取数据之前,客户必须设置读取数据的锁。若服务器对文件加写数据锁,客户只好等待服务器完成。服务器在重写数据之前,也必须对文件加写数据锁,如果这时客户加了一个读数据的锁,那服务器会被挂起直到所有客户释放这个锁。
进程可以忽略锁机制
不管客户还是服务器在读或修改文件的时候,程序都是自觉有序地等待,设置及释放文件锁。但是当别的进程设置了锁的时候,其他进程可以忽略它。Unix 的锁机制允许进程通过这种方式合作,但并不强迫它们一定要用。
信号量( Semaphores)
信号量是一个内核变量,它可以被系统中的任何进程所访问。进程间可以使用这个变量来协调对于共享内存和其他资源的访问。类似于条件对象是进程中的全局变量,而信号量则是系统中的全局变量。
(1)创建信号量集

semset_id = semget(key_t key , int numsems, int flags)

semget函数创建了一个包含numsems个信号集的集合。函数 semget 返回此信号量集的 ID。
(2) 将所有的信号量置。

semct l( jnt semset id , int semnum , int cmd , union semun arg)

这里使用 semctl 来对信号量集进行控制。此函数的第一个参数是此集合的 ID ,第二个参数是集合中某特定信号量的号码,第三个参数是控制命令。如果此控制命令需要参数,那么使用第四个参数向其提供所需的参数。
(3) 等待所有读者完成任务之后,服务器将 num_ writers 加 1

semop( int semid , struct sembuf * actions , size t numactions)

函数 semop 对信号量集完成一组操作。第一个参数用来指定信号量集。第二个参数是一组活动的数组,最后一个参数则是该数组的大小。集合中的每一个活动都是一个结构体,它的作用就是”使用选项 sem_flg 来完成对号码为 sem num 的信号量的操作 sem_op” 。整个活动集合被作为组来完成。
(4) 对 num_ writers 减
(5) 删除信号量

semctl(semset_id , 0 , IPC_RMID , 0)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值