목차
관로
실현원리
커널 링 링 큐 메커니즘은 비교적 간단한 커널 버퍼를 사용하여 구현됩니다.
특성
가짜 문서.
파이프의 데이터는 한 번만 읽을 수 있습니다.
파이프라인의 데이터는 한 방향으로만 흐를 수 있습니다.
한정
혼자만 쓸 수 있고 혼자 읽을 수는 없습니다.
데이터를 반복해서 읽을 수 없습니다.
반이중 통신.
친족 과정 사이에서 사용 가능합니다.
읽기와 쓰기 행동
파이프 읽기
파이프라인에는 데이터가 있습니다. read는 실제로 읽은 바이트 수를 반환합니다.
데이터가 없는 파이프라인:
모든 쓰기 끝이 닫히고 읽기 함수는 0을 반환합니다.
쓰기 끝이 닫히지 않고 읽기 기능이 차단되고 대기합니다.
파이프 쓰기
SIGPIPE 신호로 인해 모든 읽기 끝이 닫히고 비정상 종료됩니다.
읽기 끝이 닫히지 않습니다.
파이프라인 데이터가 꽉 차서 대기가 차단됩니다.
파이프라인 데이터가 가득 차지 않은 경우 쓴 바이트 수를 반환합니다.
버퍼 크기
ulimit -a
man 3 fpathconf
반환 값
성공: 파이프의 크기.
실패: -1.
장점과 단점
이점
신호와 비교하여 소켓은 프로세스 간 통신을 구현하므로 훨씬 간단합니다.
결점
단방향 통신만 가능하며, 양방향 통신을 위해서는 두 개의 채널을 구축해야 합니다.
아버지-아들 프로세스와 형제 프로세스(공통 조상 포함) 간의 통신에만 사용할 수 있습니다. 이 문제는 나중에 fifo 명명된 파이프를 사용하여 해결되었습니다.
파이프
파이프라인을 만들고 엽니다.
man 2 pipe
매개변수 파이프fd[2]
Pipefd[0]: 읽기 끝.
Pipefd[1]: 쓰기 끝.
반환 값
성공: 0
실패: -1
테스트 코드 1
상위 프로세스는 파이프를 사용하여 내용을 작성하고 하위 프로세스는 파이프를 사용하여 내용을 읽고 읽은 내용을 터미널에 출력합니다.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
char data[1024]; //接收的数据
int leng; //接收数据的长度
pid_t JinCheng_ID; //进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
}
printf("开始创建进程!\n");
JinCheng_ID = fork();
if (JinCheng_ID > 0) //父进程
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
close(pipefd[0]); //关闭读端
write(pipefd[1], "你好,世界!\n", strlen("你好,世界!\n")); //通过管道向子进程发送数据
sleep(1);
close(pipefd[1]);
printf("这是父进程,父进程结束。\n");
}
else if (JinCheng_ID == 0) //子进程
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子进程关闭写端
leng = read(pipefd[0], data, sizeof(data)); //接收父进程发送的数据
write(STDOUT_FILENO, data, leng); //将数据写到终端上
close(pipefd[0]);
printf("这是子进程,子进程结束。\n");
}
return 0;
}
시험 결과
테스트 코드 2
파이프를 사용하여 ls | wc -l 명령을 구현하십시오.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
char data[1024]; //接收的数据
int leng; //接收数据的长度
pid_t JinCheng_ID; //进程ID
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
exit(1);
}
printf("创建管道完成!\n");
printf("开始创建进程!\n");
JinCheng_ID = fork();
if (JinCheng_ID == 0) //子进程
{
printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子进程关闭写端
dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
execlp("wc", "wc", "-l", NULL);
perror("子进程错误");
}
else if (JinCheng_ID > 0) //父进程
{
printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
close(pipefd[0]); //关闭读端
dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
execlp("ls", "ls", NULL);
perror("父进程错误");
}
else if (JinCheng_ID == -1)
{
perror("创建进程错误");
exit(1);
}
return 0;
}
시험 결과
테스트 코드 3
형제 간 프로세스를 사용하여 ls | wc -l 명령을 구현합니다.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int GuanDao_flag; //管道标志位
int pipefd[2];
pid_t JinCheng_ID; //进程ID
int i;
printf("程序开始运行!\n");
printf("当前进程的ID是%d。\n", getpid());
printf("开始创建管道!\n");
GuanDao_flag = pipe(pipefd); //创建管道
if (GuanDao_flag == -1)
{
perror("创建管道错误");
exit(1);
}
printf("创建管道完成!\n");
printf("开始创建进程!\n");
for (i = 0; i < 2; i++)
{
JinCheng_ID = fork();
if (JinCheng_ID == -1)
{
perror("创建进程错误");
exit(1);
}
else if (JinCheng_ID == 0) //子进程
{
break;
}
}
if(i==0){
printf("这是子1进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[0]); //子1进程关闭读端
dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
execlp("ls", "ls", NULL);
perror("子1进程错误");
}
else if(i==1){
printf("这是子2进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
close(pipefd[1]); //子2进程关闭写端
dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
execlp("wc", "wc", "-l", NULL);
perror("子进程错误");
}
else if(i==2){
printf("这是父进程,当前进程的ID是%d。\n", getpid());
close(pipefd[0]); //父进程关闭读端、写端,保证兄弟进程之间形成单向通信
close(pipefd[1]);
wait(NULL);
wait(NULL);
printf("父进程结束。\n");
}
return 0;
}
시험 결과
FIFO
FIFO는 파이프와 구별하기 위해 흔히 명명된 파이프라고 불립니다. 파이프는 "혈액 관련" 프로세스 사이에서만 사용할 수 있습니다. 그러나 FIFO를 통해 관련되지 않은 프로세스도 데이터를 교환할 수 있습니다. FIFO 파일은 디스크에 데이터 블록이 없으며 커널에서 채널을 식별하는 데만 사용됩니다. 각 프로세스는 읽기/쓰기를 위해 이 파일을 열 수 있으며 실제로 프로세스 간 통신을 실현하기 위해 커널 채널을 읽고 씁니다.
생성방법
mkfifo 管道文件名
man 3 mkfifo
매개변수 경로명
파이프라인 파일 이름.
매개변수 모드
파이프라인 파일 권한.
반환 값
성공: 0
실패: -1
테스트 코드 4
mkfifo를 사용하여 파이프라인 파일을 생성합니다.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int flag;
flag=mkfifo("GuanDao",0664);
if(flag==-1){
perror("创建管道文件错误");
}
return 0;
}
시험 결과
테스트 코드 5
파이프를 사용하여 관련되지 않은 두 프로세스를 연결하고 통신합니다.
/*
CeShi5_1.c
接收CeShi5_2进程的数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, leng;
char data[4096];
printf("程序开始运行。\n");
printf("开始打开管道文件的读端。\n");
fd = open(argv[1], O_RDONLY);
if (fd == -1)
{
perror("打开管道文件错误");
exit(1);
}
printf("打开管道文件的读端完成。\n");
printf("开始读取管道文件读端的数据。\n");
while (1)
{
leng = read(fd, data, sizeof(data));
if (leng > 0)
{
//printf("读取到数据为:");
write(STDOUT_FILENO, "读取到数据为:", strlen("读取到数据为:"));
write(STDOUT_FILENO, data, leng);
}
}
close(fd);
return 0;
}
/*
CeShi5_2.c
向CeShi5_1进程发送数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, i;
char data[4096];
printf("程序开始运行。\n");
printf("开始打开管道文件的写端。\n");
fd = open(argv[1], O_WRONLY);
if (fd == -1)
{
perror("打开管道文件错误");
exit(1);
}
printf("打开管道文件的写端完成。\n");
printf("开始向管道文件写端写数据。\n");
i = 1;
while (1)
{
sprintf(data, "你好,世界!这是写进程第%d次向管道写端写数据。\n", i);
write(fd, data, strlen(data));
printf("第%d次写成功。\n", i);
i++;
sleep(1);
}
close(fd);
return 0;
}
시험 결과
프로세스 간 파일 통신
관련되지 않은 프로세스 간의 통신을 완료하려면 파일을 사용하십시오.
테스트 코드 6
/*
CeShi6_1.c
优先执行数据的写入文件
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, flag;
char *data = "你好,世界!这是CeShi6_1进程写的。\n";
char data1[1024];
int leng;
printf("程序开始运行。\n");
printf("开始打开文件。\n");
fd = open("temp.txt", O_RDWR | O_TRUNC | O_CREAT, 0664);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("开始文件写数据。\n");
write(fd, data, strlen(data));
printf("写的数据是:%s", data);
printf("文件写数据完成。\n");
printf("开始睡大觉。\n");
sleep(5);
printf("睡醒了,康康文件的数据。\n");
lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
leng = read(fd, data1, sizeof(data1));
flag=write(STDOUT_FILENO,data1,leng);
if(flag==-1){
perror("输出错误");
exit(1);
}
printf("我真是服了,把我数据给改了,CeShi6_2你个老6。\n");
close(fd);
return 0;
}
/*
CeShi6_2.c
读取文件数据和修改文件数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd, flag;
char *data = "你好,世界!这是CeShi6_2进程写的。\n";
char data1[1024];
int leng;
printf("程序开始运行。\n");
printf("开始睡大觉。\n");
sleep(2);
printf("睡醒了,我是老6,把你CeShi6_1进程写的数据给改了。\n");
printf("开始打开文件。\n");
fd = open("temp.txt", O_RDWR);
if (fd == -1)
{
perror("打开文件错误");
exit(1);
}
printf("打开文件完成。\n");
printf("让我康康你写了啥。\n");
leng = read(fd, data1, sizeof(data1));
write(STDOUT_FILENO, data1, leng);
printf("哦豁,写了这玩意。\n");
printf("开始文件写数据。\n");
lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
write(fd, data, strlen(data));
printf("写的数据是:%s", data);
printf("文件写数据完成。\n");
close(fd);
return 0;
}