APUE读书笔记---进程间通信(IPC)之管道和有名管道(FIFO)
来源:互联网 发布:淘宝代理充话费 编辑:程序博客网 时间:2024/06/08 11:06
APUE读书笔记—进程间通信(IPC)之管道和有名管道(FIFO)
1. 管道
pipe函数可以创建管道,提供一个单向数据流(半双工)。
#include <unistd.h>int pipe(int pipefd[2]);//返回值:若成功,返回0,若出错,返回-1
- 该函数返回两个文件描述符,fd[0],fd[1]。前者打开来读,后者打开来写。所以管道在用户程序看起来像是一个打开的文件,通过read(fd[0])或者write(fd[1])。向这个文件读写数据其实是在读写内核缓冲区。
- 调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过pipefd参数传出给用户程序两个文件描述符。
- 管道作用于有血缘关系的进程之间,先pipe,再通过fork来传递。
- 管道使用环形队列实现的,数据从写端流入从读端流出。
1.1 管道如何进行通信
- 父进程调用pipe函数在内核中开辟管道,得到两个文件描述符指向管道的两端。
- 父进程调用fork函数,子进程共享两个文件描述符指向同一管道。
- 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读。
1.2 管道实例
#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <sys/wait.h>#include <sys/types.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int main(void){ int fd[2]; pid_t pid; int len; int flag; char str[1024] = "hello world\n"; char buf[1024]; if(pipe(fd) < 0){ perror("pipe err"); exit(1); } //父写子读 if((pid = fork()) < 0){ perror("fork err"); exit(1); }else if(pid > 0){ //parent close(fd[0]); //关闭父进程读端 sleep(5); write(fd[1], str, strlen(str)); close(fd[1]); //写完关闭写端 wait(NULL); }else{ //child close(fd[1]); //关闭子进程写端 //设置子进程读端为非阻塞,如失败返回EAGAIN flag = fcntl(fd[0], F_GETFL); flag |= O_NONBLOCK; fcntl(fd[0], F_SETFL, flag);tryagain: len = read(fd[0], buf, sizeof(str)); if(len < 0){ if(errno == EAGAIN){ //非阻塞读若失败则返回EAGAIN write(STDOUT_FILENO, "try again\n", 10); sleep(1); goto tryagain; }else{ perror("read err"); exit(1); } } close(fd[0]); //读完关闭读端 write(STDOUT_FILENO, buf, len); } return 0;}
父进程睡眠5秒后为子进程写数据,子进程每秒尝试读数据,读不到打印出try again,否则打印出读到的数据,运行如下:
try againtry againtry againtry againtry againhello world
1.3 管道的使用事项
- 两个进程通过一个管道只能实现单向通信,如果要实现双向通信必须使用两个管道。
- 写端关闭,当读端读完管道的内容,再次读,返回0,相当于读到EOF。
- 如果管道写端没关闭,写端暂时无数据,读端读完管道里的数据后再次读,读端会阻塞。
- 读端关闭,写端写管道,会产生SIGPIPE信号,写进程默认情况下会终止进程。
- 读端没读管道数据,当写端写满管道后,再次写,写端阻塞。
1.4 管道大小
利用fpathconf函数可以查看本机管道大小
#include <stdio.h>#include <unistd.h>int main(){ int fd[2]; pipe(fd); printf("pipe buf = %ld\n", fpathconf(fd[1], _PC_PIPE_BUF)); return 0;}
运行结果:
pipe buf = 4096
2. FIFO有名管道
FIFO是指先进先出(first in first out),它是一个单向数据流(半双工),每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO。FIFO又称有名管道。
#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);//返回值:若成功,返回0,若出错,返回-1
- pathname是一个普通的路径名,它是FIFO的名字。
- mode指定文件权限位。
- mkfifo函数隐含包含O_CREATE | O_EXCL。也就是说,要么创建一个新的FIFO,要么返回一个EEXIST错误。
2.1 例子:无亲缘关系的客户与服务器
服务器主函数代码 server_main,c
#include "unpipc.h"int main(int argc, char *argv[]){ int readfd, writefd; //create two FIFO if((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)) sys_err("can't create FIFO1\n"); if((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST)) { unlink(FIFO1); sys_err("can't create FIFO2\n"); } readfd = open(FIFO1, O_RDONLY, 0); writefd = open(FIFO2, O_WRONLY, 0); server(readfd, writefd); exit(0);}
服务器函数代码 server.c
#include "unpipc.h"void server(int readfd, int writefd){ int fd; ssize_t n; char buff[MAXLINE + 1]; //read pathname from ipc channel if((n = read(readfd, buff, MAXLINE)) == 0) sys_err("end-of-file while reading pathname"); buff[n] = '\0'; if((fd = open(buff, O_RDONLY)) < 0) { snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n", strerror(errno)); //tell client error n = strlen(buff); write(writefd, buff, n); } else { while((n = read(fd, buff, MAXLINE)) > 0) write(writefd, buff, n); close(fd); }}
客户端主函数代码 client_main.c
#include "unpipc.h"int main(int argc, char *argv[]){ int readfd, writefd; writefd = open(FIFO1, O_WRONLY, 0); readfd = open(FIFO2, O_RDONLY, 0); client(readfd, writefd); close(readfd); close(writefd); unlink(FIFO2); unlink(FIFO1); exit(0);
客户端函数代码 client.c
#include "unpipc.h"void client(int readfd, int writefd){ size_t len; ssize_t n; char buff[MAXLINE]; //read pathname fgets(buff, MAXLINE, stdin); //fgets 会读最后一个‘\n’字节 len = strlen(buff); if(buff[len-1] == '\n') len--; //del newline from fgets //write pathname to ipc channel write(writefd, buff, len); //read from ipc. write to standard output while((n = read(readfd, buff, MAXLINE)) > 0) write(STDOUT_FILENO, buff, n);}
头文件 unpipc.h
#ifndef _UNPIPE_H_#define _UNPIPE_H_#include <stdio.h>#include <sys/wait.h>#include <sys/types.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <sys/stat.h>#define MAXLINE 4096#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)#define FIFO1 "/home/menwen/work/APUE/IPC/apue_code/fifo.1"#define FIFO2 "/home/menwen/work/APUE/IPC/apue_code/fifo.2"void client(int readfd, int writefd); void server(int readfd, int writefd);void sys_err(char *str);#endif
出错处理函数代码
//sys_err.c是我自己简单封装的一个出错处理函数#include "unpipc.h"void sys_err(char *str){ perror(str); exit(1);}
- 编译:
gcc server.c server_main.c unpipc.h sys_err.c -o servergcc client.c client_main.c unpipc.h -o client
- 运行:
➜ apue_code ./server & //后台运行服务器端代码[1] 21581 ➜ apue_code ./client //在执行客户端代码/home/menwen/work/APUE/IPC/apue_code/unpipc.h //输入一个文件的路径,就会通过FIFO得到文件内容如下:正是unpipc.h的内容#ifndef _UNPIPE_H_#define _UNPIPE_H_#include <stdio.h>#include <sys/wait.h>#include <sys/types.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <sys/stat.h>#define MAXLINE 4096#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)#define FIFO1 "/home/menwen/work/APUE/IPC/apue_code/fifo.1"#define FIFO2 "/home/menwen/work/APUE/IPC/apue_code/fifo.2"void client(int readfd, int writefd); void server(int readfd, int writefd);void sys_err(char *str);#endif[1] + 21581 done ./server
0 0
- APUE读书笔记---进程间通信(IPC)之管道和有名管道(FIFO)
- 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)
- Linux进程间通信(一)之无名管道(PIPE)和有名管道(FIFO)
- [IPC]通信有名管道FIFO
- 【IPC通信】有名管道FIFO
- Linux--进程间通信(管道及有名管道FIFO)
- Linux--进程间通信(管道及有名管道FIFO)
- Linux--进程间通信(管道及有名管道FIFO)
- 进程间通信______有名管道(FIFO)
- 进程间的通信----有名管道fifo
- 进程通信-有名管道FIFO
- 进程间通信-管道(PIPE)和有名管道(FIFO)
- 进程间通信-管道(有名管道和无名管道)
- 2、进程间通信-FIFO(有名管道)
- 进程间通信---------有名管道(named pipe/FIFO)
- linux进程间通信-有名管道(FIFO)
- linux进程间通信-有名管道(FIFO)
- 进程间通信——有名管道(FIFO)
- Spark mmlib开发中遇到的问题
- sd
- sql如何根据父集编号查询多集子集
- 浅谈C++基类的析构函数为虚函数
- 确定权重方法之一:主成分分析
- APUE读书笔记---进程间通信(IPC)之管道和有名管道(FIFO)
- 命运(HDU-2571)
- PHP面向对象:对象中成员的访问
- 欢迎使用CSDN-markdown编辑器
- 小公司的前端应该怎么做?
- 解决macOS下mysql的sql_mode=only_full_group_by问题
- POJ 3075 Tic-Tac-Toe 再想想
- java基础.io流.持久化.用该函数写就只能用该函数读
- 并查集