Linux——命名管道及日志

linux——进程间通信及管道的应用场景-优快云博客



前言

 

在我们循环创建子进程的时候,子进程回去拷贝父进程的页表,这样就会让下一个要创建的子进程指向上一个子进程。 就会像图片中的这样。


一、命名管道是什么?

  1. 定义
    • 命名管道(Named Pipe)是一种进程间通信(IPC)机制。它可以在同一台计算机的不同进程之间,或者在跨越网络的不同计算机的进程之间进行通信。命名管道就像是一个管道,数据可以通过这个管道在进程之间流动,并且它有一个名字,就像文件一样可以被其他进程识别和访问。
  2. 与匿名管道的区别
    • 匿名管道(Anonymous Pipe)只能用于具有亲缘关系(如父子进程)之间的通信,因为它没有名字,不能被其他无关联的进程访问。而命名管道可以被无关联的进程访问,只要这些进程知道命名管道的名字并且有适当的权限。
    • 例如,在一个命令行管道操作中,如 “ls -l | grep file”,这里使用的是匿名管道,它是由 shell 创建来连接 “ls -l” 和 “grep file” 这两个命令的输出和输入,这两个命令是在同一个 shell 进程下启动的子进程,它们之间有亲缘关系。而如果要让两个独立开发的应用程序进行通信,就需要使用命名管道。
  3. 工作原理
    • 命名管道在操作系统内核中创建了一个特殊的文件对象,这个文件对象有一个文件名(这就是命名管道的 “命名” 部分)。一个进程可以以写(write)模式打开这个管道,向管道中写入数据,就像向文件中写入数据一样。另一个进程可以以读(read)模式打开这个管道,从管道中读取数据。
    • 例如,在一个简单的客户端 - 服务器架构的应用程序中,服务器进程创建一个命名管道并等待客户端连接。客户端进程通过管道的名字找到并打开这个管道,然后向管道发送请求数据。服务器进程读取这些请求数据,进行处理,再将结果通过管道返回给客户端。

mkfifo创建管道,写在磁盘上的;

如果毫不相关的进程两个进程间通信——命名管道

理解:

1、如果两个不同的进程,打开同一个文件的时候,在内核中,操作系统会打开几个文件呢?

实际上在操作系统上还是匿名管道那套逻辑。

进程间通讯的前提;

先让两个不同的进程看到同一份资源

管道文件不需要刷盘

内存级文件,所以不需要磁盘落盘也就是不需要写入磁盘

那么我们怎么知道打开的是同一个文件呢?为什么要打开同一个管道文件?

只要看到同路径下同一个文件名就知道看到同一个文件了

同路径下同一个文件名=路径+文件名(它们是为具有唯一性)这种管道就是命名管道

2、编写代码

想要不同的进程间通讯

形成两个毫不相关可执行文件

makefile

.PHONY:ALL
ALL: server client
server:server.cc
	g++ -o $@ $^ -g -std=c++11
client:client.cc
	g++ -o $@ $^ -g -std=c++11
.PHONY:clean
	rm -rf strver client

管道封装成类,想用中管道时只需要调用实例化

#define FIFO_FILE "./myfifo"
#define MODE 0664

using namespace std;
enum
{
    FIFO_CREATE_ERR = 1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
};

class Init
{
public:
    Init();
    ~Init();
};

Init::Init()
{
    int n = mkfifo(FIFO_FILE, MODE);
    if (n < 0)
    {
        perror("mkfifo");
        exit(FIFO_CREATE_ERR);
    }
}

Init::~Init()
{
    int m = unlink(FIFO_FILE);
    if (m < 0)
    {
        perror("unlink");
        exit(FIFO_DELETE_ERR);
    }
}

读端

int main()
{
    Log log;
    Init init; // 在实例化的时候创建管道
    // 打开管道
    log.Enable(Onefile);
    int fd = open(FIFO_FILE, O_RDONLY);
    if (fd < 0)
    {
       
        exit(FIFO_OPEN_ERR);
    }
    // 开始通信
    while (true)
    {
        char buff[2024];
        int x = read(fd, buff, sizeof(buff));
        if (x == 0)
        {
            break;
        }
        if (x > 0)
        {
            buff[x] = {0};
            cout << "client say# " << buff << endl;
        }
        else
            break;
    }
    close(fd);
    // 退出进程的时候调用析构删除管道
    return 0;
}

