匿名管道应用的一个限制就是只能在具有血缘关系的进程间通信,如果想在不相关的进程之间交换数据,可以适用FIFO文件来做这项工作,它经常被称为命名管道
函数
在xshell创建一个命名管道文件
mkfifo filename
int mkfifo(const char *filename,mode_t mode);
文件类型是p,表示管道
从左边echo重定向到管道文件里,文件大小还是0,但另一个bash可以从管道里读取到内容
循环追加重定向可以不停读取内容
删除命名管道
unlink fifoname
原理
两个不同的进程打开同一个文件,在内核中,它的struct files会有两份,因为有不同的读写位置等,但文件的缓冲区还是只有一个
命名管道也是内存级文件,只需要进程间读取,并不需要保存落盘。两个进程要通信,先让两个进程看到同一份资源,首先,怎么保证打开的是同一份文件
同一路径同一个文件名:路径+文件名,就是同一个文件
匿名管道与命名管道的区别
匿名管道由pipe函数的创建并打开
命名管道由mkfifo函数创建,打开用open
FIFO与pipei之间唯一的区别是它们创建与打开的方式不同,一旦这些工作完成之后,它们具有相同的语义
命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写打开FIFO
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
测试
先创建一个管道,和文件操作一样,写的时候打开文件写入,读的时候打开文件读内容,使用文件操作函数。将管道的创建和销毁封装成一个类,构建和析构自动创建和销毁管道
common.hpp
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#define FIFO_FILE "./fifo"
#define MODE 0664
enum {
FIFO_CREAT_ERR = 1,
FIFO_DELET_ERR,
FIFO_OPEN_ERR
};
class Init
{
public:
Init()
{
// 创建管道
int n = mkfifo(FIFO_FILE, MODE);
if (n == -1)
{
perror("server creat");
exit(FIFO_CREAT_ERR);
}
}
~Init()
{
// 删除管道
int n = unlink(FIFO_FILE);
if (n == -1)
{
perror("server unlink");
exit(FIFO_DELET_ERR);
}
}
};
server.cc
#include <iostream>
#include "comon.hpp"
using namespace std;
int main()
{
Init init;
//打开管道
int fd = open(FIFO_FILE, O_RDONLY); //等待写入方打开,自己才会打开,打卡阻塞
if (fd < 0)
{
perror("server open");
exit(FIFO_OPEN_ERR);
}
//通信,服务端读取
while (true)
{
char buff[1024] = {0};
int x = read(fd, buff, sizeof(buff));
if (x > 0)
{
buff[x] = 0;
printf("client: %s\n", buff);
}
else if (x == 0)
{
printf("client quit, me too\n");
break;
}
sleep(1);
}
close(fd);
}
client.cc
#include <iostream>
#include "comon.hpp"
using namespace std;
int main()
{
// 服务端使用管道
int fd = open(FIFO_FILE, O_WRONLY);
if (fd == -1)
{
perror("client open");
exit(FIFO_OPEN_ERR);
}
//写入内容
string str;
while (true)
{
printf("please enter: ");
getline(cin, str);
write(fd, str.c_str(), str.size());
}
close(fd);
}
日志
可变参数
像上面的printf函数一样,可以传入无数个参数,这个就是可变参数。可变参数用三个.表示,必须有一个具体的参数,用来定位参数起始的地址,解析有多少个可变参数
先得有一个va_list结构,本质是char*,保存所有的参数,va_start定位到参数的起始位置,将参数内容保存到va_list,然后调用va_arg一个一个解析。解析完用va_end清理
下面用一个求和的可变参函数示范:
int sum(int n, ...)
{
int sum = 0;
va_list s;
va_start(s, n);
while (n)
{
sum = sum + va_arg(s, int);
n--;
}
return sum;
}
printf函数第一个参数是字符串,遍历字符串的占位符就知道有几个参数,需要字符串解析
日志简介
日志内容需要输出时间,想获得时间可以用time函数或gettimeofday
tm结构是时间类型,保存了时间的各个部分。注意月份是从0开始,需要加1.年份从需要加上1990
日志类分为往屏幕打印,打印到文件,和打印分类文件。日志还分等级,info常规,waring,警告,error,错误,fatal,致命,debug调试。整理完整日志信息:[级别]时间:日志内容。将内容拼接好,然后区分打印方式,往文件打印就创建文件,写入文本。多个文件需要加目录名,将日志文件都放入目录,按等级生成文件,写入不同的日志文件
#include <stdarg.h>
#include <stdio.h>
#include <cstring>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
#define info 0
#define debug 1
#define warning 2
#define error 3
#define fatal 4
#define screen 1
#define onefile 2
#define classfile 3
#define path "log.txt"
class log
{
public:
log(int style = screen)
{
printstyle = style;
dir = "log/";
}
const char *leveltostring(int level)
{
switch (level)
{
case 0:
return "info";
break;
case 1:
return "debug";
break;
case 2:
return "warning";
break;
case 3:
return "error";
break;
case 4:
return "fatal";
break;
default:
return "none";
break;
}
}
void printlog(int level, const string &logtxt)
{
switch (printstyle)
{
case screen:
cout << logtxt;
break;
case onefile:
printonefile(path, logtxt);
break;
case classfile:
printclassfile(level, logtxt);
break;
}
}
void logmessage(int level, const char *format, ...)
{
time_t t = time(nullptr);
tm *ctime = localtime(&t);
char leftbuff[1024];
sprintf(leftbuff, "[%s]%d-%d-%d %d:%d:%d:", leveltostring(level), ctime->tm_year + 1900,
ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
char rightbuff[1024];
va_list s;
va_start(s, format);
vsprintf(rightbuff, format, s);
va_end(s);
char logtext[2048];
sprintf(logtext, "%s %s\n", leftbuff, rightbuff);
//printf(logtext);
printlog(level, logtext);
}
void printonefile(const string& logname, const string& logtxt)
{
int fd = open(logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
if (fd < 0)
{
return;
}
write(fd, logtxt.c_str(), logtxt.size());
close(fd);
}
void printclassfile(int level, const string &logtxt)
{
//log.txt.info
string filename = dir + path;
filename += ".";
filename += leveltostring(level);
printonefile(filename, logtxt);
}
~log(){};
private:
int printstyle;
string dir; //分类日志,放入目录中
};
// int sum(int n, ...)
// {
// int sum = 0;
// va_list s;
// va_start(s, n);
// while (n)
// {
// sum = sum + va_arg(s, int);
// n--;
// }
// return sum;
// }
log.logmessage(fatal, “server open err%d:%s”, errno, strerror(errno));