进程间通信
进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源。
不同的进程需要进行信息的交互和状态传递(如: 数据传输/通知事件/资源共享/进程控制) , 因此需要进程间通信(IPC: Inter Processes Communication)
管道
管道也叫无名(匿名)管道,它是是 UNIX 系统 IPC(进程间通信)的最古老形式,所有的 UNIX 系统都支持这种通信机制。
统计一个目录中文件的数目命令:ls | wc –l,为了执行该命令,shell 创建了两
个进程来分别执行 ls 和 wc。
管道特点
- 管道的存储能力有限
- 管道有文件的特质:读和写, 匿名管道没有文件实体, 有名管道有文件实体, 但不存储数据
- 从管道读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是多少
- 通过管道传递的数据是顺序的,从管道中读取出来的字节的顺序和它们被写入管道的顺
序是完全一样的。 - 传递方向是单向的, 一端写一端读, 半双工
- 据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据
- 匿名管道只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。
获取管道大小
/*
#include <unistd.h>
long fpathconf(int fd, int name);
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int pipefd[2];
int ret = pipe(pipefd);
//获取管道的大小
long size = fpathconf(pipefd[0], _PC_PIPE_BUF);
printf("size: %ld\n", size); //size: 4096
return 0;
}
匿名管道
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建一个匿名管道,用来进程间通信
参数: int pipefd[2] 这个数组是传出参数
pipefd[0] 对应管道读端
pipefd[1] 对于管道写端
返回值:
成功 0
失败 -1
注意: 匿名管道只能用于具有关系的进程之间的通信
管道默认是阻塞的,如果管道中没有数据,read阻塞,如果管道满了,write阻塞
//子进程发送数据给父进程,父进程读取数据输出
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
//在fork之前创建管道
int pipefd[2];
int ret = pipe(pipefd);
if (ret == -1)
{
perror("pipe err");
exit(0);
}
//创建子进程
pid_t pid = fork();
if (pid > 0)
{
//父进程
printf("i am parent process, pid: %d\n", getpid());
char buf[1024] = {0};
while (1)
{
//从管道的读取端读取数据
int len = read(pipefd[0], buf, sizeof(buf));
printf("parent recv: %s, pid = %d\n", buf, getpid());
//读不用暂停,如果管道中没数据,会阻塞在那里
//向管道中不断写入数据
char *str = "hello, i am parent";
write(pipefd[1], str, strlen(str));
sleep(1);
}
}
else if (pid == 0)
{
//子进程
printf("i am child process, pid: %d\n", getpid());
char buf[1024] = {0};
while (1)
{
//向管道中不断写入数据
char *str = "hello, i am child";
write(pipefd[1], str, strlen(str));
sleep(1);
//从管道的读取端读取数据
int len = read(pipefd[0], buf, sizeof(buf));
printf("child recv: %s, pid = %d\n", buf, getpid());
bzero(buf,1024);
}
}
return 0;
}
设置管道为非阻塞
/*
设置管道非阻塞
int flags = fcntl(fd[0], F_GETFL); //获取原来的flag
flags != O_NONBLOCK; //修改flag的值
fcntl(fd[0], F_SETFL,flags); //设置新的flag
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
//在fork之前创建管道
int pipefd[2];
int ret = pipe(pipefd);
if (ret == -1)
{
perror("pipe err");
exit(0);
}
//创建子进程
pid_t pid = fork();
if (pid > 0)
{
//父进程
printf("i am parent process, pid: %d\n", getpid());
//关闭写端
close(pipefd[1]);
char buf[1024] = {0};
int flags = fcntl(pipefd[0], F_GETFL); //获取原来的flag
flags |= O_NONBLOCK; //修改flag的值
fcntl(pipefd[0], F_SETFL, flags); //设置新的flag
while (1)
{
//从管道的读取端读取数据
int len = read(pipefd[0], buf, sizeof(buf));
printf("len : %d\n", len);
printf("parent recv: %s, pid = %d\n", buf, getpid());
memset(buf, 0, 1024); //清空缓冲区
sleep(1);
}
}
else if (pid == 0)
{
//子进程
printf("i am child process, pid: %d\n", getpid());
//关闭读端
close(pipefd[0]);
char buf[1024] = {0};
while (1)
{
//向管道中不断写入数据
char *str = "hello, i am child";
write(pipefd[1], str, strlen(str));
sleep(5);
}
}
return 0;
}
有名管道
匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO文件。
有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。
一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/O系统调用(如read()、write()和close())。与管道一样,FIFO 也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。FIFO 的名称也由此而来:先入先出。
有名管道(FIFO)和匿名管道(pipe)有一些特点是相同的,不一样的地方在于:
- FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中。
- 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
- FIFO 有名字,不相关的进程可以通过打开有名管道进行通信。
有名管道 创建fifo文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
- pathname: 管道名称路径
- mode: 文件权限 和 open的mode一样 8进制数
返回值:
成功 0
失败 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//判断文件是否存在
int ret = access("fifo1", F_OK);
if (ret == -1)
{
printf("管道不存在,创建管道\n");
ret = mkfifo("fifo1", 0664);
if (ret == -1)
{
perror("mkfifo");
exit(0);
}
}
return 0;
}
向有名管道中写数据:
//向管道中写数据
/*
有名管道的注意事项:
1.一个为只读而打开一个管道的进程会阻塞,直到另外一个进程为只写打开管道
2.一个为只写而打开一个管道的进程会阻塞,直到另外一个进程为只读打开管道
读管道:
管道中有数据,read返回实际读到的字节数
管道中无数据:
管道写端被全部关闭,read返回0,(相当于读到文件末尾)
写端没有全部被关闭,read阻塞等待
写管道:
管道读端被全部关闭,进行异常终止(收到一个SIGPIPE信号)
管道读端没有全部关闭:
管道已经满了,write会阻塞
管道没有满,write将数据写入,并返回实际写入的字节数。
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
//判断文件是否存在
int ret = access("testfifo", F_OK);
if (ret == -1)
{
printf("管道不存在,创建管道\n");
//创建管道文件
ret = mkfifo("testfifo", 0664);
if (ret == -1)
{
perror("mkfifo");
exit(0);
}
}
//只写方式打开管道
int fd = open("testfifo", O_WRONLY);
if (fd == -1)
{
perror("open err");
exit(0);
}
for (int i = 0; i < 100; i++)
{
char buf[1024];
sprintf(buf, "hello, %d\n", i);
printf("write data : %s\n", buf);
write(fd, buf, strlen(buf));
sleep(1);
}
close(fd);
return 0;
}
读有名管道的数据
//从管道中读数据
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
//打开管道文件
int fd = open("testfifo", O_RDONLY);
if (fd == -1)
{
perror("open err");
exit(0);
}
//读数据
while (1)
{
char buf[1024] = {0};
int len = read(fd, buf, sizeof(buf));
if (len == 0)
{
printf("写端断开链接了...\n");
break;
}
printf("recv buf: %s\n", buf);
}
close(fd);
return 0;
}
标签:int,08,pipefd,间通信,管道,进程,include,buf
From: https://www.cnblogs.com/anqwjoe/p/17409732.html