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

ring_log环形日志-6M缓冲区_proc接口

文章目录

    • log_tools.c
    • log.c
    • spin_lock
    • seq_puts
    • seq_read
    • seq_write
    • single_open
    • makefile
    • test.sh
    • 测试:
      • 运行./test.sh
      • 读取日志
      • 插入日志
      • echo cat测试
    • 参考:

log_tools.c


#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>


#define MYLOG_PROC_PATH            "/proc/mylog"
#define TMP_BUF_SIZE             1024
#define BUF_SIEZ                 256
#define PER_LOG_SIZE            128

#define LOG_LEVEL_EMERG            1
#define LOG_LEVEL_ALERT            2
#define LOG_LEVEL_CRIT            3
#define LOG_LEVEL_ERR            4
#define LOG_LEVEL_WARNING        5
#define LOG_LEVEL_NOTICE        6
#define LOG_LEVEL_INFO            7
#define LOG_LEVEL_DEBUG            8

static FILE *fp;

static void print_help(void)
{
    printf("-H     --help        显示帮助\n");
    printf("-h     --help        显示帮助\n");
    printf("-r  --readall    读取日志\n");
    printf("-i  --insert    插入日志\n");
    printf("-i  <log_level> <log_message>\n");
}
void print_log_level(void)
{
    printf("log level must be range 1 to 8\n");
    printf("LOG_LEVEL_EMERG            1\n");
    printf("LOG_LEVEL_ALERT            2\n");
    printf("LOG_LEVEL_CRIT            3\n");
    printf("LOG_LEVEL_ERR            4\n");
    printf("LOG_LEVEL_WARNING        5\n");
    printf("LOG_LEVEL_NOTICE        6\n");
    printf("LOG_LEVEL_INFO            7\n");
    printf("LOG_LEVEL_DEBUG            8\n");
}

int insert_log(int argc,char const *argv[])
{
    const char *log_message = NULL;
    int log_level = -1;
    char tmpbuf[TMP_BUF_SIZE] = {0};

    if(argc != 4) {
        printf("Usage: %s -i <log_level> <log_message>\n",argv[0]);
        goto ERROR;
    }

    log_level = atoi(argv[2]);
    if (log_level < 1 || log_level > LOG_LEVEL_DEBUG) {
        printf("error log_level");
        goto ERROR;
    }

    log_message = argv[3];
    if(strlen(log_message) > PER_LOG_SIZE)
    {
        goto ERROR;
    }

    fseek(fp,0,SEEK_END);
    sprintf(tmpbuf,"%d %s\n",log_level,log_message);
    if(fwrite(tmpbuf,strlen(tmpbuf),1,fp) < 0) {
        perror("fwrite");
    }
    printf("insert %d %s\n",log_level,log_message);

ERROR:
    return 0;
}
int read_all_log(void)
{
    char buf[BUF_SIEZ] = {0};
    fseek(fp,0,SEEK_SET);
    while (fgets(buf, sizeof(buf), fp)) {
        printf("%s", buf);
    }
    return 0;
}

int main(int argc, char const *argv[])
{
    int res = -1;
    int index = -1;

    struct option argarr[] = {
        {"read",0,NULL,'r'},
        {"insert",1,NULL,'i'},
        {"help",0,NULL,'h'},
        {"help",0,NULL,'H'},
        {NULL, 0, NULL, 0}
    };

    if(argc < 2) {
        print_help();
        goto ERROR;
    }

     fp = fopen("/proc/mylog", "r+");
    if (!fp) {
        printf("Failed to open /proc/mylog\n");
        goto ERROR;
    }

    res = getopt_long(argc,(char **)argv,"H:i:h:r",argarr,&index);
    if(res < 0) {
        goto ERROR;
    }

    switch(res)
    {
        case 'r':
            if (fp != NULL) {
                read_all_log();
            } else {
                printf("Failed to open /proc/mylog\n");
            }
            break;
        case 'i':
            if (optarg) {
                insert_log(argc,argv);
            } else {
                printf("Error: no log message specified\n");
                print_help();
            }
            break;
        case 'h':
            print_help();
            break;
        case 'H':
            print_help();
            print_log_level();
            break;
        default:
            printf("Unknown option: %c\n", optopt);
            print_help();
            break;
    }
    
ERROR:
    if(fp != NULL) {
        fclose(fp);
        fp = NULL;
    }
    return 0;
}

log.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/seq_file.h>

#define LOG_PROC_NAME   "mylog"
#define LOG_BUF_SIZE    (6 * 1024 * 1024)
#define PER_LOG_SIZE    128
#define STR_TIME_LEN    32

struct log_entry {
    struct list_head list;
    char *msg;
    size_t len;
};

struct log_buffer {
    struct list_head head;
    size_t size;
    size_t used;
};

static struct log_buffer buffer;
static spinlock_t log_buf_lock;