写端

int main()
{
    // 打开文件开始写
    int fd = open(FIFO_FILE, O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        exit(FIFO_OPEN_ERR);
    }
    cout << "client open file done" << endl;

    // 开始通讯
    string line;

    while (true)
    {
        cout << "Please Enter@ ";
        getline(cin, line);
        write(fd, line.c_str(), line.size());
    }
    close(fd);
    return 0;
}

日志

1、日志是什么?

在计算机领域的日志

  • 系统日志
    • 系统日志是记录操作系统或软件系统运行过程中各种事件的文件。例如,Windows 操作系统会记录系统启动、设备驱动程序的加载和卸载、应用程序的安装和运行错误等信息。这些日志可以帮助系统管理员监控系统的健康状况,及时发现并解决潜在的问题,如安全漏洞、性能瓶颈等。
    • 以 Linux 系统为例,系统日志文件通常存储在 “/var/log” 目录下,其中 “syslog” 文件记录了系统范围内的各种消息,包括内核消息、服务启动和停止信息等。当系统出现故障,如某个服务无法正常启动,管理员可以查看 syslog 文件,查找相关的错误提示,比如 “Failed to start [service name]” 这样的信息,来确定故障原因。
  • 应用程序日志
    • 应用程序日志是由各种软件应用记录自身运行时产生的事件。比如,一个 Web 服务器应用会记录每个客户端的访问请求,包括请求的时间、请求的资源(如网页文件)、请求的状态码(如 200 表示成功,404 表示未找到资源)等。对于开发人员来说,这些日志是调试程序的重要依据。
    • 例如,一个电子商务网站的购物车应用程序会记录用户添加商品、删除商品、结算等操作的日志。如果用户反馈购物车结算出现问题,开发人员可以通过查看应用程序日志,查找在结算过程中是否有错误提示或者异常情况发生,比如数据库连接错误或者商品价格计算错误等。

2、日志有什么?

输出时间、日志的等级、日志内容、文件的名称和行号

日志的等级

lifo:常规消息

warning:报警信息

error:必要严重了,可能需要立即处理

fatal:致命的

Debug:调试

实现一个简单的日志函数

3、获取时间库函数

localtime

getoftime

日志格式

日志获取时间

默认部分+自定义部分

#pragma once

#include<cstring>
#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
#define SIZE 1024

#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 LogFile "log.txt"

   void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 格式:默认部分+自定义部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

        // printf("%s", logtxt); // 暂时打印
        printLog(level, logtxt);
    }

4、完整代码

#pragma once

#include<cstring>
#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
#define SIZE 1024

#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 LogFile "log.txt"

class Log
{
public:
    Log()
    {
        printMethod = Onefile;
        path = "./log/";
    }
    void Enable(int method)
    {
        printMethod = method;
    }
    std::string levelToString(int level)
    {
        switch (level)
        {
        case Info:
            return "Info";
        case Debug:
            return "Debug";
        case Warning:
            return "Warning";
        case Error:
            return "Error";
        case Fatal:
            return "Fatal";
        default:
            return "None";
        }
    }
    void printLog(int level, const std::string &logtxt)
    {
        switch (printMethod)
        {
        case Screen:
            std::cout << logtxt << std::endl;
            break;
        case Onefile:
            printOneFile(LogFile, logtxt);
            break;
        case Classfile:
            printClassFile(level, logtxt);
            break;
        default:
            break;
        }
    }
    void printOneFile(const std::string &logname, const std::string &logtxt)
    {
        std::string _logname = path + logname;
        int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"
        if (fd < 0)
            return;
        write(fd, logtxt.c_str(), logtxt.size());
        close(fd);
    }
    void printClassFile(int level, const std::string &logtxt)
    {
        std::string filename = LogFile;
        filename += ".";
        filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"
        printOneFile(filename, logtxt);
    }

