当前位置: 首页 > news >正文

Linux的线程篇章 - 线程池、进程池等知识】

Linux学习笔记---018

  • Linux之线程池、进程池知识储备
    • 1、线程池
      • 1.1、池化技术
        • 1.1.1、定义与原理
        • 1.1.2、优点
        • 1.1.3、应用场景
      • 1.2、线程池的特点与优势
      • 1.3、线程池的适用场景
      • 1.4、线程池的实现
    • 2、进程池
      • 2.1、定义和基本概念
      • 2.2、进程池的特点与优势
      • 2.3、进程池的适用场景:
    • 3、单例模式
      • 3.1、“饿汉”
      • 3.2、“懒汉”
    • 4、死锁问题
      • 4.1、死锁的定义
      • 4.2、死锁的产生条件
      • 4.3、死锁的原因
      • 4.4、死锁的解决方式
      • 4.5、小结
    • 5、 可重入 VS 线程安全
      • 5.1、可重入
      • 5.2、线程安全
      • 5.3、关系与区别
    • 6、volatile关键字
      • 6.1、volatile关键字的作用
      • 6.2、volatile样例
    • 7、日志的重要性
      • 7.1、日志文件概述
      • 7.2、常见日志文件及作用
      • 7.3、日志管理工具
      • 7.4、日志的查看与分析
      • 7.5、简易日志的实现
      • 7.6、日志的维护与优化

Linux之线程池、进程池知识储备

前言:
前篇开始进行了解学习Linux线程、进程、多线程、多进程等相关内容,接下来学习关于Linux线程池、进程池等知识,深入地了解这个强大的开源操作系统。
/知识点汇总/

1、线程池

线程池是一种基于池化技术的并发框架,它预先创建并管理一组线程,用于执行提交给它的任务。
线程池中的线程可以重复利用,减少了线程的创建和销毁开销,提高了系统的响应速度和吞吐量。

1.1、池化技术

池化技术(Pooling Technique)是一种广泛应用于软件开发、系统设计以及深度学习中的资源管理策略。其核心理念是通过复用预先创建和初始化的资源,来提升系统性能、效率和稳定性。

1.1.1、定义与原理

池化技术指的是在系统运行前或运行时,预先分配并维护一组资源(如线程、数据库连接、内存块等),这些资源被组织成一个“池”。当系统需要这些资源时,不是每次都重新创建新的实例,而是从池中获取一个已存在的实例进行使用。使用完毕后,实例会被归还到池中,以便后续的请求可以重复利用。

1.1.2、优点

1.资源节约:
减少资源创建与销毁的开销。例如,创建新线程、打开数据库连接等操作可能涉及耗时的系统调用,而池化技术通过复用已有资源来避免这些开销。
2.提高响应速度:
通过预先初始化好的资源和重用现有资源,避免了每次请求时的初始化延迟,从而提高了系统的响应速度。
3.控制资源使用量:
限制资源的最大使用数量,防止资源耗尽,确保系统的稳定运行。
4.增强系统性能和稳定性:
通过管理资源的生命周期(监控),可以及时调整资源分配,满足系统需求,并更好地处理资源泄露等问题。

1.1.3、应用场景

1.线程池: 管理一组可用的线程,以高效地执行并发任务。线程池减少了线程的创建和销毁开销,提高了系统对并发请求的响应能力。
2.数据库连接池: 管理数据库连接资源,通过复用连接来减少连接创建和销毁的开销,提高系统对数据库资源的访问性能。
3.内存池: 将一块连续的内存分割成多个固定大小的对象块,通过对这些对象进行复用,减少内存分配与释放的频率,提高内存使用效率。
4.连接池: 不仅限于数据库连接,还包括如Redis连接池、HTTP连接池等,用于管理各种网络连接资源。
5.对象池: 管理一组可复用的对象实例,以减少对象的创建和销毁开销,提高系统的性能和资源利用率。
6.缓存池: 将常用的计算结果、数据或资源存储在内存中,以加快对这些数据的访问速度。

1.2、线程池的特点与优势

1.资源共享: 线程池中的线程共享相同的地址空间和资源,使得线程间的通信和同步变得容易。
2.减少开销: 线程的创建和销毁开销相对较小,线程池通过重用线程减少了这些开销。
3.提高响应速度: 线程池可以快速地响应大量的并发请求,因为它可以立即分配一个空闲线程来处理新任务。
4.易于管理: 线程池提供了统一的接口来管理线程的生命周期和任务执行,简化了并发编程的复杂性。

1.3、线程池的适用场景

1.IO密集型任务: 如Web服务器处理HTTP请求,数据库操作等。这些任务大部分时间都在等待IO操作完成,线程池可以充分利用CPU资源来并发处理这些任务。
2.高并发场景: 当需要处理大量并发请求时,线程池可以快速地分配线程来处理这些请求,提高系统的响应速度和吞吐量。
3.计算密集型任务(但非极端情况): 虽然线程池在处理计算密集型任务时可能不如进程池高效(因为线程间的切换开销和共享资源竞争),但在非极端情况下,线程池仍然可以胜任。

1.4、线程池的实现

ThreadPool.hpp

#pragma once#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <queue>
#include <functional>
#include "Thread.hpp"using namespace ThreadMoudle;static const int gdefaultnum = 5;void test()
{while (true){std::cout << "hello world" << std::endl;sleep(1);}
}template <typename T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void Wakeup(){pthread_cond_signal(&_cond);}void WakeupAll(){pthread_cond_broadcast(&_cond);}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}bool IsEmpty(){return _task_queue.empty();}void HandlerTask(const std::string& name) // this,处理任务{while (true){// 取任务LockQueue();while (IsEmpty() && _isrunning){_sleep_thread_num++;Sleep();//休眠_sleep_thread_num--;}// 判定这种情况才退出if (IsEmpty() && !_isrunning){std::cout << name << " quit" << std::endl;UnlockQueue();break;}// 有任务T t = _task_queue.front();_task_queue.pop();UnlockQueue();// 处理任务t(); // 处理任务,此处不用/不能在临界区中处理std::cout << name << ": " << t.result() << std::endl;}}public:ThreadPool(int thread_num = gdefaultnum) : _thread_num(thread_num), _isrunning(false), _sleep_thread_num(0){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}void Init(){//强关联,绑定,且placeholders自动推导name参数func_t func = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);//绑定for (int i = 0; i < _thread_num; i++){std::string threadname = "thread-" + std::to_string(i + 1);_threads.emplace_back(threadname, func);//内部构造,减少临时拷贝}}void Start(){_isrunning = true;//启动线程池for (auto& thread : _threads){thread.Start();}}void Stop(){LockQueue();_isrunning = false;WakeupAll();UnlockQueue();}void Equeue(const T& in){LockQueue();if (_isrunning)//运行状态才进行操作{_task_queue.push(in);//入队if (_sleep_thread_num > 0)//有线程才唤醒Wakeup();//唤醒}UnlockQueue();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:int _thread_num;std::vector<Thread> _threads;std::queue<T> _task_queue;bool _isrunning;int _sleep_thread_num;//计数休眠线程个数//加锁pthread_mutex_t _mutex;//唤醒pthread_cond_t _cond;
};

Task.hpp

#pragma once/
//方法三:function
/**/
#include <iodtream>
#include <functional>using Task_t = std::function<void()>;//函数对象:返回值为void,参数为空的函数类型对象
//等价
//typedef std::function<void()> Task_t;void Download()
{std::cout << "我是一个下载任务!" << std::endl;
}/
//方法二:类
/*
class Task
{Task(){}Task(int x,int y):_x(x),_y(y){}void Excute(){_result =  _x + _y;}void operator()(){Excute();}std::string debug(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";return msg;}std::string result(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + std::to_string(_result);return msg;}
private:int _x;int _y;int _result;
};
*/
/**/
#pragma once#include<iostream>
#include<functional>// typedef std::function<void()> task_t;
// using task_t = std::function<void()>;// void Download()
// {
//     std::cout << "我是一个下载的任务" << std::endl;
// }// 要做加法
class Task
{
public:Task(){}Task(int x, int y) : _x(x), _y(y){}void Excute(){_result = _x + _y;}void operator ()(){Excute();}std::string debug(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";return msg;}std::string result(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=" + std::to_string(_result);return msg;}private:int _x;int _y;int _result;
};

main.cc

#include "ThreadPool.hpp"
#include "Task.hpp"
//#include <memory>int main()
{//std::unique_ptr<ThreadPool> tp = std::make_unique<ThreadPool>();//C++14 智能指针//ThreadPool<int>* tp = new ThreadPool<int>();ThreadPool<Task>* tp = new ThreadPool<Task>();tp->Init();tp->Start();int cnt = 10;while (cnt--){//不断的向线程池推送任务sleep(1);Task t(1, 1);tp->Equeue(t);sleep(1);std::cout << "cnt" << cnt-- << std::endl;}tp->Stop();std::cout << "thread pool stop" << std::endl;sleep(10);return 0;
}

2、进程池

2.1、定义和基本概念

进程池与线程池类似,但它管理的是一组进程而不是线程。
每个进程都有自己独立的地址空间和资源,因此进程间的通信和同步相对复杂,但提供了更好的隔离性和安全性。

2.2、进程池的特点与优势

1.高隔离性:
每个进程都有自己独立的地址空间和资源,因此进程间的隔离性较好,一个进程的崩溃不会影响其他进程。
2.安全性:
由于进程间的隔离性,进程池可以提供更高的安全性,防止恶意代码或错误操作对其他进程造成影响。
3.适用性强:
对于需要独立运行的任务或需要较高隔离性的场景,进程池是更好

的选择。

2.3、进程池的适用场景:

**1.CPU密集型任务: **
当任务主要是计算密集型时,进程池可以提供更好的性能,因为每个进程都有自己独立的CPU资源,减少了线程间的切换开销和共享资源竞争。
**2.高隔离性需求: **
当任务之间需要较高的隔离性时,如避免一个任务的崩溃影响其他任务时,进程池是更好的选择。
3.安全性要求高的场景:
如处理敏感数据或执行高风险操作时,进程池可以提供更高的安全性保障。

3、单例模式

Linux单例模式是一种常用的软件设计模式,**旨在确保一个类在系统中只有一个实例,并提供一个全局访问点来获取这个实例。**单例模式可以分为饿汉式和懒汉式两种实现方式,它们的主要区别在于实例的创建时机。

3.1、“饿汉”

1.特点:

系统一运行就立即创建实例,实例的创建是线程安全的。
实例在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。

2.实现方式:

饿汉式单例模式通常通过静态初始化块或直接在静态变量初始化时创建实例。这种方式保证了单例的唯一性,并且由于是静态初始化,因此也是线程安全的。

class Singleton {  
private:  static Singleton instance; // 静态实例,类加载时就初始化  // 私有化构造函数和析构函数,防止外部创建和销毁实例  Singleton() {}  ~Singleton() {}  public:  // 提供一个静态方法返回实例  static Singleton& GetInstance() {  return instance;  }  // 禁止拷贝和赋值  Singleton(const Singleton&) = delete;  Singleton& operator=(const Singleton&) = delete;  
};  // 静态实例初始化  
Singleton Singleton::instance;

注意:
在C++中,如果全局对象之间有依赖关系,可能会导致构造顺序问题。此外,由于实例在类加载时就创建,可能会浪费资源,尤其是当实例很大且长时间不被使用时。

3.2、“懒汉”

1.特点:

实例在第一次被使用时创建,即“延时加载”。
在多线程环境下,需要加锁来保证线程安全,否则可能创建多个实例。

2.实现方式:

懒汉式单例模式在第一次调用GetInstance方法时创建实例。为了实现线程安全,可以使用互斥锁(如std::mutex)来保护实例的创建过程。

#include <mutex>  class Singleton {  
private:  static Singleton* instance; // 静态指针,指向类的唯一实例  static std::mutex mtx; // 互斥锁  // 私有化构造函数和析构函数  Singleton() {}  ~Singleton() {}  public:  // 提供一个静态方法返回实例  static Singleton* GetInstance() {  if (instance == nullptr) {  std::lock_guard<std::mutex> lock(mtx); // 加锁  if (instance == nullptr) { // 双重检查锁定  instance = new Singleton();  }  }  return instance;  }  // 禁止拷贝和赋值  Singleton(const Singleton&) = delete;  Singleton& operator=(const Singleton&) = delete;  
};  // 静态成员初始化  
Singleton* Singleton::instance = nullptr;  
std::mutex Singleton::mtx;

注意:
懒汉式单例模式在多线程环境下需要加锁来保证线程安全,但加锁会带来一定的性能开销。此外,懒汉式单例模式在第一次使用实例时才创建,因此可以提高程序启动速度,并减少不必要的资源浪费。但是,如果实例创建过程较为复杂或耗时较长,可能会影响程序的首次响应时间。

4、死锁问题

死锁是指在一组进程中,各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态称为死锁,即: 长时间代码不被推进
Linux死锁问题是一个在多进程或多线程环境中常见的复杂问题,它指的是两个或多个进程(或线程)在执行过程中,因互相持有对方所需的资源而又都等待对方释放资源,导致它们都无法继续执行下去的一种僵局状态。

4.1、死锁的定义

在Linux操作系统中,死锁是指一组进程(或线程)中的每一个进程(或线程)都在等待仅由该组进程中的其他进程(或线程)才能引发的事件,从而导致它们都无法继续执行。这种情况发生时,进程(或线程)将无法进行下去,无法释放资源,也无法获取需要的资源,进而导致系统无法继续运行。

4.2、死锁的产生条件

死锁通常发生在多进程或多线程环境中,当满足以下四个条件时,就可能发生死锁:

1.互斥条件: 一个资源只能被一个进程(线程)访问,即资源独占。
2.占有且等待: 进程(线程)在占有一个资源时,可以请求其他资源。
3.不可剥夺条件: 一个资源只能由其持有者释放,不能强行剥夺。
4.循环等待条件: 多个进程(线程)之间形成一种循环等待资源的关系,每个进程(线程)等待下一个进程(线程)所持有的资源。

4.3、死锁的原因

Linux中产生死锁的原因主要有以下几种:

1.竞争不可抢占资源: 如共享文件时引起死锁,当两个或多个进程都试图访问对方已占用的资源时,就可能发生死锁。
2.竞争可消耗资源: 如消息传递中的死锁,当进程间通过消息传递进行通信时,如果它们都以特定的顺序发送和接收消息,且都先等待接收消息而不发送,就可能发生死锁。
3.进程推进顺序不当: 进程在运行过程中,请求和释放资源的顺序不当,也可能导致死锁。

4.4、死锁的解决方式

为了避免和解决死锁问题,可以采取以下几种方法:

1.预防死锁: 通过设置某些限制条件,以破坏产生死锁的四个必要条件中的一个或几个,来防止发生死锁。例如,通过资源预分配、资源有序性等方法来降低死锁的可能性。
2.避免死锁: 在资源的动态分配过程中,使用某种方法去防止系统进入不安全状态,从而避免了死锁的发生。例如,使用银行家算法来动态检查资源分配的安全性。
3.检测死锁: 允许系统运行过程中发生死锁,但通过系统所设置的检测机构,及时检测出死锁的发生,并精确地确定与死锁有关的进程和资源。
4.解除死锁: 一旦检测到死锁,就采取适当措施从系统中消除死锁。常用的方法是撤销或者挂起一些进程,以便于释放出一些资源,再将它分配给已经处于阻塞的进程,使其转换为就绪状态可以继续运行。

4.5、小结

死锁的四个条件:

1.互斥条件:一个资源每次只能被一个执行流使用
2.请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺;
4.循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。

避免死锁:

1.破坏死锁的四个必要条件
2.加锁顺序一致
3.避免锁未释放的场景
4.资源一次性分配

5、 可重入 VS 线程安全

5.1、可重入

可重入性通常指的是一个函数或程序段在被多个线程(或单个线程多次)调用时,能够正确地执行而不会产生不可预测的结果,即使这些调用是同时进行的(并发执行)

在C/C++等语言中,一个函数如果满足以下条件,则可以被认为是可重入的:

1.不修改全局变量:或者至少是以一种线程安全的方式修改全局变量。
2.不依赖于静态局部变量:因为静态局部变量在函数的不同调用之间保持其值。
3.不调用任何不可重入的函数。

值得注意的是: 可重入性通常关注的是函数内部的状态管理,而不直接涉及多线程间的同步问题。

5.2、线程安全

线程安全指的是一个函数或程序段在多个线程同时执行时,能够正确地执行而不会产生不可预测的结果。

线程安全通常需要确保:

1.对共享资源的访问是同步的:
这通常通过互斥锁(mutexes)、信号量(semaphores)或其他同步机制来实现。
2.避免了竞态条件:
即多个线程尝试同时修改同一资源时可能导致的问题。
3.正确处理了所有可能的并发访问情况。
线程安全的概念比可重入更广泛,因为它涉及到多线程之间的交互和同步。一个线程安全的函数或程序段可能包括不可重入的函数,但通过适当的同步机制来保证整体的线程安全性。

5.3、关系与区别

1.可重入是线程安全的一个必要条件: 一个线程安全的函数或程序段必须是可重入的,但可重入的函数或程序段不一定是线程安全的,特别是当它们访问共享资源时。
2.关注点不同: 可重入性主要关注函数内部的状态管理,而线程安全性则关注多线程之间的交互和同步。

小结:

1.线程安全:
多个线程并发同一段代码时,不会出现不同的结果。
2. 重入:
指对同一个函数被不同的执行流调用,当前一个流程还没完,就有其他的执行流再次进入,我们称之为重入。且一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数。
3.结论:
如果一个函数是可重入的那么,多线程调用这个可重入函数时,多线程也是安全的
反过来多线程能调用可重入。那么整个代码就是安全的。
线程安全不一定是可重入的,可重入函数则一定是线程安全的。

6、volatile关键字

C++中的volatile关键字是一个类型修饰符,用于告诉编译器该变量的值可能会在程序的控制之外被改变。这通常用于多线程编程、硬件访问(如内存映射的I/O寄存器)或中断服务例程中,其中变量的值可能会因为外部事件(如硬件中断或另一个线程的执行)而突然改变。

6.1、volatile关键字的作用

1.防止编译器过度优化:

编译器在编译代码时,会尝试通过优化来提高程序的执行效率。对于它认为在两次读取之间不会改变的变量,编译器可能会将其值缓存在寄存器中,而不是每次都从内存中读取。然而,如果这样的变量被volatile修饰,编译器就会知道这个变量的值可能会突然改变,因此它不会对这个变量进行这样的优化。

2.保证内存可见性:

在多线程环境中,一个线程对volatile变量的修改对其他线程是可见的。然而,需要注意的是,volatile并不保证操作的原子性,也不提供任何形式的同步机制(如互斥锁)。

6.2、volatile样例

