Linux进程间通信
进程是程序运行资源分配的最小单位。每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,Inter-Process Communication)。
匿名管道pipe
pipe只能用于有血缘关系的进程进行单向通信。调用 pipe 函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过 fd 参数传出给用户程序两个文件描述符, fd[0] 指向管道的读端, fd[1] 指向管道的写端。支持多端读或多端写,但不支持一端同时读写。也就是说,管道是半双工通信(即双方都可以发送信息,但是双方不能同时发送信息)
文件描述符
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开的文件的记录表。当程序打开一个文件时,内核向进程返回一个文件描述符。
file descriptors: 由用户程序维护的记录表,记录的是该用户程序打开的所有的文件的fd。每个进程会预留三个默认的fd:stdin(0)、stdout(1)、stderr(2)。
file table:该表是全局唯一的,由系统内核维护,记录了所有进程打开的文件的状态、偏移量、访问模式(可读写)、文件类型、该文件对应的inode对象引用等。
Inode table: 全局唯一的表,是硬盘存储的文件的元数据的集合。
三个表的关系如下图所示:
简而言之,fd就是系统维护的file table表的某一项entry的指针,应用程序通过它能读写硬盘里文件。应用程序用它来跟内核打交道,让内核以fd定位应用程序所需访问的文件并帮忙读写数据
下图可以较为清楚地表明Pipe的功能:
pipe原型
#include <unistd.h>
int pipe(int pipefd[2]);
传入的参数是一个大小为2的数组,然后就得到了两个文件描述符pipefd[0]和pipefd[1]
pipe举例
(1)举一个简单的栗子:
这里我们用一个父子进程来举例,如果要实现父子进程间的通信,在fork前就需要创建一个pipe管道
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLINE 80
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)//如果管道的文件描述符小于0
{
perror("pipe");
exit(1);
}
if ((pid = fork()) < 0)//子进程如果创建成功了,返回的pid值一定大于0
{
perror("fork");
exit(1);
}
if (pid > 0)
{ /* parent */
close(fd[0]);
write(fd[1], "hello world\n", 12);
wait(NULL);
}
else
{ /* child */
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
return 0;
}
运行结果如下:
可见这是父进程把字符串“hello world”写入到管道,子进程再从管道里面读取出来并且打印到标准输出上面来。
(2)运行博客园老师所给的pipedemo.c
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int len, i, apipe[2];//两个文件描述符
char buf[BUFSIZ];//长度为BUFSIZ
if ( pipe ( apipe ) == -1 ){
perror("could not make pipe");
exit(1);
}
printf("Got a pipe! It is file descriptors: { %d %d }\n",
apipe[0], apipe[1]);
while ( fgets(buf, BUFSIZ, stdin) ){//从输入端获取字符,存入buf数组中
len = strlen( buf );
if ( write( apipe[1], buf, len) != len ){//apipe[1]是写入端,这里write()函数将buf指针指向的内存的len长个字节写入到apipe[1]所指向的管道缓冲区中。
perror("writing to pipe");
break;
}
for ( i = 0 ; i<len ; i++ )
buf[i] = 'X' ;
len = read( apipe[0], buf, BUFSIZ ) ;
if ( len == -1 ){
perror("reading from pipe");
break;
}
if ( write( 1, buf,len ) != len ){
perror("writing to stdout");
break;
}
}
}
运行结果如下:
运行云班课里所给的代码pipedemo2.c
结果如下:
命名管道fifo
FIFO(First In First Out)文件在磁盘上没有数据块,仅仅是内核中一条通道,各进程可以读写从而实现的进程间通信。支持多端读或多端写。
命令:mkfifo 管道名
库函数:int mkfifo(const char *pathname, mode_t mode);
我们首先需要用mkfifo myfifo
生成一个fifo文件用于两个进程之间的通信
然后在同一个目录下创建两个程序文件:
wr.c:
// file: wr.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int fd, ret, i = 0;
char buf[256];
fd = open("myfifo", O_WRONLY);
if(fd < 0)
{
perror("open error");
}
printf("write start!\n");
while(i < 100)
{
snprintf(buf, 256, "hello %d\n", i);
ret = write(fd, buf, strlen(buf));
if(ret < 0)
{
perror("write error");
}
printf("write ok: %d\n", i);
i++;
sleep(1);
}
return 0;
}
rd.c:
// file: rd.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd, ret;
char buf[4096];
fd = open("myfifo", O_RDONLY);
if(fd < 0)
{
perror("open error");
}
printf("read start!\n");
while(1)
{
ret = read(fd, buf, 4096);
write(STDOUT_FILENO, buf, ret);
sleep(1);
}
return 0;
}
运行结果如下:
进入myfifo文件夹下,首先编译运行testmf.c,创建一个fifo文件,并命名为myyfifo,该文件也处在该目录下,再编译老师所给的consumer.c和producer.c代码,运行结果如下:
可见消费者端读取出了当时在生产者端写入的hahahah,fifo管道建立成功。
标签:pipe,int,fifo,间通信,fd,进程,include,buf From: https://www.cnblogs.com/ssssspm/p/16863574.html