    ~Log()
    {
    }
    void operator()(int level, const char *format, ...)
    {
        time_t t = time(nullptr);
        struct tm *ctime = localtime(&t);
        char leftbuffer[SIZE];
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
                 ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,
                 ctime->tm_hour, ctime->tm_min, ctime->tm_sec);

        va_list s;
        va_start(s, format);
        char rightbuffer[SIZE];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
        va_end(s);

        // 格式:默认部分+自定义部分
        char logtxt[SIZE * 2];
        snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);

        // printf("%s", logtxt); // 暂时打印
        printLog(level, logtxt);
    }

private:
    int printMethod;
    std::string path;
};


总结

命名管道(Named Pipe)是一种进程间通信(IPC)机制。它可以在同一台计算机的不同进程之间,或者在跨越网络的不同计算机的进程之间进行通信。命名管道就像是一个管道,数据可以通过这个管道在进程之间流动,并且它有一个名字,就像文件一样可以被其他进程识别和访问。

以 Linux 系统为例,系统日志文件通常存储在 “/var/log” 目录下,其中 “syslog” 文件记录了系统范围内的各种消息,包括内核消息、服务启动和停止信息等。当系统出现故障,如某个服务无法正常启动,管理员可以查看 syslog 文件,查找相关的错误提示,比如 “Failed to start [service name]” 这样的信息,来确定故障原因

### SecureCRT 中命名管道的使用方法 #### 命名管道简介 命名管道(Named Pipe)是一种进程间通信机制,允许不同程序之间通过指定路径共享数据。它通常用于本地系统中的应用程序之间的交互。 在 SecureCRT 中配置命名管道涉及设置会话选项以及调整目标系统的权限和环境支持。以下是关于如何在 SecureCRT 中启用并使用命名管道的相关说明: --- #### 配置 SecureCRT 的命名管道功能 1. **启动 SecureCRT 并打开会话属性** 打开会话管理器,在选定的目标主机上右键单击并选择“Properties”。这一步是为了进入详细的会话参数配置界面[^3]。 2. **导航到终端设置部分** 在弹出的窗口中依次点击 `Terminal` -> `Advanced` 子菜单项。在这里可以找到与输入输出流相关的高级选项。 3. **激活 Named Pipes 支持** 查找名为 “Use named pipes for input/output” 或类似的选项框,并将其勾选开启。此操作告知 SecureCRT 将标准输入输出重定向至特定命名管道而非默认控制台设备[^4]。 4. **定义具体管道名称** 输入具体的命名管道路径字符串于对应字段内,默认格式可能类似于 `\.\pipe\YourPipeNameHere` 。注意这里的反斜杠转义规则需遵循操作系统约定。 5. **保存更改退出对话框** 完成上述修改之后确认提交改动,重新连接远程服务器验证效果即可。 --- #### Linux 下配合命令实现文件查找实例 假设我们已经在 Windows 主机上的 SecureCRT 正确设置了某个命名为 `MySecureCRTPipe` 的双向通讯渠道,则可以在关联 SSH 登录后的 Kali Linux 终端执行如下脚本片段来演示实际应用情形之一——自动筛选符合条件的日志条目并通过该通道返回给客户端显示: ```bash #!/bin/bash # 设置变量简化后续调用逻辑 PIPE_PATH="\\.\pipe\MySecureCRTPipe" SEARCH_DIRS=("/etc/" "/root/") PATTERN="[A-Z]*" for DIR in "${SEARCH_DIRS[@]}"; do find "$DIR" -type f \( -name "*$PATTERN*" \) | while read FILE; do echo "Found matching file: $FILE" > "$PIPE_PATH" done done ``` 以上代码段利用循环结构遍历预设目录列表逐一检索满足条件(即名字首字母大写)的所有常规文件对象并将发现结果逐行发送回先前创建好的专用传输链路之中[^5]。 --- #### 注意事项 - 确认所使用的操作系统版本完全兼容拟议方案; - 调整防火墙策略确保无阻碍正常的数据交换过程发生; - 如果遇到任何异常状况务必查阅官方文档获取进一步指导信息[^6]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃喵的鲤鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值