内核2.6.24中__kfifo_put的smp_mb位置为何后置而非前置?
关于Linux内核2.6.24中__kfifo_put函数内存屏障位置的疑问
以下是内核2.6.24版本中的__kfifo_put函数代码:
unsigned int __kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len) { unsigned int l; len = min(len, fifo->size - fifo->in + fifo->out); /* * Ensure that we sample the fifo->out index -before- we * start putting bytes into the kfifo. */ smp_mb(); /* first put the data starting from fifo->in to buffer end */ l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l); /* then put the rest (if any) at the beginning of the buffer */ memcpy(fifo->buffer, buffer + l, len - l); /* * Ensure that we add the bytes to the kfifo -before- * we update the fifo->in index. */ smp_wmb(); fifo->in += len; return len; }
我的疑问是:为何smp_mb()没有放在获取fifo->out值的操作(即len = min(len, fifo->size - fifo->in + fifo->out))之前,而是放在该操作之后?
解答
要搞懂这个问题,得先明确smp_mb()的作用和这段代码的核心诉求:
- 代码注释已经说得很清楚:要保证先完成对
fifo->out的采样,再开始往kfifo里写数据。 smp_mb()是全内存屏障,它的作用是阻止屏障前后的内存操作发生重排序——屏障之前的所有内存操作必须完全完成、对其他CPU可见后,屏障之后的内存操作才能执行。
那为什么屏障要放在读取fifo->out之后?
- 我们要约束的是「读取
fifo->out」和「后续写buffer」这两个操作的顺序:必须先拿到准确的fifo->out值(确认有足够的空闲空间),再执行写操作,绝不能让CPU把写buffer的操作重排到读取fifo->out之前。 - 如果把
smp_mb()放在读取fifo->out之前,那屏障只能保证它之前的操作(比如函数参数的读取)完成,根本管不到「读取fifo->out」和「写buffer」的顺序,完全达不到注释里的要求。 - 放在读取
fifo->out之后,就能确保:所有在屏障前的读操作(包括读取fifo->out、fifo->in)都已经完成,之后才会执行屏障后的memcpy写buffer操作,从根本上避免了生产者还没确认空闲空间就写数据、覆盖消费者未读取内容的问题。
另外结合kfifo的并发模型来看:__kfifo_put是生产者端函数,和消费者端的__kfifo_get(会修改fifo->out)并发运行。这个屏障就是为了在多CPU环境下,让生产者拿到的fifo->out是最新的、有效的值,再基于这个值安全地写入数据。
内容的提问来源于stack exchange,提问作者lan muming
相关产品推荐
相关产品推荐

