logo设计公司介绍,中小型企业网站优化价格,网站会员推广功能,云南文山学院文章目录 1. 进程间通信的概念2. 进程间通信的7种方式2.1 管道/匿名管道(pipe)2.2 有名管道(FIFO)2.3 信号(Signal)2.4 消息(Message)队列2.5 共享内存(share memory)2.6 信号量(semaphore)2.7 套接字(socket) 1. 进程间通信的概念
每个进程各自有不同的用户地址空间#xff… 文章目录 1. 进程间通信的概念2. 进程间通信的7种方式2.1 管道/匿名管道(pipe)2.2 有名管道(FIFO)2.3 信号(Signal)2.4 消息(Message)队列2.5 共享内存(share memory)2.6 信号量(semaphore)2.7 套接字(socket) 1. 进程间通信的概念
每个进程各自有不同的用户地址空间任何一个进程的全局变量在另一个进程中都看不到所以进程之间要交换数据必须通过内核在内核中开辟一块缓冲区进程1把数据从用户空间拷到内核缓冲区进程2再从内核缓冲区把数据读走内核提供的这种机制称为进程间通信IPCInterProcess Communication
2. 进程间通信的7种方式
2.1 管道/匿名管道(pipe)
管道是一种半双工的通信机制只允许数据在一个方向上流动。当需要双向通信时需要建立两个独立的管道。它主要用于具有亲缘关系的进程之间进行通信例如父子进程或者兄弟进程。 管道在操作系统中单独构成一种独立的文件系统但与普通文件不同它只存在于内存中不属于任何文件系统。管道两端的进程将其视为一个文件通过写入和读取数据来进行通信。 数据在管道中的读取和写入是按照先进先出的原则进行的。一个进程向管道中写入的内容被另一个进程从管道的另一端读取。写入的内容被添加在管道缓冲区的末尾并且从缓冲区的头部逐个读取。
管道的实质
管道的实质是一个由内核管理的缓冲区进程通过管道以先进先出的方式进行数据传输。一端的进程按顺序将数据写入缓冲区而另一端的进程则按顺序读取数据。这个缓冲区可以被视为一个循环队列读写操作在缓冲区中自动增长无法随意改变。每个数据只能被读取一次一旦读取数据就从缓冲区中删除。当缓冲区为空或者已满时系统会根据一定的规则将对应的读取或写入进程置于等待队列中。当有新数据写入空缓冲区或者已满的缓冲区中的数据被读取时等待队列中的进程会被唤醒以便继续进行读写操作。
管道的局限主要体现在其特点上 只支持单向数据流管道只能支持单向数据传输即数据只能从一端流向另一端无法实现双向通信。 只能用于具有亲缘关系的进程之间管道通常只能在具有亲缘关系的进程之间进行通信例如父子进程或兄弟进程。 没有名字管道是匿名的没有名称标识因此无法在不相关的进程之间共享和使用。 管道的缓冲区是有限的管道的缓冲区大小是有限的通常在创建管道时会为其分配一个固定大小的内存空间。 管道传输的是无格式字节流管道所传输的数据是无格式的字节流读写进程需要事先约定好数据的格式和解析规则以确保正确的数据传输和解析。
2.2 有名管道(FIFO)
有名管道FIFO与匿名管道的主要区别在于有名管道具有路径名与之关联以文件形式存在于文件系统中。因此即使创建有名管道的进程不存在亲缘关系其他进程只要可以访问该路径就能够通过有名管道相互通信。有名管道严格遵循先进先出FIFO原则读操作总是从管道的起始处返回数据写操作则将数据添加到管道的末尾。需要注意的是有名管道不支持诸如lseek()等文件定位操作因为其数据流是线性的不能随意定位。有名管道的名字存储在文件系统中而其内容存放在内存中。
匿名管道和有名管道的主要区别和总结如下
管道特性 管道是一种特殊类型的文件在满足先入先出FIFO的原则下用于进程间的通信但不能进行定位读写。匿名管道 匿名管道是单向的只能在有亲缘关系的进程间通信。 在使用匿名管道时写入的进程必须确定读取的进程存在否则写入操作会阻塞反之读取操作也会阻塞。 如果管道中没有数据可读读取操作也会阻塞。 当管道的另一端断开时写入操作会自动退出。有名管道 有名管道以磁盘文件的形式存在可以实现本机任意两个进程之间的通信。 在打开有名管道时需要确保对方进程的存在否则会阻塞。 可以以读写O_RDWR模式打开有名管道即当前进程读取数据同时也可以向管道中写入数据这种情况不会发生阻塞。 这些管道在使用时需要注意阻塞的问题特别是在确定对方进程存在的情况下保证写入和读取操作的顺利进行避免出现阻塞的情况。
2.3 信号(Signal)
信号是 Linux 系统中用于进程间通信或操作的一种机制。信号可以在任何时候发送给某个进程而无需知道该进程的状态。如果目标进程当前未处于执行状态该信号将由内核保存直到该进程恢复执行并将信号传递给它为止。如果进程将某个信号设置为阻塞状态那么该信号的传递将被延迟直到取消阻塞后才会传递给进程。
信号可以用于各种目的如通知进程发生了某个事件、中断进程的执行、终止进程等。在 Linux 中每个信号都有一个唯一的编号以及一个对应的名称。一些常见的信号包括 SIGINT终端中断信号通常由 CtrlC 发送、SIGKILL用于强制终止进程、SIGTERM终止进程信号等。
进程可以通过调用系统调用 signal() 或 sigaction() 来注册对信号的处理程序从而在收到信号时执行特定的操作。信号处理程序可以是预定义的行为如终止进程也可以是用户定义的函数。
Linux系统中常用信号
1SIGHUP用户从终端注销所有已启动进程都将收到该进程。系统缺省状态下对该信号的处理是终止进程。 2SIGINT程序终止信号。程序运行过程中按CtrlC键将产生该信号。 3SIGQUIT程序退出信号。程序运行过程中按Ctrl\键将产生该信号。 4SIGBUS和SIGSEGV进程访问非法地址。 5SIGFPE运算中出现致命错误如除零操作、数据溢出等。 6SIGKILL用户终止进程执行信号。shell下执行kill -9发送该信号。 7SIGTERM结束进程信号。shell下执行kill 进程pid发送该信号。 8SIGALRM定时器信号。 9SIGCLD子进程退出信号。如果其父进程没有忽略该信号也没有处理该信号则子进程退出后将形成僵尸进程。
信号来源
信号是软件层次上对中断机制的一种模拟是一种异步通信方式信号可以在用户空间进程和内核之间直接交互内核可以利用信号来通知用户空间的进程发生了哪些系统事件信号事件主要有两个来源
硬件来源这些信号是由硬件事件触发的比如用户按下 CtrlC 退出程序时硬件会发送一个信号给操作系统通知它中断当前进程的执行。另一个例子是硬件异常比如无效的存储访问也会触发信号。软件终止这些信号是由软件事件触发的例如进程终止信号当一个进程执行完成或者发生错误时它会向操作系统发送一个信号来终止自己的执行。其他进程也可以通过调用系统函数 kill 来发送信号给目标进程从而触发相应的操作。另外软件异常也会产生信号比如除零操作或者数据溢出。
信号生命周期和处理流程 信号的产生某个进程产生信号并设置要传递给哪个进程通常是对应进程的 PID然后将信号传递给操作系统。 操作系统的处理操作系统根据接收进程对信号的设置来决定是否将信号发送给接收者。如果接收者阻塞了该信号且该信号是可以阻塞的操作系统会暂时保留该信号直到接收者解除对该信号的阻塞。如果接收者已经退出则丢弃该信号。如果接收者没有阻塞该信号操作系统将传递该信号给接收者。 接收进程的处理接收进程接收到信号后根据当前进程对信号设置的预处理方式暂时终止当前代码的执行并保护当前上下文主要包括临时寄存器数据、当前程序位置以及当前 CPU 的状态。然后执行与该信号相关的中断服务程序。执行完成后恢复到中断发生之前的位置。对于抢占式内核中断返回时可能会触发新的调度切换到其他进程的执行。
2.4 消息(Message)队列
消息队列是一种存放在内核中的消息链表每个消息队列都由唯一的消息队列标识符来表示。与管道不同的是消息队列存放在内核中只有在内核重启或者显式地删除一个消息队列时该消息队列才会被真正删除。
另一个与管道不同的地方是消息队列不需要等待消息到达的进程。在某个进程向队列写入消息之前并不需要另一个进程在该队列上等待消息的到达。这意味着消息队列可以实现进程间的异步通信发送方可以向消息队列发送消息而不必关心接收方是否正在等待消息。
消息队列的这些特性使其成为一种非常灵活和高效的进程间通信机制特别适用于需要解耦发送方和接收方的场景以及需要在消息到达时进行异步处理的情况。
消息队列特点总结
消息队列是一种消息链表结构具有特定的格式存放在内存中并由消息队列标识符标识。允许一个或多个进程向消息队列写入消息以及从消息队列读取消息。消息队列与管道类似通信数据都遵循先进先出的原则。消息队列可以实现消息的随机查询不一定要按照先进先出的顺序读取也可以根据消息的类型进行读取这是与管道(FIFO)相比的优势之一。消息队列克服了信号通信承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。目前主要有两种类型的消息队列POSIX消息队列和System V消息队列其中System V消息队列目前被广泛使用。System V消息队列随内核持续存在只有在内核重启或者手动删除时该消息队列才会被删除。
2.5 共享内存(share memory)
共享内存是一种最快的可用IPC形式它允许多个进程直接读写同一块内存空间是为了解决其他通信机制效率较低而设计的。
在多个进程间交换信息时内核会留出一块内存区域允许需要访问的进程将其映射到自己的私有地址空间。这样进程就可以直接读写这块内存而不需要进行数据的拷贝从而大大提高了效率。
由于多个进程共享同一段内存因此需要依靠某种同步机制例如信号量来实现进程间的同步和互斥。这样可以确保在多个进程同时访问共享内存时不会出现数据混乱或竞争条件的问题从而保证数据的一致性和正确性。
2.6 信号量(semaphore)
信号量是一个计数器用于控制多个进程对共享资源的访问其主要目的是实现进程间的同步。
使用信号量的基本流程如下
创建一个信号量调用者需要指定初始值通常为1或0用于控制共享资源的访问权限。等待信号量该操作会测试信号量的值如果其值小于等于0进程将被阻塞直到信号量的值大于0。这个操作也被称为P操作。发送信号量该操作将信号量的值加1以允许其他等待进程继续执行。这个操作也被称为V操作。为了确保信号量操作的原子性信号量通常在内核中实现。在Linux环境中有三种主要类型的信号量Posix信号量Portable Operating System Interface for Unix有名信号量使用Posix IPC命名标识以及基于内存的Posix信号量存储在共享内存区中。此外还有System V信号量它也常用于进程间或线程间的同步。
举例来说
两个进程可以共享一个二值信号量以控制对某个共享资源的互斥访问。两个进程可以使用一个Posix有名二值信号量来实现跨进程的同步。一个进程的两个线程可以共享基于内存的信号量用于线程间的同步。
信号量与普通整型变量的区别
操作方式 信号量只能通过两个标准原子操作进行访问等待操作wait或P操作和发送操作signal或V操作。这两个操作被称为PV原语。 普通整型变量可以在任何语句块中直接进行读取和修改没有限制。用途 信号量主要用于实现进程间的同步和互斥以及控制对共享资源的访问。 普通整型变量通常用于存储数据或计数而不用于实现进程间的同步或互斥。原子性 信号量的操作是原子性的即它们在执行时不会被中断。这确保了在多线程或多进程环境中对共享资源的正确访问。 普通整型变量的操作不是原子性的可能会在多线程或多进程环境中导致竞态条件或数据不一致性的问题。
信号量与互斥量之间的区别
互斥和同步的区别 互斥是指同一时间只允许一个线程对资源进行访问具有唯一性和排它性但不限制访问顺序。 同步是在互斥的基础上通过其他机制实现对资源的有序访问。值的类型 互斥量的值通常是二进制的只能为0或1表示锁的状态即资源是否被占用。 信号量的值可以是非负整数用来表示某个资源的可用数量或者是某个操作的计数器。适用范围 互斥量主要用于实现对单个资源的互斥访问即同一时间只允许一个线程访问该资源。 信号量可以用于实现对多个资源的互斥访问以及多线程之间的同步。它可以控制对多个同类资源的访问或者实现多个线程之间的协作。加锁和解锁 对于互斥量加锁和解锁必须由同一个线程来匹配使用即同一个线程负责对资源的加锁和解锁。 信号量的操作没有这种限制一个线程可以对信号量进行等待操作减少而另一个线程可以对信号量进行释放操作增加它们不需要由同一个线程来匹配使用。
2.7 套接字(socket)
套接字是一种通信机制凭借这种机制客户/服务器即要进行通信的进程系统的开发工作既可以在本地单机上进行也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。
Socket是应用层和传输层之间的桥梁
套接字是支持TCP/IP的网络通信的基本操作单元可以看做是不同主机之间的进程进行双向通信的端点简单的说就是通信的两方的一种约定用套接字中的相关函数来完成通信过程。
套接字的特性由3个属性确定它们分别是域、端口号、协议类型。
套接字的域 套接字的域决定了通信中所使用的网络介质。最常见的套接字域是AF_INET用于Internet网络通信。另一个常见的域是AF_UNIX用于本地进程间通信它通过文件系统实现。套接字的端口号 端口号是套接字的标识符用于区分主机上的不同程序进程。端口号是一个16位无符号整数范围是0-65535。低于1024的端口号通常是系统保留的用于一些标准服务如HTTP的80端口。套接字的协议类型 套接字可以基于不同的协议类型进行通信。常见的协议类型有 流套接字SOCK_STREAM通过TCP/IP连接实现的套接字提供可靠的、有序的、双向的字节流传输适用于需要可靠性和顺序性的应用。 数据报套接字SOCK_DGRAM通过UDP/IP协议实现的套接字不需要建立连接适用于需要快速传输但可以容忍丢失的应用。 原始套接字SOCK_RAW允许对较低层次的协议直接访问适用于需要对网络数据包进行底层操作和控制的应用如网络监听和协议测试。
原始套接字与标准套接字的区别在于
原始套接字 原始套接字允许应用程序直接读写网络层IP和传输层TCP、UDP数据包而不经过操作系统的处理。它可以用于访问和操作网络层和传输层协议的数据包包括发送和接收特定协议的数据包。因此它提供了更高级别的网络控制和自定义协议实现的能力。标准套接字 标准套接字流套接字和数据报套接字只能读取特定协议TCP 或 UDP的数据包。流套接字SOCK_STREAM提供面向连接的、可靠的、基于字节流的通信适用于需要可靠性和顺序性的应用如HTTP、FTP。数据报套接字SOCK_DGRAM提供无连接的、不可靠的、基于数据包的通信适用于需要快速传输但可以容忍丢失的应用如DNS。
套接字通信的建立
Socket通信基本流程 服务器端
使用 socket() 系统调用创建一个套接字获得一个套接字描述符该套接字描述符用于后续通信。使用 bind() 系统调用将套接字与服务器的地址和端口号绑定。使用 listen() 系统调用监听连接请求设置连接队列的最大长度。使用 accept() 系统调用接受客户端的连接请求并创建一个新的套接字用于与客户端通信同时返回一个新的套接字描述符。
客户端
使用 socket() 系统调用创建一个套接字获得一个套接字描述符。使用 connect() 系统调用连接到服务器的地址和端口号。连接建立后就可以像使用文件描述符一样使用套接字进行双向通信发送和接收数据。