Linux cpu核心代码set_bit的实现分析

在arm系统中,对cpu核的设计。使用了位技术变量来代表每个cpu的使用情况。

但是这里考虑到多核对同一变量的设置,因为有了多核访问,于是乎就需要防止冲突的机制。真样产生了特殊情况的操作位接口--> macro bitop, name, instr。

使用汇编的形式来完成。主要的技术,是arm arch6中的strex, ldrex。

 

STREX指令的英文解释如下:

STREX (Store Register Exclusive) performs a conditional store to memory. The store only occurs if the
executing processor has exclusive access to the memory addressed.

也就是说,这条存储指令具有cpu核的排它性。只有cpu具有独立访问该memor addressed的时候,才可以存储。否则,cpu存储指令失败。接下来就看看具体的使用方式。

 

语法如下:

Syntax
STREX{<cond>} <Rd>, <Rm>, [<Rn>]

where:
<cond> Is the condition under which the instruction is executed. The conditions are defined in The
      condition field on page A3-3. If <cond> is omitted, the AL (always) condition is used.
<Rd> Specifies the destination register for the returned status value. The value returned is:
    0
   if the operation updates memory
  1
 if the operation fails to update memory.
<Rm> Specifies the register containing the word to be stored to memory.
<Rn> Specifies the register containing the address.

Rd是一个返回该存储状态寄存器,这里可以知道这次存储的状况。

Rm保留了需要存储到memory的值。

Rn 指定了将Rm值保存到memory的地址。

 

 

LDREX指令的英文解释如下:

LDREX (Load Register Exclusive) loads a register from memory, and:
• if the address has the Shared memory attribute, marks the physical address as exclusive access for the
   executing processor in a shared monitor
• causes the executing processor to indicate an active inclusive access in the local monitor.

 

语法如下:

LDREX{<cond>} <Rd>, [<Rn>]
where:
<cond> Is the condition under which the instruction is executed. The conditions are defined in The
      condition field on page A3-3. If <cond> is omitted, the AL (always) condition is used.
<Rd> Specifies the destination register for the memory word addressed by <Rd>.
<Rn> Specifies the register containing the address.

操作如下:

MemoryAccess(B-bit, E-bit)
if ConditionPassed(cond) then
processor_id = ExecutingProcessor()
Rd = Memory[Rn,4]
physical_address = TLB(Rn)
if Shared(Rn) == 1 then
MarkExclusiveGlobal(physical_address,processor_id,4)
MarkExclusiveLocal(physical_address,processor_id,4)
/* See Summary of operation on page A2-49 */

 

 

在ARM手册中有如下说明使用STREX与LDREX的范围:

Use STREX in combination with LDREX to implement inter-process communication in multiprocessor and
shared memory systems

Use LDREX in combination with STREX to implement inter-process communication in shared memory
multiprocessor systems. For more information see Synchronization primitives on page A2-44. The
mechanism can also be used locally to ensure that an atomic load-store sequence occurs with no intervening
context switch.

指令总结:

1,这条指令是用于多核处理器中,用于处理多核访问共享内存的排他性而设计的,单核最好不要使用。

因为比一般的存储指令要耗时。

从STREX指令的伪操作可以猜测出:

MemoryAccess(B-bit, E-bit)
processor_id = ExecutingProcessor()
if ConditionPassed(cond) then
if (CP15_reg1_Ubit == 0) then
if address[0] == 0b0 then
Memory[address,2] = Rd[15:0]
else
Memory[address,2] = UNPREDICTABLE
else
/* CP15_reg1_Ubit ==1 */
Memory[address,2] = Rd[15:0]
if Shared(address) then
/* ARMv6 */
physical_address = TLB(address)
ClearExclusiveByAddress(physical_address,processor_id

 

2,LDREX与STREX要一起共同使用构成同步原语。

 

有了这些知识点的普及, 我们在来分析bitop这个宏汇编是如何实现设置一个long数组中的某个位。

 

bitop宏代码如下:

 

	.macro	bitop, name, instr
ENTRY(	\name		)
UNWIND(	.fnstart	)
	ands	ip, r1, #3
	strneb	r1, [ip]		@ assert word-aligned
	mov	r2, #1
	and	r3, r0, #31		@ Get bit offset
	mov	r0, r0, lsr #5
	add	r1, r1, r0, lsl #2	@ Get word offset
	mov	r3, r2, lsl r3
1:	ldrex	r2, [r1]
	\instr	r2, r2, r3
	strex	r0, r2, [r1]
	cmp	r0, #0
	bne	1b
	bx	lr
UNWIND(	.fnend		)
ENDPROC(\name		)
	.endm

 

我们使用实例,

set_bit(cpumask_check(cpu), cpumask_bits(dstp));来分析该汇编代码。

 

化间成:

set_bit(int cpu, long * (maskp)->bits);

所以对于汇编来说:bitop    _set_bit, orr   展开前。

r0 = cpu;

r1 = long型数据的首地址,用于存放位状态的存储器。

instr = orr指令。

 

第一模块: 内存对齐检测

    ands    ip, r1, #3
    strneb    r1, [ip]        @ assert word-aligned

 

主要看一看long型的数据首地址是否位word-aligned, 如果不对齐,去访问0地址空间,引起NULL异常。

 

第二模块:将r0与32位进行mod操作,获取位偏移量,和long数组的偏移量。

    mov    r2, #1
    and    r3, r0, #31        @ Get bit offset
    mov    r0, r0, lsr #5
    add    r1, r1, r0, lsl #2    @ Get word offset
    mov    r3, r2, lsl r3

 

1,将r2 = = 1  (mov    r2, #1)

2,%操作,保留r0中32位中的余数到r3寄存器中,也就是位的偏移量。(and    r3, r0, #31)

3,获得参数中parm0中的word偏移量(mov    r0, r0, lsr #5),这个时候,r0为原始parm0中的long数组偏移量。

4,将指针转到parm1,long数组中的目标long数据地址。(add    r1, r1, r0, lsl #2),r1存放的是param1中相应parm0位的整数偏移量。

5,r3是param0中具体某个long数据的偏移量设置成1(mov    r3, r2, lsl r3)

 

 

第三模块:orr操作,并且保存结果到memory中。

1:    ldrex    r2, [r1]
    \instr    r2, r2, r3
    strex    r0, r2, [r1]
    cmp    r0, #0
    bne    1b

 

1,使用多核同步原语  ldrex, 获取r1地址中的数据。保存到r2中。(ldrex    r2, [r1])

2,使用orr,或操作, 将保存在r3中的设置位与r1地址中的数据进行or操作,完成指定位的操作。(\instr    r2, r2, r3)

3,通过多核同步原语将r2中的新值存储到r1指定的地址。

4,判断该多核存储操作是否完成,如果完成,返回,否则重复进行。

 

到这里, 就完成了arm set_bit的汇编代码分析。   可以看到,这里使用了ldrex与strex同步原语。

避免了多核之间的数据不同步问题。

 

 

 (该博客是原创,转载请注明原创地址。 谢谢!)

 

 

你可能感兴趣的:(linux)