static ssize_t log_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
    char *msg = NULL;
    struct log_entry *entry = NULL;
    size_t msg_pos = 0;
    size_t len = 0;
    size_t i = 0;
    struct timespec ts = {
        .tv_sec = 0,
        .tv_nsec = 0
    };

    if(count + STR_TIME_LEN > PER_LOG_SIZE) {
        // count = PER_LOG_SIZE;
        goto ERROR;
    }

    msg = kmalloc(count + STR_TIME_LEN + 1, GFP_KERNEL);
    if (!msg) {
        goto ERROR;
    }

    memset(msg,0,count + STR_TIME_LEN + 1);
    ktime_get_ts(&ts);
    sprintf(msg, "[%ld.%09ld] ", ts.tv_sec, ts.tv_nsec);
    msg_pos = 0;
    msg_pos += strlen(msg);

    if (copy_from_user(msg + msg_pos, buf, count)) {
        goto COPY_ERROR;
    }

    msg[count + STR_TIME_LEN] = '\0';
    len = 0;
    len = strlen(msg) + 1;

    if (buffer.used + len > buffer.size) {
        i = buffer.used;
        while(buffer.used > i/2) {
            entry = list_first_entry(&buffer.head, struct log_entry, list);
            buffer.used -= entry->len;
            list_del(&entry->list);
            kfree(entry->msg);
            entry->msg = NULL;
            kfree(entry);
            entry = NULL;
        }
    }
    
    entry = NULL;
    entry = kmalloc(sizeof(struct log_entry), GFP_KERNEL);
    if (!entry) {
        goto COPY_ERROR;
    }

    entry->msg = msg;
    entry->len = len;
    list_add_tail(&entry->list, &buffer.head);
    buffer.used += len;

    return count;

COPY_ERROR:
    if(msg != NULL) {
        kfree(msg);
        msg = NULL;
    }
ERROR:
    return -1;

}
static int log_list_show(struct seq_file *m, void *v)
{
    struct log_entry *entry = NULL;
    struct log_entry *n = NULL;

    spin_lock(&log_buf_lock);
    list_for_each_entry_safe(entry,n,&buffer.head,list) {
        seq_printf(m, "%s", entry->msg);
    }
    spin_unlock(&log_buf_lock);

    return 0;
}

static int log_list_open(struct inode *inode, struct file *file)
{
    return single_open(file, log_list_show, NULL);
}


static const struct file_operations log_fops = {
    .owner = THIS_MODULE,
    .open = log_list_open,
    .read = seq_read,
    .write = log_write,
};

static int __init log_init(void)
{
    INIT_LIST_HEAD(&buffer.head);
    buffer.size = LOG_BUF_SIZE;
    buffer.used = 0;

    if (!proc_create(LOG_PROC_NAME, 0666, NULL, &log_fops)) {
        printk(KERN_ERR "Failed to create /proc/%s\n", LOG_PROC_NAME);
        return -ENOMEM;
    }

    spin_lock_init(&log_buf_lock);
    printk(KERN_INFO "log module loaded\n");

    return 0;
}

static void __exit log_exit(void)
{
    struct log_entry *entry = NULL;
    struct list_head *pos = NULL, *next = NULL;

    list_for_each_safe(pos, next, &buffer.head) {
        entry = list_entry(pos, struct log_entry, list);
        list_del(&entry->list);
        kfree(entry->msg);
        kfree(entry);
    }

    remove_proc_entry(LOG_PROC_NAME, NULL);

    printk(KERN_INFO "log module unloaded\n");
}

module_init(log_init);
module_exit(log_exit);
MODULE_LICENSE("GPL");

spin_lock

spin_lock是Linux内核中的一种自旋锁,用于保护共享资源的访问。与mutex不同,spin_lock在获取锁时并不会进入睡眠状态,而是使用循环不断尝试获取锁,直到获取到锁才会退出循环,因此也称为自旋锁。

spin_lock的使用方法如下:

  1. 在需要保护的代码段前调用spin_lock函数获取锁。

  2. 在代码段结束时调用spin_unlock函数释放锁。

seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c。seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的只读文件中大量使用了seq函数处理。

由于procfs的默认操作函数只使用一页的缓存,在处理较大的proc文件时就有点麻烦,并且在输出一系列结构体中的数据时也比较不灵活,需要自己在read_proc函数中实现迭代,容易出现Bug。所以内核黑客们对一些/proc代码做了研究,抽象出共性,最终形成了seq_file(Sequence file:序列文件)接口。 这个接口提供了一套简单的函数来解决以上proc接口编程时存在的问题,使得编程更加容易,降低了Bug出现的机会。

