当前位置: 首页 > Linux

进程间通信方式(六种)

时间:2023-04-06 23:59:02 Linux

前提知识:每个进程都有自己的用户空间,内核空间是每个进程共享的。因此,如果要在进程之间进行通信,就需要通过内核来实现。管道:管道是最简单、效率最低的通信形式。管道本质上是内核中的缓存。当进程创建管道时,Linux会返回两个文件描述符,一个是写端描述符,一个是输出端描述符。这两个描述符可用于进入管道写入或读取数据。如果想让两个进程通过管道进行通信,就需要让创建管道的进程fork子进程,让子进程拥有父进程的文件描述符,这样子进程也可以访问相同的管道操作。缺点:半双工通信,一个管道只能一个进程写,一个进程读。一个进程完成写入后,另一个进程可以读取,反之亦然。消息队列:管道的通信方式效率低下,不适合进程间频繁的数据交换。这个问题可以通过消息队列的通信方式来解决。进程A向消息队列写入数据后可以正常返回,进程B可以在需要的时候读取,效率更高。而且,数据会被一个一个的划分成数据单元,称为消息体。消息发送者和接收者就消息体的数据类型达成一致。与管道的无格式字节流类型不同,这样做的好处是可以边接收边发送,无需等待完整的数据。但也有缺点。每个消息体都有最大长度限制,队列中包含的消息体的总长度也有限制。这是缺点之一。另一个缺点是在消息队列通信过程中存在用户态和内核态之间的数据拷贝问题。当一个进程向消息队列写入数据时,会发送从用户态到内核态拷贝数据的进程。同样,在读取数据时,它会将数据从内核态复制到用户态。共享内存:共享内存解决了消息队列中内核态和用户态之间的数据拷贝问题。现代操作系统采用虚拟内存技术进行内存管理,也就是说每个进程都有自己的虚拟内存空间,虚拟内存映射到真实的物理内存。共享内存的机制是不同进程取出一块虚拟内存空间,映射到同一个物理内存空间。这样一个进程写的东西可以立即被另一个进程看到而无需复制。(这里的物理内存好像不是内核空间的内存?)被其他进程覆盖。因此,需要一种保护机制。信号量本质上是一个整数计数器,用于实现进程间的互斥和同步。信号量表示资源的数量。操作信号量有两种方式:P操作:该操作将从信号量中减一。如果减法后信号量小于0,说明资源已经被占用,进程需要阻塞等待。;如果大于等于0,说明还有资源可用,进程可以正常执行。V操作:这个操作会给信号量加一。如果相加后信号量小于等于0,说明当前有进程阻塞,则该进程将被唤醒;如果大于0,说明当前没有阻塞进程。(1)信号量实现互斥:信号量初始化为1,进程A在访问共享内存前执行P操作。由于信号量的初始值为1,进程A执行完P操作后信号量变为0。表示共享资源可用,所以进程A可以访问共享内存。如果此时B进程也想访问共享内存,执行P操作,结果信号量变为-1,意味着临界资源已经被占用,所以B进程被阻塞。直到进程A访问完共享内存,才会执行V操作,让信号量归0,然后唤醒阻塞的线程B,让进程B访问共享内存,最后执行V操作将在对共享内存的访问完成后执行。,使信号量恢复到初始值1。(2)信号量的同步:由于多线程下各个线程的执行顺序是不可预测的,所以有时我们希望多个线程能够紧密配合。这时候就需要考虑线程的同步了。信号量初始化为0,如果进程B先于进程A执行,那么在执行P操作时,由于信号量的初始值为0,信号量会变成-1,表示进程A还没有产生数据,所以进程B然后,当进程A生产完数据后,执行V操作,这会使信号量变为0,所以它会唤醒阻塞在P操作中的进程B;最后,B进程被唤醒后,说明A进程已经产生了数据,所以B进程可以正常读取数据。信号:在Linux中,为了响应各种事件,提供了几十种信号,可以通过kill-l命令查看。如果是运行在shell终端上的进程,可以通过键盘组合键向进程发送信号,例如使用Ctrl+C产生SIGINT信号,表示进程终止。如果是后台运行的进程,可以通过命令向进程发送信号,例如使用kill-9PID产生SIGKILL信号,表示进程立即终止。Socket:前面提到的管道、消息队列、共享内存、信号量和信号,都是在同一主机上的进程之间进行通信。如果要在不同主机上跨网络和进程进行通信,就需要使用套接字。实际上,Socket不仅可以跨网络、跨主机的进程之间进行通信,还可以在同一主机上的进程之间进行通信。Socket是操作系统提供给程序员操作网络的接口。根据底层的实现方式不同,通信方式也不同。Socket系统调用:intsocket(intdomain,inttype,intprotocol)TCP的Socket通信:服务端和客户端初始化Socket,获取文件描述符服务端调用bind,绑定IP和端口服务端调用listen,监听服务端调用accept,等待客户端进行连接,客户端调用connect向服务器发起连接请求。(TCP三次握手)服务端调用accept返回用于传输的Socket的文件描述符(区别于第一点获取的Socket)。客户端使用write写入数据,服务端调用read读取数据。当客户端断开连接时会调用close,服务端也会调用close(TCP挥手四次)。这里需要注意的是,调用accept时,连接成功后获取的Socket用于传输数据,Socket的第一次初始化用于监听。两个不同功能的Socket。UDP的套接字通信: