📅  最后修改于: 2023-12-03 15:06:21.276000             🧑  作者: Mango
五级管道是指在Unix系统中用于输入和输出数据的一种数据传输方式。它可以将一个程序的输出作为另一个程序的输入,从而实现两个程序之间的数据传递。在本文中,我们将介绍五种不同类型的五级管道,它们分别是普通管道、有名管道、环形缓冲区、消息队列和套接字。
普通管道也称为匿名管道,是最常用的五级管道类型。它是一种单向的、半双工的数据传输方式,具有以下特点:
使用普通管道需要调用系统调用pipe()
函数,该函数在调用成功时会返回两个文件描述符,分别表示管道的读端和写端。具体使用方法可以参考以下代码片段:
int fd[2]; // 用于保存管道的读端和写端
char buf[1024]; // 用于保存从管道中读取的数据
int ret = pipe(fd); // 创建管道
if (ret == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
if (fork() == 0) {
// 子进程
close(fd[0]); // 关闭管道的读端
write(fd[1], "hello world", strlen("hello world")); // 向管道中写入数据
close(fd[1]); // 关闭管道的写端
exit(EXIT_SUCCESS);
} else {
// 父进程
close(fd[1]); // 关闭管道的写端
read(fd[0], buf, sizeof(buf)); // 从管道中读取数据
printf("received data from pipe: %s\n", buf);
close(fd[0]); // 关闭管道的读端
wait(NULL); // 等待子进程退出
}
有名管道也称为FIFO,是一种特殊的管道类型,具有以下特点:
使用有名管道需要调用系统调用mkfifo()
函数,在调用成功时会在文件系统中创建一个FIFO文件。打开FIFO文件时,需要使用标志位O_RDONLY
或O_WRONLY
或O_RDWR
,表示读、写或读写模式。具体使用方法可以参考以下代码片段:
const char* fifo_path = "/tmp/myfifo"; // 用于保存FIFO文件的路径
char buf[1024]; // 用于保存从FIFO中读取的数据
int ret = mkfifo(fifo_path, 0666); // 创建FIFO文件
if (ret == -1 && errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
int fd = open(fifo_path, O_RDONLY); // 打开FIFO文件,并以只读模式读取数据
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
read(fd, buf, sizeof(buf)); // 从FIFO中读取数据
printf("received data from fifo: %s\n", buf);
close(fd); // 关闭FIFO文件
环形缓冲区是一种基于共享内存的数据传输方式,它具有以下特点:
由于环形缓冲区不是标准的Unix五级管道类型,因此需要使用类似于POSIX共享内存的API来实现。具体使用方法可以参考以下代码片段:
typedef struct {
int head;
int tail;
char buf[1024];
} CircularBuffer;
int fd = shm_open("/myshm", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); // 创建共享内存
ftruncate(fd, sizeof(CircularBuffer)); // 设置共享内存大小
CircularBuffer* cb = (CircularBuffer*)mmap(NULL, sizeof(CircularBuffer), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); // 映射共享内存
close(fd); // 关闭共享内存文件引用
cb->head = 0;
cb->tail = 0;
strcpy(cb->buf, "hello world"); // 向环形缓冲区中写入数据
cb->head += strlen("hello world"); // 更新头指针
char buf[1024];
int len = cb->tail - cb->head; // 计算待读取数据的长度
memcpy(buf, cb->buf + cb->head, len); // 从环形缓冲区中读取数据
printf("received data from shared memory: %s\n", buf);
cb->tail += len; // 更新尾指针
munmap(cb, sizeof(CircularBuffer)); // 解除共享内存的映射
shm_unlink("/myshm"); // 删除共享内存
消息队列是一种基于消息的数据传输方式,它具有以下特点:
使用消息队列需要调用系统调用msgget()
函数来创建或打开消息队列,调用msgsnd()
函数向消息队列中写入数据,调用msgrcv()
函数从消息队列中读取数据,调用msgctl()
函数进行消息队列的控制操作。具体使用方法可以参考以下代码片段:
typedef struct {
long mtype; // 消息类型(必须大于0)
char mtext[1024]; // 消息内容(可以为任意类型的数据)
} Message;
key_t key = ftok("/tmp", 'A'); // 用于生成消息队列的key
int msqid = msgget(key, IPC_CREAT|IPC_EXCL|0666); // 创建消息队列
if (msqid == -1 && errno == EEXIST) {
msqid = msgget(key, 0666); // 打开已经存在的消息队列
}
if (msqid == -1) {
perror("msgget");
exit(EXIT_FAILURE);
}
Message msg;
msg.mtype = 1; // 消息类型必须大于0
strcpy(msg.mtext, "hello world"); // 设置消息内容
if (msgsnd(msqid, (void*)&msg, sizeof(Message)-sizeof(long), 0) == -1) { // 向消息队列中写入数据
perror("msgsnd");
exit(EXIT_FAILURE);
}
if (msgrcv(msqid, (void*)&msg, sizeof(Message)-sizeof(long), 1, 0) == -1) { // 从消息队列中读取数据
perror("msgrcv");
exit(EXIT_FAILURE);
}
printf("received data from message queue: %s\n", msg.mtext);
msgctl(msqid, IPC_RMID, NULL); // 删除消息队列
套接字是一种抽象的接口,用于将Unix五级管道拓展到网络通信中,它具有以下特点:
使用套接字需要调用系统调用socket()
函数创建Socket,调用bind()
函数将Socket绑定到指定的IP地址和端口号,调用listen()
函数监听Socket,调用accept()
函数接受客户端的连接请求,调用connect()
函数主动发起连接请求,调用send()
函数向对端发送数据,调用recv()
函数从对端接收数据,调用close()
函数关闭套接字。具体使用方法可以参考以下代码片段:
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建Socket
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET; // 使用IPv4地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址
addr.sin_port = htons(12345); // 设置端口号
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { // 发起连接请求
perror("connect");
exit(EXIT_FAILURE);
}
if (send(sockfd, "hello world", strlen("hello world"), 0) == -1) { // 向对端发送数据
perror("send");
exit(EXIT_FAILURE);
}
char buf[1024];
if (recv(sockfd, buf, sizeof(buf), 0) == -1) { // 从对端接收数据
perror("recv");
exit(EXIT_FAILURE);
}
printf("received data from socket: %s\n", buf);
close(sockfd); // 关闭Socket