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

一个日志输出系统的设计

本文介绍一个基于linux进程调度策略来调度的信息输出系统,多条信息可以显示多次,每次的显示时间也可以设置:
以下是头文件定义loglet.h:
#include <pthread.h>
#define MAX_PRIO 8
#define MAX_LENGTH 512
static inline void __set_bit(int nr, volatile unsigned long * addr)
{
...
}
static inline void clear_bit(int nr, volatile unsigned long * addr)
{
...
}
static inline void __clear_bit(int nr, volatile unsigned long * addr)
{
...
}
struct list_head {
...
};
#define list_entry(ptr, type, member) \
...
static inline void INIT_LIST_HEAD(struct list_head *list)
{
...
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
...
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
...
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
...
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
...
}
static inline void list_del(struct list_head *entry)
{
...
}
static inline int list_empty(const struct list_head *head)
{
...
}
struct prio_array {
...
};
//log_buff定义一条日志任务
struct log_buff {
char buf[MAX_LENGTH]; //实际需要展示的数据
int prio; //这个日志任务的优先级
int times; //这个任务目前为止显示的次数
int sed_slice; //持续多久被调度出去
int need_go; //是否被抢占
int remove_flag; //是否应该被删除
int alive_times; //这个任务需要显示的次数
int blink; //每次显示持续的时间
struct list_head tasks; //全局链表
struct prio_array *array; //目前属于哪个数组,活跃还是过期?
struct list_head run_list; //加入链表的锚
};
//log_queue定义一个全局的队列
struct log_queue {
unsigned long nr_running; //一共有多少任务
unsigned long long last_tick; //上次调度的时间
struct log_buff *idle; //它的idle任务,没事干的时候显示
struct prio_array *active;, *expired, arrays[2]; //活跃数组/过期数组
void (*handler_init)(void *argv); //输出处理初始化回调函数,比如初始化网络,LCD屏幕等等
void (*handler_process)(void *argv, int blink); //输出处理函数,比如写LCD屏幕等
int (*prio2times)(int prio); //优先级转化为时间片的函数
pthread_mutex_t queue_mutex;

};
//以下定义一系列导出函数
//全局初始化
int global_init();
//以下三个设置log_queue的回调函数
void set_handle_process(void (*handler_process)(void *argv, int blink));
void set_handle_init(void (*handler_init)(void *argv), void *arg);
void set_prio2times(int (*prio2times)(int prio));
//设置调度间隔
int set_interval(int val);
void set_idle(const char *info);
int insert_log(const char *info, int prio, int alives, int blink);
--END--
以下是实现文件代码:
#include "log_let.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

struct log_buff *current = NULL;
struct log_buff *prev = NULL;
unsigned long long tick = 0;
struct log_queue *global_rq;

//从一个链表中将一个日志退出
static void dequeue_log(struct log_buff *p, struct prio_array *array)
{
array->nr_active--;
list_del(&p->run_list);
if (list_empty(array->queue + p->prio))
__clear_bit(p->prio, array->bitmap);
}
//将一条日志加入一个链表
static void enqueue_log(struct log_buff *p, struct prio_array *array)
{
struct list_head *lh = &global_rq->active->queue[p->prio];
list_add_tail(&p->run_list, &array->queue[p->prio]);
__set_bit(p->prio, array->bitmap);
array->nr_active ++;
p->array = array;

}
//得到默认队列
struct log_queue *get_queue()
{
return global_rq;
}