下面是一个简单的C++多线程示例,其中使用了volatile关键字来确保一个线程能够检测到另一个线程对共享变量的修改。但是,请注意,这个示例并不是最佳实践,因为volatile并不足以保证线程安全。在实际应用中,应该使用更高级的同步机制,如互斥锁(std::mutex)或原子操作(std::atomic)。

#include <iostream>  
#include <thread>  
#include <chrono>  volatile bool running = true; // 使用volatile修饰共享变量  void threadFunction() {  while (running) {  // 执行一些操作  std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟工作  }  std::cout << "Thread has stopped." << std::endl;  
}  int main() {  std::thread t(threadFunction);  // 做一些工作  std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟主线程的工作  running = false; // 通知线程停止  t.join(); // 等待线程结束  return 0;  
}

在这个例子中,running变量被声明为volatile,以确保当主线程将其设置为false时,这个修改对threadFunction中的线程是可见的。然而,由于volatile不保证操作的原子性,如果running的读取和写入不是原子的(在大多数现代处理器上,单个字节的读写通常是原子的,但这不是由C++标准保证的),那么仍然可能会遇到竞态条件。
此外,即使running的读取和写入是原子的,volatile也不足以防止指令重排序等更复杂的并发问题。因此,在需要确保线程安全的情况下,应该使用更强大的同步机制。
注意:
在C++11及更高版本中,推荐使用std::atomic来代替volatile进行线程间的同步和通信。std::atomic提供了必要的原子性和内存顺序保证,以安全地在多线程环境中使用共享变量。
volatile主要用于与硬件直接交互的场景,或者当你确信编译器优化会导致问题时。在大多数多线程编程场景中,应该优先考虑使用std::atomic、std::mutex、std::condition_variable等同步机制

7、日志的重要性

Linux日志是Linux系统中非常重要的组成部分,它们记录了系统的运行情况、错误信息、用户活动等重要信息,对于系统管理和故障排查具有不可替代的作用。

7.1、日志文件概述

Linux系统中的日志文件通常存储在/var/log目录下,该目录下包含了多种不同类型的日志文件,用于记录不同类型的信息。例如,/var/log/messages记录了系统的各种事件和错误信息,/var/log/secure记录了用户的登录、认证和授权信息,/var/log/httpd/access_log和/var/log/httpd/error_log则分别记录了Apache
HTTP服务器的访问日志和错误日志等。

7.2、常见日志文件及作用

1.系统日志

nameValue
/var/log/messages记录了系统的各种事件和错误信息,包括系统启动、登录、网络连接、内核错误等
/var/log/dmesg记录了系统引导过程中的信息,包括硬件检测、内核启动等
/var/log/boot.log记录了系统在引导过程中发生的事件,与/var/log/dmesg类似,但可能包含更详细的启动信息

2.用户日志

nameValue
/var/log/secure记录了用户的登录、认证和授权信息,包括成功和失败的登录尝试、sudo或su命令的使用等
/var/log/lastlog记录了每个用户最后一次成功登录的信息,如登录时间、IP地址等
/var/log/wtmp记录了用户登录、注销、系统启动等信息,可以使用last命令查看信息
/var/log/btmp记录了失败的登录尝试信息,可以使用lastb命令查看

3.程序日志

应用程序通常会将日志信息写入到/var/log目录下的特定文件中,文件名和位置取决于应用程序的配置。例如,Apache HTTP服务器的访问日志和错误日志分别存储在/var/log/httpd/access_log和/var/log/httpd/error_log中。
其他常见的程序日志包括Nginx的/var/log/nginx/access.log和/var/log/nginx/error.log,MySQL的/var/log/mysql/error.log等。

4.安全日志

nameValue
/var/log/audit/audit.log记录了安全事件、安全漏洞、攻击尝试等信息,需要启用审计服务(auditd)才能生成此日志文件

7.3、日志管理工具

Linux系统提供了多种日志管理工具,用于帮助用户查看、分析和管理日志文件。

常用的工具有:

nameValue
tail用于查看文件的末尾内容,特别是与-f选项结合使用时,可以实时监控日志文件的更新
cat用于显示整个文件的内容,虽然对于大型日志文件来说可能不太实用,但在处理小型文件或需要查看整个文件内容时非常有用
grep用于在文件中搜索包含特定模式的行,是日志分析中最常用的命令之一
less提供了一个分页查看文件内容的界面,允许用户向前和向后浏览文件,搜索文本等
awk一个强大的文本处理工具,可以用于格式化文本文件和提取数据
sed一个流编辑器,用于对文本文件进行基本的文本转换

7.4、日志的查看与分析

在Linux系统中,查看和分析日志文件的常用方法包括:

1.使用tail、cat、grep等命令直接查看日志文件的内容。
2.使用less或more命令分页查看大型日志文件的内容。
3.使用awk、sed等文本处理工具对日志文件进行格式化、提取和转换操作。
4.结合使用多个命令和管道符(|),实现复杂的日志分析和过滤操作。

