__attribute__ ((packed))的意思是什么
如果变量使用packed修饰,告诉编译器不要为了对齐等,在里面填充字节;
更重要的一点是生成的代码,将以字节形式处理结构体中的变量。
typedef struct smd_tx_stream_tag
{unsigned int write;
const unsigned int read;
unsigned int size;
const unsigned char res[20];
unsigned char buffer[SMD_BUFFER_SIZE];
const unsigned char protection[32];
} __attribute__ ((packed)) T_SMD_TX_STREAM;
已函数smd_stream_write_avail为例
unsigned int smd_stream_write_avail(T_SMD_STREAM_CHANNEL *ch)
{
T_SMD_TX_STREAM *stream=&ch->tx_stream;
return (stream->size - stream->write + stream->read);
}
如果使用packed,则生成的汇编代码,都是使用ldrb,对字节进行操作
crash> dis smd_stream_write_avail
0xc021e6fc <smd_stream_write_avail>: mov r12, sp
0xc021e700 <smd_stream_write_avail+4>: push {r4, r5, r6, r11, r12, lr, pc}
0xc021e704 <smd_stream_write_avail+8>: sub r11, r12, #4
0xc021e708 <smd_stream_write_avail+12>: ldrb r12, [r0, #37] ; 0x25
0xc021e70c <smd_stream_write_avail+16>: ldrb r3, [r0, #36] ; 0x24
0xc021e710 <smd_stream_write_avail+20>: ldrb r1, [r0, #41] ; 0x29
0xc021e714 <smd_stream_write_avail+24>: ldrb r2, [r0, #40] ; 0x28
0xc021e718 <smd_stream_write_avail+28>: orr r12, r3, r12, lsl #8
0xc021e71c <smd_stream_write_avail+32>: ldrb r4, [r0, #38] ; 0x26
0xc021e720 <smd_stream_write_avail+36>: ldrb r3, [r0, #42] ; 0x2a
0xc021e724 <smd_stream_write_avail+40>: orr r1, r2, r1, lsl #8
0xc021e728 <smd_stream_write_avail+44>: ldrb r2, [r0, #39] ; 0x27
0xc021e72c <smd_stream_write_avail+48>: ldrb r6, [r0, #33] ; 0x21
0xc021e730 <smd_stream_write_avail+52>: orr r12, r12, r4, lsl #16
0xc021e734 <smd_stream_write_avail+56>: ldrb r5, [r0, #43] ; 0x2b
0xc021e738 <smd_stream_write_avail+60>: orr r1, r1, r3, lsl #16
0xc021e73c <smd_stream_write_avail+64>: ldrb r4, [r0, #34] ; 0x22
0xc021e740 <smd_stream_write_avail+68>: orr r12, r12, r2, lsl #24
0xc021e744 <smd_stream_write_avail+72>: ldrb r3, [r0, #32]
0xc021e748 <smd_stream_write_avail+76>: ldrb r2, [r0, #35] ; 0x23
0xc021e74c <smd_stream_write_avail+80>: orr r1, r1, r5, lsl #24
0xc021e750 <smd_stream_write_avail+84>: orr r3, r3, r6, lsl #8
0xc021e754 <smd_stream_write_avail+88>: add r0, r12, r1
0xc021e758 <smd_stream_write_avail+92>: orr r3, r3, r4, lsl #16
0xc021e75c <smd_stream_write_avail+96>: orr r3, r3, r2, lsl #24
0xc021e760 <smd_stream_write_avail+100>: rsb r0, r3, r0
0xc021e764 <smd_stream_write_avail+104>: ldm sp, {r4, r5, r6, r11, sp, pc}
如果去掉packeted属性,则生成的汇编代码为:
注意这个是使用objdump工具看得
arm-none-eabi-objdump -D vmlinux | less,而上面那个使用的是 crash工具
c0254518 <smd_stream_write_avail>:
c0254518: e1a0c00d mov ip, sp
c025451c: e92dd800 push {fp, ip, lr, pc}
c0254520: e24cb004 sub fp, ip, #4 ; 0x4
c0254524: e5901024 ldr r1, [r0, #36]
c0254528: e5902028 ldr r2, [r0, #40]
c025452c: e5903020 ldr r3, [r0, #32]
c0254530: e0810002 add r0, r1, r2
c0254534: e0630000 rsb r0, r3, r0
c0254538: e89da800 ldm sp, {fp, sp, pc}
后果是什么
因为变量如读写指针,是按字节更新的,当另一个 CPU [BP]使用使,看到的可能是更新工程中的一部分【变量都是4个字节,但按字节1个个更新】
算法的设计是一次更新4个字节,就是一次更新完变量。
如函数:smd_stream_read_avail中,可以发现 可以读出的数据大于最大的实际的整个shared memory,这肯定是错误的。
unsigned int smd_stream_read_avail(T_SMD_STREAM_CHANNEL *ch)
{
int len = 0;
T_SMD_RX_STREAM *stream=&ch->rx_stream;
len = stream->write - stream->read;
if(len < 0 || len > SMD_BUFFER_SIZE)
{
pr_err("ERRORED current write %x, read %x\n", stream->write, stream->read);
pr_err("ERRORED Last write %x, read %x\n", stream->protection[1], stream->protection[0]);
pr_err("ERRORED SMD READ/WRITE POINTER!!!!!!!!!\n");
stream->read = stream->write;
}
stream->protection[0]= stream->read;
stream->protection[1]= stream->write;
return (stream->write - stream->read);
}
总结
即使使用原子操作,操作这些变量,也不能解决问题。原子操作的意思是SMP【AP】中多个CPU的访问,而不是说的另外一个CPU【BP】。
问题的解决,却是修改数据变量的类型,有点神奇啊,解决bug,思路要开阔啊,敢于审视那些应该不会出问题的地方。