void remove_log(struct log_buff *buf)
{
buf->remove_flag = 1;
}
//信号处理函数
void time_tick(void)
{
unsigned long long now = tick ++;
struct log_buff *p = current;
struct log_queue *rq = get_queue();//global_rq;

rq->last_tick = now;
//如果是idle日志,不处理
//如果还没有任何日志,不处理
//如果和前一次处理的一样,不重复处理
if (p == rq->idle || !p || p == prev) {
return;
}
prev = p;
//如果时间片耗尽,则要么加入到“过期数组”,要么被删除
if (!--p->sed_slice) {
p->need_go = 1;
//如果此条日志需要显示的次数到了,那么就要删除它了
if (p->alive_times > 0 && !--p->alive_times) {
remove_log(p); //仅仅设置一个删除标志
return;
}
//将一个“过期”的日志加入到一个“过期数组”中,注意需要锁操作
pthread_mutex_lock(&rq->queue_mutex);
dequeue_log(p, rq->active);
pthread_mutex_unlock(&rq->queue_mutex);
//重新设置显示“时间片”信息
p->sed_slice = rq->prio2times(p->prio);
pthread_mutex_lock(&rq->queue_mutex);
enqueue_log(p, rq->expired);
pthread_mutex_unlock(&rq->queue_mutex);
}
}
//激活一条日志任务
static void __activate_log(struct log_buff *p, struct log_queue *rq)
{
struct prio_array *target = rq->active;
pthread_mutex_lock(&rq->queue_mutex);
enqueue_log(p, target);
pthread_mutex_unlock(&rq->queue_mutex);
rq->nr_running++;
}
//实际的插入动作参数依次是(内容,优先级别,显示次数,每次显示滞留的时间,是否是idle)
int insert_log_real(const char *content, int prio, int alive_times, int blink, int idle)
{
int ret = 0;
struct log_queue *rq;
struct log_buff *lb;
if ((current == NULL && !idle) || (current && idle)) {
ret = -1;
goto last;
}
rq = get_queue();
lb = (struct log_buff *)calloc(1, sizeof(struct log_buff));
strncpy(lb->buf, content, strlen(content)?:MAX_LENGTH);
INIT_LIST_HEAD(&lb->run_list);
lb->prio = prio;
lb->times = 0;
lb->alive_times = alive_times;
lb->blink = blink;
lb->sed_slice = (rq->prio2times)(prio);//show_style_func(prio); //可设置一个数学函数
if (!idle) //idle任务并不加入到任何链表当中,方便操作
__activate_log(lb, rq);
else {
current = lb;
rq->idle = lb;
}
//如果插入了一条信息且当前正在运行idle,则抢占它
//如果当前的任务的优先级别没有新插入的任务的优先级别高,则抢占它
if (current && ((current == rq->idle&&!idle) || current->prio>prio))
current->need_go = 1;
last:
return ret;

}
//插入idle日志任务
void set_idle(const char *idlebuf)
{
insert_log_real(idlebuf, 7, 0, 1, 1);
}
//插入一般的日志任务
int insert_log(const char *content, int prio, int alive_times, int blink)
{
return insert_log_real(content, prio, alive_times, blink, 0);
}
//以下这些都是直接从linux内核源代码中copy出来的
static inline unsigned long __ffs(unsigned long word)
{
...
}
static inline int sched_find_first_bit(const unsigned long *b)
{
...
}
//设置输出初始化回调函数
void set_handle_init(void (*func)(void *argv), void *arg)
{
struct log_queue* rq = get_queue();
rq->handler_init = func;
rq->handler_init(arg);
}
//设置输出处理函数
void set_handle_process(void (*func)(void *argv, int blink))
{
struct log_queue* rq = get_queue();
rq->handler_process = func;
}
//设置从优先级到时间片的转换函数
void set_prio2times(int (*func)(int prio))
{
struct log_queue* rq = get_queue();
rq->prio2times = func;
}
//全局的初始化
int global_init()
{
int ret = 0;
//分配全局队列
global_rq = (struct log_queue*)calloc(1, sizeof(struct log_queue));
struct prio_array *active = (struct prio_array*)malloc(sizeof(struct prio_array));
struct prio_array *expired = (struct prio_array*)malloc(sizeof(struct prio_array));
memset(active, 0, sizeof(struct prio_array));
memset(expired, 0, sizeof(struct prio_array));
global_rq->active = active;
global_rq->expired = expired;
ret = pthread_mutex_init(&global_rq->queue_mutex, NULL);
int i;
//初始化活动数组
for (i = 0; i < MAX_PRIO; i++) {
INIT_LIST_HEAD(&global_rq->active->queue[i]);
}
//初始化过期数组
for (i = 0; i < MAX_PRIO; i++) {
INIT_LIST_HEAD(&global_rq->expired->queue[i]);
}
return ret;
}
//设置调度时间间隔并且开始调度
int set_interval(int second)
{
static struct itimerval p_realt;
p_realt.it_interval.tv_sec = second;
p_realt.it_interval.tv_usec = 0;
p_realt.it_value.tv_sec = second;
p_realt.it_value.tv_usec = 0;
signal(SIGALRM,time_tick);
if(setitimer(ITIMER_REAL,&p_realt,(struct itimerval *)0) == -1) {
perror("setitimer error! ");
return -1;
}
return 0;
}
//全局启动
int start()
{
int ret = 0;
struct log_buff *previous;
//以下的逻辑完全就是linux内核O(1)进程调度器的逻辑
//参见linux内核kernel/sched.c中的schedule函数代码
while (1) {
struct log_queue *rq = get_queue();
struct prio_array *array = rq->active;
previous = current;
if (current->need_go) {
redo:
if (!array->nr_active) {
rq->active = rq->expired;
rq->expired = array;
array = rq->active;
}
struct log_buff *lb;
if (!array->nr_active) {
lb = rq->idle;
goto switch_to;
}
int idx = sched_find_first_bit(array->bitmap);
struct list_head *queue = array->queue + idx;
lb = list_entry(queue->next, struct log_buff, run_list);
if (lb->remove_flag) {
int redo_flag = 0;
if (lb == current) {
redo_flag = 1;
}
pthread_mutex_lock(&rq->queue_mutex);
dequeue_log(lb, lb->array);
pthread_mutex_unlock(&rq->queue_mutex);
free(lb);
lb = NULL;
if(redo_flag)
goto redo;
}
switch_to:
lb->need_go = 0;
current = lb;
}
if (current == previous && current == rq->idle) {
usleep(500);
continue;
}
rq->handler_process(current->buf, current->blink);
}
return ret;
}
下面是一个测试程序
#include "lcd_task.h"
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>


