进程间通信的难点:
-独立的进程地址空间
-异步工作
常用的进程间通信机制
- 管道(Pipe)及有名管道(name pipe):进程间建立字节流队列。
- 消息队列(Message Queue):进程间建立消息队列
- 信号(Signal):信号是在软件层次上对中断机制的一种模拟。一个进程可以向另一个进程发送信号,就像外设触发了CPU的中断。
- 共享内存(Shared memory):将同一块物理内存映射到两个不同进程的地址空间中
- 信号量(Semaphore):主要是作为进程之间的同步和互斥手段
-套接字(Socket):这是一种更为一般的进程间通信机制,它可用于网络中不同机器上的两个进城之间的进程间通信,应用非常广泛。
管道主要包括两种:无名管道和有名管道
-无名管道特点
*只能用于具有亲缘关系的进城之间的通信(父子进程或者兄弟进程)
*半双工的通信模式,具有固定的读端和写端
*管道也可以看成是一种内存中的特殊文件,对于他的读写也可以使用普通的read()、write()等函数
-有名管道特点
*他可以使互不相关的两个进程实现彼此通信
*对应一个实际的文件,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样
进行读写操作,使用非常方便。
*严格地遵守先进先出的规则,对管道的度总之从开始处返回数据,对他们的写则是把数据添加到末尾,因此不支持如lseek()等文件定位操作。
关于无名管道的系统调用:
-管道创建和关闭
*管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fds[0]和fds[1],其中fds[0]
固定用于读管道,而fd[1]固定用于写管道,这样就构成可一个半双工的通道。
*管道关闭时只需将这两个文件描述符关闭即可,可使用普通的close()函数逐个关闭各个文件描述符。
-无名管道读写注意点:
只有在管道的读端存在是,向管道写入数据才有意义。否则,向管道写入数据的进程将收到内核传来的SIGPIPE信号
向管道写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。
如果读进程不读取管道缓冲区中的数据,那么些操作将会一直阻塞。
如果管道缓冲区是空,那么读进程将会一直阻塞
有名管道(FIFO)
-有名管道可以实现任意连个进程之间的通信,并不限制两个进程间有亲缘关系
-有名管道作为一种特殊的文件存放在文件系统中,使用完仍然存在,除非对其进行删除
-有名管道也只能用于单向传输
有名管道
-FIFO读写
对于读进程
-若该管道是阻塞打开,且当前FIFO内没有数据,则读进程而言将一直阻塞到有数据写入
-若该管道是非阻塞打开,则不论FIFO内是否有数据,读进程会立即执行读操作,即如果FIFO内没有数据,读函数都会立刻返回0
对于写进程
-若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入
-若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入后者调用失败。
Linux信号机制是在应用软件层次对中断机制的一种模拟,是一种异步通信方式
每个进程都有一个自己私有的信号处理函数映射表,当该进程收到一个信号时,对应的信号处理函数被触发执行
一个进程可以向另一个进程发送信号,也可以向自己发送信号,咋偶哦系统内核页可以向一个进程发送信号,以
通知某些硬件事件。
信号处理函数映射表共有64个表项。前32个信号,编号为0-31,有预定义的含义和处理函数;后32个作为扩充。
前32个是不可靠信号,后32个为可靠信号。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号
丢失,而后者不会。
信号发送:kill() raise()
kill系统命令是kill()函数一个用户接口,它不仅可以终止进程(实际上发出SIGKILL信号),也可以向进程发送其他信号。
raise()函数允许进程向其自身发送信号
信号捕捉:alarm() pause()
alerm()也称为闹钟函数,他可以在进程中设置一个定时器,当定时器指定的时间到时,他就可以向进程发送SIGALARM信号。
pause()函数是用于将调用进程挂起直至捕捉到信号为止(该信号没有被阻塞)。
信号的处理:signal、sigaction
信号量
-信号量是用来共享资源的种进程访问机制,是的共享资源在一个时刻只有一个
-信号量对应于某一种资源,取一个非负的整型值。信号量指的是当前可用的该资源的数量,若他等于0则意味着者目前没有可用的资源。
-针对信号量的操作:
P操作:如果有可用的资源(信号量值>0),则占用一个资源(给信号量值减去-,进入临界区代码),如果没有可用资源(信号量等于0),
则进入到该信号量的等待队列中并阻塞休眠,直到系统将其唤醒。
V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程。如果没有进程等待它,则释放一个资源。
使用信号量的步骤:
第一步:创建信号量或获得系统已存在的信号量,此时需要调用semget()函数。不同进程通过使用同一个信号量
键值来获取同一个信号量。
第二步:初始化信号量,此时使用semctl()函数的SETVAL操作。当使用二维信号量时,通常将信号量初始化为1
第三步:进行信号量的PV操作,此时调用semop()函数。这一步是实现进程之间的同步和互斥的核心工作部分
第四步:如果不需要信号量,则从系统中删除它,此时使用semctl函数的IPC_RMD操作。此时需要注意,在程序中
不应该出现对已经被删除的信
共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
*为了在多个进程间交换信息,内核专门留出了一块内存区,可以有需要访问的进程将其映射到自己的私有地址空间
*进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率。
*由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。
实现共享内存的步骤
-创建共享内存,这里用到的函数是shmget,也就是从内存中获得一段共享内存区域
-映射到共享内存,也就是把这段创建的共享内存映射到具体的进程空间中去,这里使用的函数是shmst
-到这里,就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作
-撤销映射的操作,其函数为shmdt
-删除共享内存使用shmctl函数。
消息队列
消息队列就是一些消息的列表。用户可以在消息队列中添加消息和读取消息。消息队列具有一定的FIFO特性,但
是它可以实现消息的随机查询,比FIFO具有更大的优势。同时这些消息又是存在于内核中的,由"队列ID"来表示。
消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列这4种操作。
创建会打开消息队列使用的函数是msgget,这里创建的消息队列的数量谁受到系统消息队列数量的限制。
添加消息使用的函数是msgsnd函数,他爸消息添加到已打开的消息队列末尾
读取消息使用的函数是msgrcv,他把消息从消息队列中取走,与FIFO不同的是,这里可以指定取走某一条消息
控制消息队列使用的函数是msgctl,塔尔可以完成多项功能。