7.5、简易日志的实现

获取时间的函数:

 int gettimeofday(struct timeval* tv,struct timezone* tz);int settimeofday(const struct timeval* tv,const struct timezone* tz);
 time_t time(time_t* tloc);//time_t时间戳

转换时间戳为年月日制:

  struct tm* loacltime(const time_t* timep);

处理可变参数常用:vsnprintf

 #include <stdarg.h>int vsnprintf(char* str,size_t size,const char* format,va_list ap);

补充:
实参传递到形参,是把形参入栈,从有向左入栈的。
比如可变参数的样例。
va_list ap;就是可变参数指针
va_start(ap,num);初始化,num是距离可变参数左边最近的参数

ca_end(ap);释放

Log.hpp程序实现

#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include "LockGuard.hpp"namespace log_ns
{enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level)//日志等级{switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetCurrTime()//获取时间{time_t now = time(nullptr);struct tm* curr_time = localtime(&now);//转时间制char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class logmessage{public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;// log.logMessage("", 12, INFO, "this is a %d message ,%f, %s hellwrodl", x, , , );class Log{public:Log(const std::string& logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage& lg)//显示器显示{printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage& lg)//文件显示{std::ofstream out(_logfile, std::ios::app);//文件流,且追加细节if (!out.is_open())return;char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());out.write(logtxt, strlen(logtxt));//写入out.close();}void FlushLog(const logmessage& lg){// 加过滤逻辑 --- TODO,筛选等级,宏参/条件编译等处理LockGuard lockguard(&glock);//加锁兼容多线程switch (_type)//类型选择{case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void logMessage(std::string filename, int filenumber, int level, const char* format, ...){logmessage lg;lg._level = LevelToString(level);//日志等级lg._id = getpid();//pidlg._filename = filename;//文件名lg._filenumber = filenumber;//行号lg._curr_time = GetCurrTime();//时间va_list ap;//可变参数处理va_start(ap, format);//可变参数初始化char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);//可变参数格式化转为字符串放入指定log_info中va_end(ap);//销毁lg._message_info = log_info;// 打印出来日志FlushLog(lg);}~Log(){}private:int _type;//打印目标的类型,1.屏幕 2.文件std::string _logfile;};Log lg;//优化,日志输出格式,VA_ARGS()/__VA_ARGS__属于C99语法,作用是让宏支持可变参数,//并在此之前加两个##表示兼容没有可变参数的情况。自动去掉前面的逗号。匹配只需要的参数即可
#define LOG(Level, Format, ...)                                        \do                                                                 \{                                                                  \lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \} while (0)
#define EnableScreen()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
};

7.6、日志的维护与优化

由于日志文件可能会不断增长并占用大量磁盘空间,因此需要对它们进行定期的维护和优化。

常用的方法包括:

1.使用logrotate工具定期压缩、归档和删除旧的日志文件。
2.配置日志文件的轮转策略,如按天、周或月进行轮转。
3.设置日志文件的大小限制,当文件达到指定大小时自动进行轮转。
4.清理不必要的日志文件,以释放磁盘空间。

相关文章:

Linux的线程篇章 - 线程池、进程池等知识】

Linux学习笔记---018 Linux之线程池、进程池知识储备1、线程池1.1、池化技术1.1.1、定义与原理1.1.2、优点1.1.3、应用场景 1.2、线程池的特点与优势1.3、线程池的适用场景1.4、线程池的实现 2、进程池2.1、定义和基本概念2.2、进程池的特点与优势2.3、进程池的适用场景&#x…...

汇昌联信做拼多多运营正规吗?

汇昌联信在拼多多平台上的运营是否正规&#xff0c;是许多商家和消费者都关心的问题。随着电商行业的快速发展&#xff0c;平台运营的正规性直接关系到消费者的购物体验和商家的信誉。 一、公司背景与资质审核 明确回答问题&#xff1a;汇昌联信作为一家专业的电商运营公司&…...

240810-Gradio自定义Button按钮+事件函数+按钮图标样式设定

A. 最终效果 B. 参考代码 要通过自定义HTML按钮来触发Gradio自带按钮的 click 函数&#xff0c;你可以使用JavaScript来模拟点击Gradio的按钮。这里是一个示例代码&#xff0c;展示了如何实现这一点&#xff1a; import gradio as gr# 自定义的 JavaScript&#xff0c;用于捕…...

排序算法--快速排序

一、三色旗问题 问题描述 有一个数组是只由0&#xff0c;1&#xff0c;2三种元素构成的整数数组&#xff0c;请使用交换、原地排序而不是计数进行排序&#xff1a; 解题思路 1.定义两个变量&#xff0c;i和j&#xff08;下标&#xff09;&#xff0c;从index0开始遍历 2.如…...

springMVC -- 学习笔记

