九八智能建站,网站制作佛山,代理注册公司一般多少钱,设置网关欢迎来到Cefler的博客#x1f601; #x1f54c;博客主页#xff1a;折纸花满衣 #x1f3e0;个人专栏#xff1a;题目解析 #x1f30e;推荐文章#xff1a;【LeetCode】winter vacation training 目录 #x1f4cb;进程通信的目的#x1f4cb;管道匿名管道pipe函数创… 欢迎来到Cefler的博客 博客主页折纸花满衣 个人专栏题目解析 推荐文章【LeetCode】winter vacation training 目录 进程通信的目的管道匿名管道pipe函数创建匿名管道 管道的5种特性4种情况站在文件描述符的角度看管道命名管道命名管道和匿名管道的区别 命名管道实现两个毫不相干进程间的读写联系Makefilecommon.hserver.cc(读)client.cc(写) 进程通信的目的
数据传输一个进程需要将它的数据发送给另一个进程资源共享多个进程之间共享同样的资源。通知事件一个进程需要向另一个或一组进程发送消息通知它它们发生了某种事件如进程终止时要通知父进程。进程控制有些进程希望完全控制另一个进程的执行如Debug进程此时控制进程希望能够拦截另一个进程的所有陷入和异常并能够及时知道它的状态改变。
管道
进程通信的管道底层原理是使用操作系统提供的文件描述符来实现。
在Linux中管道是通过内核中的缓冲区来实现进程间数据传输的。管道可以被看作是一个字节流它有两个文件描述符**一个用于读取数据一个用于写入数据。**这两个文件描述符分别被称为管道的读端和写端。当一个进程往管道的写端写入数据时数据会被放入管道的缓冲区中而另一个进程从管道的读端读取数据时数据会从缓冲区中被取出。
具体来说管道的底层原理如下 创建管道当调用pipe()函数时操作系统会创建一个管道并返回两个文件描述符一个用于读取数据一个用于写入数据。 数据传输一个进程可以通过write()系统调用将数据写入管道的写端数据会被放入管道的缓冲区中。另一个进程可以通过read()系统调用从管道的读端读取数据数据会从缓冲区中被取出。读写操作是原子的即每次写入或读取的数据大小是固定的。 阻塞与非阻塞管道的写端和读端都可以设置为阻塞或非阻塞模式。在阻塞模式下如果写端或读端没有准备好缓冲区已满或为空相应的写入或读取操作会被阻塞直到准备好为止
匿名管道
当谈到 Linux 的匿名管道时我们指的是一种特殊的进程间通信机制。它允许一个进程将输出直接发送给另一个进程而无需使用临时文件或其他形式的中间存储。
匿名管道使用竖线符号|来表示通过将一个进程的标准输出连接到另一个进程的标准输入来实现数据传输。这种连接使得第一个进程的输出变为第二个进程的输入实现了进程间的数据流动。
在 Linux 中匿名管道是通过 pipe 系统调用创建的。它返回两个文件描述符一个用于读取数据另一个用于写入数据。这两个文件描述符可以用于在相关进程之间传输数据。
以下是一个简单的示例来说明匿名管道的使用
$ ls | grep txt在这个示例中ls 命令列出当前目录下的所有文件并将输出通过匿名管道传递给 grep 命令。grep 命令会过滤出包含 “txt” 的文件。
匿名管道对于在 Linux 上进行进程间通信非常有用。然而它也有一些限制比如只能实现单向通信只能用于有亲缘关系的进程例如父子进程或兄弟进程并且在数据量较大时可能会引起阻塞。
pipe函数创建匿名管道
pipe 函数是一个系统调用用于在 Linux 系统中创建一个匿名管道。
它的函数原型如下
#include unistd.hint pipe(int pipefd[2]);pipe 函数接受一个整型数组 pipefd 作为参数该数组包含两个文件描述符。pipefd[0] 是管道的读取端pipefd[1] 是管道的写入端。
当成功调用 pipe 函数时它会创建一个无名管道并将对应的文件描述符存储在 pipefd 数组中。这样我们就可以使用这两个文件描述符来实现进程间的通信。
以下是一个简单的示例展示了如何使用 pipe 函数创建管道并进行进程间通信
#include unistd.h
#include stdio.h
#include string.hint main() {int pipefd[2];char buffer[20];// 创建管道if (pipe(pipefd) -1) {perror(pipe);return 1;}// 创建子进程pid_t pid fork();if (pid 0) {perror(fork);return 1;}if (pid 0) {// 子进程从管道中读取数据close(pipefd[1]); // 关闭写入端read(pipefd[0], buffer, sizeof(buffer));printf(子进程收到消息%s\n, buffer);close(pipefd[0]); // 关闭读取端} else {// 父进程向管道中写入数据close(pipefd[0]); // 关闭读取端const char* message Hello, child!;write(pipefd[1], message, strlen(message) 1);close(pipefd[1]); // 关闭写入端}return 0;
}在这个示例中父进程创建了一个管道并通过 write 函数向管道中写入消息。子进程通过 read 函数从管道中读取消息并输出到控制台。
需要注意的是为了正确使用管道我们需要在适当的时候关闭文件描述符。父进程关闭了读取端子进程关闭了写入端。
以上示例代码可以分为三个步骤 1.建立管道 2.创建子进程 3.父子关闭不需要的fd,形成单向通信的管道
管道的5种特性4种情况 管道的4种情况 1.如果管道没有数据了读端就只能等待 2.如果管道被写满了写端必须等待直到有空间为止 3.写端关闭读端一直读取读端读到read返回值为0表示读到文件结尾 4.读端关闭写端一直写入OS会直接杀掉写端进程通过向目标进程发送SIGPIPE13信号终止目标进程 管道的5钟特性 1.只能用于具有共同祖先的进程具有亲缘关系的进程之间进行通信通常一个管道由一个进程创 建然后该进程调用fork此后父、子进程之间就可应用该管道 2.匿名管道默认要给读写端提供同步机制 3.匿名管道是面向字节流的假如写了n量的数据不一定要全部读入根据自己的设置需求想怎么读就怎么读 4.管道的生命周期是随进程的进程结束管道结束 5.管道是单向通信的半双工通信的一种特殊情况
站在文件描述符的角度看管道
父进程中两个文件描述符分别指向读和写子进程继承父进程的文件描述符表。 父进程断开写的连接子进程断开读的连接最后形成子写父读。
命名管道
命名管道Named Pipe也被称为 FIFOFirst In First Out是一种在 Linux 系统中用于进程间通信的机制。
与匿名管道不同的是命名管道是通过文件系统中的一个特殊文件来实现的。它具有一个在文件系统中唯一标识的名称并且可以由多个进程进行读写操作。
要创建一个命名管道我们可以使用 mkfifo 命令或者在 C 语言中使用 mkfifo 函数。下面是一个示例演示了如何使用命名管道进行进程间通信
命令行示例
首先在命令行中创建一个命名管道
$ mkfifo mypipe然后在一个终端中执行以下命令将消息写入命名管道
$ echo Hello, named pipe! mypipe最后在另一个终端中执行以下命令从命名管道中读取消息
$ cat mypipe你将看到第二个终端输出了刚才写入的消息。
C 语言示例
以下是在 C 语言中使用命名管道进行进程间通信的示例
#include stdio.h
#include stdlib.h
#include fcntl.h
#include sys/stat.h
#include sys/types.h
#include unistd.hint main() {const char* fifo_file mypipe;const char* message Hello, named pipe!;char buffer[256];// 创建命名管道mkfifo(fifo_file, 0666);// 打开命名管道进行写操作int fd open(fifo_file, O_WRONLY);write(fd, message, strlen(message) 1);close(fd);// 打开命名管道进行读取操作fd open(fifo_file, O_RDONLY);read(fd, buffer, sizeof(buffer));printf(Received message: %s\n, buffer);close(fd);// 删除命名管道unlink(fifo_file);return 0;
}在这个示例中我们首先使用 mkfifo 函数创建了一个命名管道并指定了文件权限。然后我们使用 open 函数打开命名管道进行写操作并通过 write 函数向管道中写入消息。接着我们再次使用 open 函数打开命名管道进行读取操作并通过 read 函数读取管道中的消息。
需要注意的是命名管道是阻塞的如果没有进程同时打开读取端和写入端那么写入端的进程将会被阻塞直到有其他进程打开读取端。 命名管道和匿名管道的区别
命名管道Named Pipe和匿名管道Anonymous Pipe是两种不同的进程间通信机制它们有以下几个区别 创建方式匿名管道通过 pipe 函数创建而命名管道通过 mkfifo 函数或者命令行的 mkfifo 命令创建。 文件系统依赖性匿名管道不依赖于文件系统它只存在于内存中没有对应的文件。命名管道则是在文件系统中创建了一个特殊文件通过该文件进行读写操作。 进程间关系匿名管道通常用于父子进程之间的通信因为它们共享同一个进程空间。而命名管道可以用于任意进程之间的通信只要它们可以访问到同一个命名管道文件。 生命周期匿名管道在创建它的进程结束后自动销毁无法被其他进程继续使用。而命名管道会一直存在于文件系统中直到被显式地删除。 阻塞特性匿名管道是阻塞的如果没有进程同时打开读取端和写入端写入端的进程将会被阻塞。命名管道也是阻塞的但可以使用非阻塞方式打开以避免阻塞。 容量限制匿名管道的容量是有限的通常是几千字节。命名管道的容量取决于文件系统一般比匿名管道大得多。
命名管道实现两个毫不相干进程间的读写联系
Makefile
.PHONY:all
all:server client #依赖关系生成全部client:client.ccg -o $ $^ -stdc11
server:server.ccg -o $ $^ -stdc11
.PHONY:clean
clean:rm -f server client .fifocommon.h
#pragma once#define FILENAME .fifo
server.cc(读)
#includeiostream
#includecstring
#includecerrno
#include sys/types.h
#includesys/stat.h
#includefcntl.h
#includeunistd.h#include common.h
using namespace std;int main()
{int n mkfifo(FILENAME,0666);//创建命名管道if(n0){cerrerrno:errno,errstring:strerror(errno)endl;return 1;}coutmkfifo success...readendl;int rfd open(FILENAME,O_RDONLY);if(rfd0){cerrerrno:errno,errstring:strerror(errno)endl;return 2;}char buffer[1024];while(true){ssize_t s read(rfd,buffer,sizeof(buffer)-1);//读端if(s0){buffer[s] 0;//将最后一位置为反斜杠coutClient say# bufferendl;}else if(s0){coutclient quit,server quit tooendl;break;}}close(rfd);coutclose fifo success...endl;return 0;
}client.cc(写)
#includeiostream
#includecstring
#includecerrno
#include sys/types.h
#includesys/stat.h
#includefcntl.h
#includeunistd.h#include common.h
using namespace std;int main()
{int wfd open(FILENAME,O_WRONLY);if(wfd0){cerrerrno:errno,errstring:strerror(errno)endl;return 1;}coutopen fifo success...writeendl;string message;while(true){coutPlease Enter# ;getline(cin,message);ssize_t s write(wfd,message.c_str(),message.size());//写端if(s0){cerrerrno:errno,errstring:strerror(errno)endl;break;}}close(wfd);return 0;
}