<Learn From Kernel> printk_ratelimit()

本文深入探讨了Linux内核中的打印限速机制,详细解释了printk_ratelimit()函数的工作原理及其如何防止大量控制台消息导致系统性能下降。


今天在看ldd网卡驱动的时候,发现一个有趣的函数 printk_ratelimit()

 

 他的主要做用和 prco文件系统下的,这两个接口有关系

printk_ratelimit 定义了消息之间允许的最小时间间隔。

printk_ratelimit_burst 定义消息数量

 

 

于是我机器上显示的就是 5s 内最多10条。

 

只是一个非常有意义的函数。 我们知道

ldd3 写道
hundreds or thousands of console messages per second is a good way to bog down
the system entirely and hide the real source of problems

 

以前在做 服务器的时候  ,都是通过一个信号 重定向 a:打屏 b:dev/null c:文件 ,压力测试的时候打屏一直是瓶颈。我们来看看内核怎么处理大量的prink的。

 

#define DEFINE_RATELIMIT_STATE(name, interval_init, burst_init)		\
									\
	struct ratelimit_state name = {					\
		.lock		= __SPIN_LOCK_UNLOCKED(name.lock),	\
		.interval	= interval_init,			\
		.burst		= burst_init,
		.begin = // long 				\
	}

 

这里的 interval_init 就是 5 , burst_init 是10

___ratelimit(&printk_ratelimit_state, func);
/*func 就是当前执行的函数 
*printk_ratelimit_state 就是那个初始化过的结构体*/
 

  

 主要代码和分析如下

int ___ratelimit(struct ratelimit_state *rs, const char *func)
{
	unsigned long flags;
	int ret;

	if (!rs->interval)
		return 1;

	/*这里当然要用 非阻塞的自选锁 ,保存并且禁止了本地中断 */
	if (!spin_trylock_irqsave(&rs->lock, flags))
		return 0;
	/*如果之前过了一个 打印时间点 就继续记录当前jiffies*/
	if (!rs->begin)
		rs->begin = jiffies;
	/*这里其实 用了宏 time_after,用来判断当前的jiffies 有没有流逝过 打印时间点 */
	if (time_is_before_jiffies(rs->begin + rs->interval)) {
		if (rs->missed)
			printk(KERN_WARNING "%s: %d callbacks suppressed\n",
				func, rs->missed);
		/*正好过了 就开始清零*/
		rs->begin   = 0;
		rs->printed = 0;
		rs->missed  = 0;
	}
	/*没过的话 就继续看rs->printed 剩余的
	*过了的话肯定打印*/
	if (rs->burst && rs->burst > rs->printed) {
		rs->printed++;//打印计数
		ret = 1;
	} else {
		rs->missed++;//忽略掉的
		ret = 0;
	}
	/*恢复 */
	spin_unlock_irqrestore(&rs->lock, flags);

	return ret;
}

 

 从以上的步骤可以看出,我们很容易在应用中,实现类似的功能。

 

 #define DEBUG_ONE(a) { 
if( printk_ratelimit()){\
 printf("*** %s DEBUG[%s:%d]: ", nowtime(time(NULL)), __FILE__, __LINE__); \
  printf a; printf("\n");} \
}

 

 

#define  time_after(now,after)宏

 

也很容易通过

static long  get_time()
{
	struct timeb tm;
	long Req_Begin=0,Req_Begin_ms=0,Res_Begin_Time=0;
	ftime(&tm);
	Req_Begin = tm.time;
	Req_Begin_ms = tm.millitm;
	Res_Begin_Time = ((long)Req_Begin*1000 +(long)Req_Begin_ms);
	return Res_Begin_Time;
}

 

这样的函数实现。

 

int printk(const char *fmt, ...); /* * Special printk facility for scheduler/timekeeping use only, _DO_NOT_USE_ ! */ __printf(1, 2) __cold int printk_deferred(const char *fmt, ...); /* * Please don't use printk_ratelimit(), because it shares ratelimiting state * with all other unrelated printk_ratelimit() callsites. Instead use * printk_ratelimited() or plain old __ratelimit(). */ extern int __printk_ratelimit(const char *func); #define printk_ratelimit() __printk_ratelimit(__func__) extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, unsigned int interval_msec); extern int printk_delay_msec; extern int dmesg_restrict; extern int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void *buf, size_t *lenp, loff_t *ppos); extern void wake_up_klogd(void); char *log_buf_addr_get(void); u32 log_buf_len_get(void); void log_buf_vmcoreinfo_setup(void); void __init setup_log_buf(int early); __printf(1, 2) void dump_stack_set_arch_desc(const char *fmt, ...); void dump_stack_print_info(const char *log_lvl); void show_regs_print_info(const char *log_lvl); extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold; extern asmlinkage void dump_stack(void) __cold; extern void printk_safe_flush(void); extern void printk_safe_flush_on_panic(void); #else static inline __printf(1, 0) int vprintk(const char *s, va_list args) { return 0; } static inline __printf(1, 2) __cold int printk(const char *s, ...) { return 0; } 底下有 int printk(const char *s, ...) { return 0; }
10-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值