在路上...

The development of life
我们一直都在努力,有您的支持,将走得更远...

站内搜索: Google

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
在Linux操作操作系统某一些异常情况下,大家常会在log文件中看到N messages supressed (N为数字).
下面来解析一下产生原因和含义.
假设有如下情况(仅仅是假设):某设备收到数据(Data)后,需要在内核空分配一缓冲区,如果因为某种原因分配缓冲区失败时,驱动程序(Procedures)(Procedures)用printk打印日志:“Allocte Mem failed".
像上述异常处理情况看似没什么问题,但是在异常情况下,如果每个数据(Data)包分配计算机内存都失败,那会出现用户的 console或者是日志文件中不断出现"Allocate Mem failed".这样会导致“拒绝服务”.
为此,Linux对日志的输出使用了RateLimit机制.
将上述过程改写为:
if (net_ratelimit())
{
    printk(KERN_WARNING "Allocate Mem failed.\n");
}
int net_msg_cost =5*HZ;
int net_msg_burst =10;
int net_ratelimit(void)
{
    return __printk_ratelimit(net_msg_cost, net_msg_burst);
}
int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst)
{
    static DEFINE_SPINLOCK(ratelimit_lock);
    static unsigned long toks =10 * 5 * HZ;
    static unsigned long last_msg;
    static int missed;unsigned long flags;
    unsigned long now =jiffies;
    spin_lock_irqsave(&ratelimit_lock, flags);
    toks +=now - last_msg;last_msg =now;
    if (toks > (ratelimit_burst * ratelimit_jiffies))
        toks =ratelimit_burst * ratelimit_jiffies;
    if (toks >=ratelimit_jiffies)
     {
        int lost =missed;
        missed =0;
        toks -=ratelimit_jiffies;
        spin_unlock_irqrestore(&ratelimit_lock, flags);
        if (lost)
            printk(KERN_WARNING "printk: %d messages suppressed.\n", lost);
        return 1;
    }
    missed++;
    spin_unlock_irqrestore(&ratelimit_lock, flags);
    return 0;
}

思考:
I    ratelimit_jiffies的含义到底是什么?
    从表面看,ratelimit_jiffies代表了阈值,仔细分析下来发现,当然他还有个作用就是控制多少时间之内的message不重复显示,而不能是在超过该段时间后,输出 N message suppressed消息,当中N为这段时间内欲输出但被suppressed的数目。
II    ratelimit_burst的含义是什么?
    从表面看  ratelimit_burst*ratelimit_jiffies  控制了 toks  的上限.在最开始的阶段,当toks达到最大上限时,此时必定超过阈值,但missed又为0,所以我们可以得出结论,不怎么会输出message suppressed消息,但由于返回值为1,所以我们可以得出结论会让相应的模块输出应有的message,因为 
 if (net_ratelimit())
{
    printk(KERN_WARNING "Allocate Mem failed.\n");
}
此后toks递减ratelimit_jiffies,当再一次调用__printk_ratelimit后,
如果上次递减后的数值+"now - last_msg"还超过阈值则还可能会再输出相应的消息.所以我们可以得出结论ratelimit_burst*ratelimit_jiffies控制了在消息抑制前输出的消息多少.
III   注意:   toks  和   missed  是静态局部变量

最后,我在加一点,在  LDD 3 的第四章中(P83),也讲述了对于  printk 打印速度的限制,不过使用的是内核自己提供的  API,
int  printk_ratelimit(void)  函数,
使用方法和上面讲述的基本相同,

if( printk_ratelimit())
    printk(KERN_NOTICE "xxxxxxxxxxxx\n");

/* minimum time in jiffies between messages */
int printk_ratelimit_jiffies = 5 * HZ;

/* number of messages we send before ratelimiting */
int printk_ratelimit_burst = 10;

int printk_ratelimit(void)
{
    return __printk_ratelimit(printk_ratelimit_jiffies,
                printk_ratelimit_burst);
}

主要工作还是由这个  __printk_ratelimit()函数来完成的,这个函数需要两个参数,是由静态定义的,
这两个参数实际上可以通过  proc 文件系统进行修改,
/proc/sys/kernel/printk_ratelimit   文件控制在重新打开消息之前应该等待的秒数。
/proc/sys/kernel/printk_burst     文件控制在进行速度限制之前可以接受的消息。

int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst)
{
    static DEFINE_SPINLOCK(ratelimit_lock);
    static unsigned long toks = 10 * 5 * HZ;
    static unsigned long last_msg;
    static int missed;
    unsigned long flags;
    unsigned long now = jiffies;

    spin_lock_irqsave(&ratelimit_lock, flags);
    toks += now - last_msg;
    last_msg = now;
    if (toks > (ratelimit_burst * ratelimit_jiffies))
        toks = ratelimit_burst * ratelimit_jiffies;
    if (toks >= ratelimit_jiffies) {
        int lost = missed;

        missed = 0;
        toks -= ratelimit_jiffies;
        spin_unlock_irqrestore(&ratelimit_lock, flags);
        if (lost)
            printk(KERN_WARNING "printk: %d messages suppressed.\n", lost);
        return 1;
    }
    missed++;
    spin_unlock_irqrestore(&ratelimit_lock, flags);
    return 0;
}

在  LDD3 中的第四章中也描述了  printk 打印级别的控制,
主要是通过   /proc/sys/kernel/printk  文件对打印级别进行限制。
http://qgjie456.blog.163.com/blog/static/354513672008101331854328/
posted on 2009-08-25 16:17  palam  阅读(1401)  评论(0编辑  收藏  举报