前言简介演示 ⇒ &#xff08;最简单的原理&#xff0c;开发并不这样&#xff0c;理解一下就好&#xff09;演示 ⇒ 接近真实注解开发&#xff08;好好理解一下&#xff09;重要的源码献上 Controller 讲解RequestMapper ⇒ 没啥记的&#xff0c;第一个案例看看就行了RestFul 风…...

修复本地终端(windows)连接服务器使用zsh出现乱跳的问题

目前市面上还没有发现解决方案&#xff0c;记录一下&#xff01; 1.起因&#xff1a; 在服务器配置了zsh后&#xff0c;用本地的windows去连接的时候&#xff0c;终端内容会出现乱跳&#xff0c;比如输入了一个“l”&#xff0c;后面出现多个“lll”&#xff0c;如下: ⚡ roo…...

【扒代码】regression_head.py

import torch from torch import nnclass UpsamplingLayer(nn.Module):# 初始化 UpsamplingLayer 类def __init__(self, in_channels, out_channels, leakyTrue):super(UpsamplingLayer, self).__init__() # 调用基类的初始化方法# 初始化一个序列模型&#xff0c;包含卷积层、…...

vue2 使用axios 请求后台返回文件流导出为excel

目录 步骤 1: 安装 Axios 步骤 2: 创建 Axios 实例 步骤 3: 发起请求并处理文件流 说明 步骤 1: 安装 Axios 首先&#xff0c;确保项目中已经安装了 Axios。如果没有&#xff0c;可以通过以下命令进行安装&#xff1a; npm install axios 步骤 2: 创建 Axios 实例 为了更…...

MATLAB数据可视化:在地图上画京沪线的城市连线

matlab自带的geoplot(lat,lon) 可以在地理坐标中绘制线条。使用 lat和lon分别指定以度为单位的经度和纬度坐标。 绘制京沪线所经城市线条&#xff1a; citys [116.350009,39.853928; 116.683546,39.538304; 117.201509,39.085318; 116.838715,38.304676;...116.359244,37.436…...

【AI】CV基础1

定期更新&#xff0c;建议关注更新收藏。 本站友情链接&#xff1a; OCR篇1 可变形卷积Deformable Conv opencv-python形态学操作合集 目录 仿射变换图像二阶导数本质探讨PIL通道、模式、尺寸、坐标系统、调色板、信息滤波器实现图像格式转换 OpenCV轮廓提取 仿射变换 仿射变换…...

数据结构《栈》

数据结构《栈》 1、栈的概念及结构2、栈的实现3、练习: 1、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&…...

说一说mysql的having?和where有什么区别?

在 MySQL 中&#xff0c;HAVING 子句和 WHERE 子句都是用于过滤查询结果的&#xff0c;但它们之间有一些重要的区别。下面我将详细介绍这两个子句的区别以及它们的使用场景。 1. HAVING 子句 作用: HAVING 子句用于过滤聚合后的结果集。它通常与 GROUP BY 子句一起使用&#x…...

LeetCode45. 跳跃游戏 II

题目链接&#xff1a; 45. 跳跃游戏 II - 力扣&#xff08;LeetCode&#xff09; 思路分析&#xff1a;这属于上一题的变种&#xff0c;思路有所不同&#xff0c;要用到贪心的思想。从第一步开始&#xff0c;在可以跳跃的范围内&#xff0c;选择能够到达最远位置的点将其作为…...

算法打卡 Day19(二叉树)-平衡二叉树 + 二叉树的所有路径 + 左叶子之和 + 完全二叉树的节点个数

Leetcode 101-平衡二叉树 文章目录 Leetcode 101-平衡二叉树题目描述解题思路 Leetcode 257-二叉树的所有路径题目描述解题思路 Leetcode 404-左叶子之和题目描述解题思路 Leetcode 222-完全二叉树的节点个数题目描述解题思路 题目描述 https://leetcode.cn/problems/balanced…...

国际以太网专线 (IEPL)/国际专线(IPLC)-全球覆盖,无界沟通

中国联通国际公司产品&#xff1a;国际以太网专线 (IEPL)/国际专线&#xff08;IPLC&#xff09;—— 跨境数据传输的坚实桥梁 在全球化日益加深的今天&#xff0c;跨境、跨地域的数据传输需求激增&#xff0c;企业对数据传输的速度、安全性和稳定性提出了前所未有的高要求。中…...

信息安全管理知识体系攻略(至简)

信息安全管理知识体系主要包括信息安全管理体系、信息安全策略、信息安全系统、信息安全技术体系等。 一、信息安全管理 1、信息安全管理体系&#xff08;ISMS&#xff09;。ISO27001是国际标准化组织&#xff08;ISO&#xff09;和国际电工委员会&#xff08;ICE&#xff09…...

HCIE学习笔记:IPV6 地址、ICMP V6、NDP 、DAD (更新补充中)

