一个日志输出系统的设计

晨曦之光 发布于 2012/04/10 14:58
阅读 507
收藏 0

本文介绍一个基于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();
}


原文链接:http://blog.csdn.net/dog250/article/details/6070074
加载中
返回顶部
顶部