进程间的通信:管道 信号量 消息队列 socket套接字
管道:线性字节数组,类似文件,使用文件读写的方式进行访问
管道分为:
有名管道:fifio 相当于创建了一个文件,这个文件只要有权限的,所有人都能来访问(不相关的进程也能进行数据的交换)
无名管道:pipe 只能由父子进程进行使用,返回两个文件描述符
创建管道时系统调用popen()或者pipe(),popen需要提供一个目标进程作为参数,然后再调用该函数的进程和目标进程之间创建一个管道,是有名管道。
管道和记名管道不是所有操作系统都支持,主要支持管道通信方式的是UNIX和类UNIX(如Linux的操作系统)
同一主机,一个管道可以被多个进程打开,多个管道之间互不影响,服务器端可以创建一个管道文件,然后,客户端的进程可以通过open;来打开服务器的管道文件。
管道的一个特点是,使用管道的两个线程之间必须存在某种关系,例如:使用popen()需要提供另一端进程的文件名,使用pipe()两个进程必须隶属于父子进程。
信号量:用来在进程的通信时表示现在进行到那个状态了,而使另外一个进程进行下面的操作。
主要应用在同步机制中。
竞争:两个或多个线程争相执行同一段代码或访问同一资源的现象。
临界区:共享代码段或者资源的区域成为临界区。
原子操作如何实现:
P操作: V操作
1)等待信号量取值变为1 1)将信号量的值设置为1
2)将信号量的值设置位0 2)叫醒在该信号量上面等待的
3)继续往下执行 3)线程继续往下执行
互斥锁:为了保证不同时访问一个资源,使临界区的代码不同时被执行
加锁是对临界区加锁。一个加锁后,其他的在临界区要进行等待。
为什么要进行加锁:多线程会共享资源,为了保证资源能够互斥访问,就必须给资源加锁,拥有该资源锁的线程才能访问该资源,每隔对象都有自己的锁。
开锁的操作:打开锁
闭锁的操作有两个步骤:1.等待锁达到打开状态。2.获得锁并锁上。(闭锁的两个操作应该是原子操作,即不能再分开)
锁的特性:锁的初始状态要是打开状态,进入临界区获得锁,出临界区开锁,等待其他的线程打开锁
生产者与消费者模型:
sleep和wakeup就是操作系统里的睡觉和叫醒操作原语,一个程序调用sleep后将进入休眠状态,其所占用的cpu将被释放,一个执行wakeup的程序将发送一个信号给指定的接受进程,如wakeup(producer)就是发送一个信号给生产者。
模拟生产者和消费者模型:一个进程代表生产者,一个进程代表消费者,一片内存缓冲区代表商店,生产者生产的商品从一端放入缓冲区,消费者从另外一端获取物品。
上述操作对count没有进行保护,可能发生数据的竞争,即生产者和消费者同时对数据进行修改也就是死锁状态。
造成死锁的条件:因为系统资源不足 进程推进的顺序不合适 资源分配不当
信号量:特殊的变量,可以进行原子减1(P操作,代表获取资源,V操作,代表释放资源)(信号量可用来解决生产者和消费者的同步问题)
先设置3个信号量:
mutex:一个二元信号量,用来防止两个线程同时对缓冲区进行操作 初始值位1
full:记录缓冲区里商品的件数 初始值为0
empty:记录缓冲区里空置空间的数量 初始值为N(缓冲区的大小)
改进之后的生产者消费者程序:
消息队列:一列具有头和尾的消息排列,新来的消息放在队列尾部,而读取消息则从队列头部开始。
记名管道的一段是固定的进程。消息队列的读写都是不固定的。可以支持多进程的读写,管道支持的是一点对点。消息队列只在内存中出现。
共享内存:两个进程共同拥有一片内存,这片内存中的任何内容,二者均可以进行访问
使用全局变量在同一个进程的线程间实现通信的不是共享内存,是信号量。
要使共享内存进行通信,一个进程首先创建一片内存空间专门作为通信用,而其他进程则将该片内存映射到自己的虚拟地址空间,这样,读写自己地址空间中对应共享内存的区域时,就是在和其他进程进行通信。
共享内存的要求是他不是一个文件,在内存中,而记名管道是一个文件,在磁盘上。再次,共享内存要求多个进程将共享的那一段内存映射到自己的虚拟空间中。其次,共享内存的访问时随机的。不再是一段读一端写了。
socket套接字:
通信双方都要建立一个套接字,服务器的套接字的端口是固定的,以方便客户端进行连接。这需要绑定操作。然后执行监听。客户端链接上后,服务器段需要再生成一个新的客户端套接字与其进行通信。从而建立了一个通信信道。
必须先建立连接后,才能通信,相当于打电话。建立连接非常耗时,所以小的通信量使用信号来处理。信号就是进程在收到一个信号的时候,立即作出回应。
服务器套接字既不发送数据,也不接收数据(指不接受正常的用户数据,而不是链接请求数据)
其他通信机制:
1.更好的共享数据,传递数据。
2.多个线程进行通信,使资源的安全性得到保证,因为线程是进程的一段执行序列,所以线程之间的通信是必须的。
3.消息队列和管道的结构是类似的,但消息队列一般只能在内存中,而管道可以在文件中,管道在创建的时候是给出一个目标进程。而消息队列可以让任何程序进行访问。
4.是一个线性表
5.相同点是都需要建立一个通信信道,都可以支持不同主机的通信。但是管道不能代替套接字,服务器的套接字只是用来监听是否有连接产生,处理的客户端套接字用来处理连接。
6.应该是套接字。
7.管道,记名管道和套接字都必须进行通信信道 的建立。一般建立了信道就要多的进行数据传递。信号量和信号指示少量的数据进行传递。共享内存是为了尽可能多的共享数据。消息队列和管道类似,但是不需要指定目标进程。
参考《从哲学层次看操作系统》