在需要创建一个由一系列数据顺序组合而成的虚拟文件或一个较大的虚拟文件时,推荐使用seq_file接口。但是我个人认为,并不是只有procfs才可以使用这个seq_file接口,因为其实seq_file是实现的是一个操作函数集,这个函数集并不是与proc绑定的,同样可以用在其他的地方。

seq_puts

是Linux内核中的一个函数,用于将字符串写入序列文件的缓冲区。序列文件是用于顺序访问数据流的特殊文件。seq_puts函数接受两个参数:指向序列文件的指针(m)和要写入文件的字符串。

seq_read

是一个Linux内核函数,用于从序列文件中读取数据并将其放入用户空间缓冲区中

seq_write

是一个Linux内核中的函数,它用于将数据写入到顺序访问的设备中

seq_write 是一个辅助函数,用于在 proc 文件系统中创建一个支持顺序写入的文件。它的原型定义如下:

ssize_t seq_write(struct seq_file *, const char *, size_t);

第一个参数是一个指向 seq_file 的指针,第二个参数是一个指向数据缓冲区的指针,第三个参数是数据的长度。

seq_write 函数的返回值是一个 ssize_t 类型的整数,表示写入的字节数。如果返回值小于 0,则表示写入失败。

在创建一个支持顺序写入的 proc 文件时,可以使用 seq_write 函数来向文件中写入数据。通常需要在文件的 write 函数中调用 seq_write 函数来实现写入操作。

single_open

single_open 是一个辅助函数,用于在 proc 文件系统中创建一个仅支持顺序读取的文件。它的原型定义如下:

int single_open(struct file , int ()(struct seq_file *, void *), void *);

第一个参数是一个指向文件的指针,第二个参数是一个回调函数,用于向 seq_file 中写入数据,第三个参数是一个指向私有数据的指针,可用于传递一些上下文信息给回调函数。

single_open 函数的返回值是一个整数,表示函数执行的状态。如果返回值小于 0,则表示创建文件失败,否则表示创建文件成功。

在创建一个支持顺序读取的 proc 文件时,通常可以使用 single_open 函数来创建文件,然后将回调函数和私有数据传递给它。在回调函数中,可以使用 seq_puts、seq_printf 等函数向 seq_file 中写入数据。最后,使用 single_release 函数来释放资源。

makefile

obj-m := log.o     # ubuntu16


CC=gcc

LD=ld

KDIR := /usr/src/linux-headers-$(shell uname -r)

PWD := $(shell pwd)



all:

	$(MAKE) -C $(KDIR) M=$(PWD) modules CC=$(CC) LD=$(LD)

 

clean:

	$(MAKE) -C $(KDIR) M=$(PWD) clean

test.sh

#!/bin/sh


 

sudo make



sudo rmmod log.ko

sudo insmod log.ko

 

echo 1 2 > /proc/mylog

cat /proc/mylog


./test.sh

测试:

在这里插入图片描述

运行./test.sh

读取日志

在这里插入图片描述

插入日志

在这里插入图片描述

echo cat测试

参考:

linux 在 /proc 里实现文件 - 樊伟胜 - 博客园 (cnblogs.com)

https://www.cnblogs.com/fanweisheng/p/11141527.html

linux内核seq_file接口

https://www.cnblogs.com/embedded-linux/p/9751995.html


http://www.kler.cn/a/2644.html

相关文章:

  • WEB前端-2
  • 68.基于SpringBoot + Vue实现的前后端分离-心灵治愈交流平台系统(项目 + 论文PPT)
  • VSCode Live Server 插件安装和使用
  • 单元测试MockitoExtension和SpringExtension
  • 【Vue】:解决动态更新 <video> 标签 src 属性后视频未刷新的问题
  • springboot + vue+elementUI图片上传流程
  • Linux上用Samba建立共享文件夹并通过Linux测试
  • 蓝桥杯每日一真题——[蓝桥杯 2021 省 B] 杨辉三角形(二分+规律)
  • oracle查询表空间大小以及每个表所占空间的大小
  • 已解决AttributeError:module tensorflow no attribute app异常的正确解决方法,亲测有效!!!
  • HAL库 STM32 串口通信
  • 使用STM32F103ZE开发贪吃蛇游戏
  • 把python开发的web服务,打包成docker镜像的方法
  • 算法基础-回溯算法
  • 什么是Nginx
  • 【从零开始的C语言】操作符详解
  • css总结9(过渡和2D变换)
  • C语言手撕一个Hash表(HashTable)
  • Redis缓存穿透、击穿、雪崩问题及解决方法
  • 算法训练营第五十九天|LeetCode647、516
  • JavaSE进阶之(十六)枚举
  • 项目文章 | 缓解高胆固醇血症 ,浒苔多糖如何相助?
  • 【系统学习】环境土壤物理模型HYDRUS1D/2D/3D
  • 原力计划来了【协作共赢 成就未来】
  • <C++> 类和对象(下)
  • Java四种内部类(看这一篇就够了)