系列文章目录 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、IPV4、IPv6包头对比1. IPV4包头2.IPv6包头3.IPV6扩展包头 二、IPV6基础知识地址结构、地址分类三、ICMPV4、ICMPV61、 lnternet控…...

人工智能】Transformers之Pipeline(九):物体检测(object-detection)

目录​​​​​​​ 一、引言 二、物体检测&#xff08;object-detection&#xff09; 2.1 概述 2.2 技术原理 2.3 应用场景 2.4 pipeline参数 2.4.1 pipeline对象实例化参数 2.4.2 pipeline对象使用参数 2.4 pipeline实战 2.5 模型排名 三、总结 一、引言 pipel…...

[SWPUCTF 2021 新生赛]easy_md5

分析代码&#xff1a;1.包含flag2.php 2.GET传name&#xff0c;POST传password $name ! $password && md5($name) md5($password) 属于MD5绕过中的php 弱类型绕过 解题方法: 方法一 import requests# 网站的URL url "http://node7.anna.nssctf.cn:28026&q…...

Redis面试题大全

文章目录 Redis有哪几种基本类型Redis为什么快&#xff1f;为什么Redis6.0后改用多线程?什么是热key吗&#xff1f;热key问题怎么解决&#xff1f;什么是热Key&#xff1f;解决热Key问题的方法 什么是缓存击穿、缓存穿透、缓存雪崩&#xff1f;缓存击穿缓存穿透缓存雪崩 Redis…...

【langchain学习】BM25Retriever和FaissRetriever组合 实现EnsembleRetriever混合检索器的实践

展示如何使用 LangChain 的 EnsembleRetriever 组合 BM25 和 FAISS 两种检索方法&#xff0c;从而在检索过程中结合关键词匹配和语义相似性搜索的优势。通过这种组合&#xff0c;我们能够在查询时获得更全面的结果。 1. 导入必要的库和模块 首先&#xff0c;我们需要导入所需…...

【C语言】预处理详解(上)

文章目录 前言1. 预定义符号2. #define 定义常量3. #define定义宏4. 带有副作用的宏参数5. 宏替换的规则 前言 在讲解编译和链接的知识点中&#xff0c;我提到过翻译环境中主要由编译和链接两大部分所组成。 其中&#xff0c;编译又包括了预处理、编译和汇编。当时&#xff0c…...

uni-app内置组件(基本内容,表单组件)()二

文章目录 一、 基础内容1.icon 图标2.text3.rich-text4.progress 二、表单组件1.button2.checkbox-group和checkbox3.editor 组件4.form5.input6.label7.picker8.picker-view 和 picker-view-column9.radio-group 和 radio10.slider11.switch12.textarea 一、 基础内容 1.icon…...

linux搭建redis超详细

1、下载redis包 链接: https://download.redis.io/releases/ 我以7.0.11为例 2、上传解压 mkdir /usr/local/redis tar -zxvf redis-7.0.11.tar.gz3、进入redis-7.0.11&#xff0c;依次执行 makemake install4、修改配置文件redis.conf vim redis.conf为了能够远程连接redis…...

Flink-DataWorks第二部分:数据集成(第58天)

系列文章目录 数据集成 2.1 概述 2.1.1 离线&#xff08;批量&#xff09;同步简介 2.1.2 实时同步简介 2.1.3 全增量同步任务简介 2.2 支持的数据源及同步方案 2.3 创建和管理数据源 文章目录 系列文章目录前言2. 数据集成2.1 概述2.1.1 离线&#xff08;批量&#xff09;同步…...

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…...

javaweb_07:分层解耦

一、三层架构 &#xff08;一&#xff09;基础 在请求响应中&#xff0c;将代码都写在controller中&#xff0c;看起来内容很复杂&#xff0c;但是复杂的代码总体可以分为&#xff1a;数据访问、逻辑处理、接受请求和响应数据三个部分。在程序中我们尽量让一个类或者一个方法…...

调用 Python 开源库,获取油管英文视频的手动或自动英文srt字幕,以及自动中文简体翻译srt字幕

前提条件 非常抱歉&#xff0c;这个程序就是个雏形&#xff0c;非常不完善&#xff0c;输入需要手动编辑&#xff0c;凑活着可以用&#xff0c;请自己完善吧。 开源声明&#xff1a;此文代码引用了一个开源MIT License的Python库&#xff0c;其他代码是本人自写自用。你可以随…...

UDP协议实现通信与数据传输(创建客户端和服务器)

目录 一、UDP &#xff08;传输层&#xff0c;用户数据报协议&#xff09; 二、服务器Server的创建 三、客户端Client的创建 四、效果实现&#xff08;描述&#xff09; 一、UDP &#xff08;传输层&#xff0c;用户数据报协议&#xff09; UDP&#xff08;User Datagram Pr…...

【红黑树】

红黑树 小杨 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&am…...