int show_style_func(int prio)
{
return 1;//9-prio;
}
void lcd_init(void *argv)
{
//打开并设置屏幕
... open( ..., O_RDWR | O_NOCTTY | O_NDELAY);
...
}

void lcd_process(void *argv, int blink)
{
//argv就是需要输出的信息,blink就是输出的间隔
char *buf = (char*)argv;
...
for (i = 0; i < 需要的屏幕数; i++, ...) {
//换屏处理,每屏幕都显示blink的间隔,下面的处理为了防止被调度timer信号中断sleep
while(blink = sleep(blink));
blink = blk;
}
}
void *test_func(void *arg)
{
int prio;
int i, j;
int total = 20;
char buf[16];
while (total--) {
prio = random()%8;
for (i = 0; i < 8; i++) {
if(prio == i) {
memset(buf, 0, sizeof(buf));
for (j = 0; j < 2*i; j++) {
strcat(buf, "-");
}
if (i == 0)
strcat(buf, "0000");
insert_log(buf, prio, 2, 2);
break;
}
}
sleep(1);
}
}

int main(int argc, char **argv)
{
pthread_t id;
global_init();
set_handle_process(lcd_process);
set_handle_init(lcd_init, NULL);
set_prio2times(show_style_func);
set_interval(1);
set_idle("I'm IDLE");
insert_log("this is one", 4, 3, 5);
...
pthread_create(&id,NULL,test_func,NULL);
start();
}

相关文章:

  • linux内核内存管理中的pagevec结构体
  • poj_2352 线段树
  • Mac周边环境 goBASIC语言HelloWorld
  • linux内存管理系统后期的内核对zonelist的简化
  • bzoj3809: Gty的二逼妹子序列
  • linux内核page结构体的PG_referenced和PG_active标志
  • 解決BufferedReader读取UTF-8文件中文乱码(转)
  • 问题以及发现问题和解决问题
  • bitmap格式分析(转)
  • 关于数组或集合中判断存在某个元素
  • kexec机制
  • Spring事务配置的五种方式
  • buffer_head和bio
  • 关于人脸识别,稀疏表示的若干论文的小结
  • asp.net——正则表达式
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【5+】跨webview多页面 触发事件(二)
  • ECMAScript6(0):ES6简明参考手册
  • express + mock 让前后台并行开发
  • HashMap剖析之内部结构
  • HomeBrew常规使用教程
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • node-glob通配符
  • rc-form之最单纯情况
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • v-if和v-for连用出现的问题
  • Vue 动态创建 component
  • windows下如何用phpstorm同步测试服务器
  • 成为一名优秀的Developer的书单
  • 动态魔术使用DBMS_SQL
  • 给Prometheus造假数据的方法
  • 给初学者:JavaScript 中数组操作注意点
  • 基于axios的vue插件,让http请求更简单
  • 检测对象或数组
  • 近期前端发展计划
  • 前端性能优化——回流与重绘
  • 如何利用MongoDB打造TOP榜小程序
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • PostgreSQL之连接数修改
  • #Java第九次作业--输入输出流和文件操作
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (二)换源+apt-get基础配置+搜狗拼音
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (原)本想说脏话,奈何已放下
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .net 后台导出excel ,word
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NetCore 如何动态路由
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .net对接阿里云CSB服务