29 命名管道

匿名管道应用的一个限制就是只能在具有血缘关系的进程间通信,如果想在不相关的进程之间交换数据,可以适用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));

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值