linux内核printk的一些并发处理

我们知道,linux内核为了支持在各种位置都能使用printk,做了不少的工作,这篇文章简单介绍一下printk的一些并发处理。
本文基于linux内核4.19.195.
printk最终会调用到vprintk_func函数。

__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{
	/*
	 * Try to use the main logbuf even in NMI. But avoid calling console
	 * drivers that might have their own locks.
	 */
	if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK) &&
	    raw_spin_trylock(&logbuf_lock)) { //看注释,以及这里是raw_spin_trylock
		int len;

		len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);
		raw_spin_unlock(&logbuf_lock);
		defer_console_output();
		return len;
	}

	// nmi和vprintk_safe的分支都走的printk_safe_log_store,只是传入的buffer不一样

	/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
	if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
		return vprintk_nmi(fmt, args);

	/* Use extra buffer to prevent a recursion deadlock in safe mode. */
	if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
		return vprintk_safe(fmt, args);

	/* No obstacles. */
	return vprintk_default(fmt, args);
}

可以看到,这个函数有三个分支:vprintk_nmi、vprintk_safe、vprintk_default。其中,vprintk_default是正常走的分支,vprintk_nmi是在nmi中断中调用printk走的分支,vprintk_safe是在不安全的上下文中调用printk走的分支。下面我们主要以vprintk_nmi为例分析。
我们知道,printk最终会将输出信息保存在一个buffer中。如果多核同时调用printk,则最简单的情况,都走到vprintk_default分支,最终是由logbuf_lock_irqsave以及logbuf_unlock_irqrestore来完成并发处理的。

#define logbuf_lock_irqsave(flags)			\
	do {						\
		printk_safe_enter_irqsave(flags);	\
		raw_spin_lock(&logbuf_lock);		\
	} while (0)
asmlinkage int vprintk_emit(int facility, int level,
			    const char *dict, size_t dictlen,
			    const char *fmt, va_list args)
{
	****
	/* This stops the holder of console_sem just where we want him */
	logbuf_lock_irqsave(flags);
	curr_log_seq = log_next_seq;
	printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
	pending_output = (curr_log_seq != log_next_seq);
	logbuf_unlock_irqrestore(flags);
	*****
}

可以看到,这里为了做好并发处理,使用了关中断以及spin_lock实现的。我们知道nmi中断是无法被屏蔽掉的,那么我们如果在nmi中断中使用printk时,怎么保证并发安全呢?

static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
{
	struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);

	return printk_safe_log_store(s, fmt, args);
}
static __printf(2, 0) int printk_safe_log_store(struct printk_safe_seq_buf *s,
						const char *fmt, va_list args)
{
	int add;
	size_t len;
	va_list ap;

again:
	len = atomic_read(&s->len);

	/* The trailing '\0' is not counted into len. */
	if (len >= sizeof(s->buffer) - 1) {
		atomic_inc(&s->message_lost);
		queue_flush_work(s);
		return 0;
	}

	/*
	 * Make sure that all old data have been read before the buffer
	 * was reset. This is not needed when we just append data.
	 */
	if (!len)
		smp_rmb();

	va_copy(ap, args);
	add = vscnprintf(s->buffer + len, sizeof(s->buffer) - len, fmt, ap);
	va_end(ap);
	if (!add)
		return 0;

	/*
	 * Do it once again if the buffer has been flushed in the meantime.
	 * Note that atomic_cmpxchg() is an implicit memory barrier that
	 * makes sure that the data were written before updating s->len.
	 */
	if (atomic_cmpxchg(&s->len, len, len + add) != len)
		goto again;

	queue_flush_work(s);
	return add;
}

通过代码可以看到,nmi中断并没有直接把printk要打印的东西输出到全局的buffer中,而是通过将内容输出到一个percpu的buffer—nmi_print_seq中,然后调用queue_flush_work(),利用irq_work机制把输出的内容memcpy到全局的buffer中,从而支持了nmi中断中使用printk,具体的memcpy动作在work函数__printk_safe_flush()中完成。
此外,printk的基本原理,可以参考https://github.com/kaka555/KAKAOS/blob/master/C/ubuntu/src/kernel/OS_LIB/myMicroLIB.c中函数ka_printf()的实现

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言.........................................................................................................................................................5 本书的组织形式..............................................................................................................................6 精华版方法论部分导读......................................................................................................................8 Linux大史记...................................................................................................................................9 内核学习的方法论..........................................................................................................................9 驱动开发的方法论........................................................................................................................12 Linux内核问题门..........................................................................................................................13 缅怀已逝的十八年(1991~1998) ............................................................................................14 Linux诞生记.................................................................................................................................15 泰坦尼克的狂潮............................................................................................................................16 缅怀已逝的十八年(1999~2002) ............................................................................................17 提前发生的革命............................................................................................................................17 和平、爱情和Linux ......................................................................................................................19 缅怀已逝的十八年(2003~2006) ............................................................................................20 Ubuntu4.10 ...............................................................................................................................2
Linux内核编程 著者:Ori Pomerantz 翻译:徐辉 目 录 1.HELLO, WORLD................................................................................................三 EXHELLO.C..............................................................................................................三 1.1内核模块的编译文件........................................................................................................四 1.2 多文件内核模块..................................................................................................................五 2.字符设备文件.......................................................................................................八 2.1多内核版本源文件........................................................................................................十六 3./PROC文件系统...............................................................................................十七 4.使用/PROC进行输入...................................................................................二十二 5.和设备文件对话(写和IOCTLS)................................................................三十 6.启动参数.......................................................................................................四十四 7.系统调用.......................................................................................................四十七 8.阻塞进程.......................................................................................................五十三 9.替换PRINTK’S............................................................................................六十三 10.调度任务.....................................................................................................六十六 11.中断处理程序..............................................................................................七十一 11.1 INTEL 结构上的键盘.........................................................................................七十一 12.对称多处理.................................................................................................七十五 常见的错误.........................................................................................................七十六 2.0和2.2版本的区别........................................................................................七十六 除此以外.............................................................................................................七十六 其他......................................................................................................................七十八 GOODS AND SERVICES.....................................................................................................七十八 GNU GENERAL PUBLIC LICENSE........................................................................七十八 注..........................................................................................................................八十四

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值