struct queue_entry_s {
odp_buffer_hdr_t *head;
odp_buffer_hdr_t *tail;
int status;
enq_func_t enqueue ODP_ALIGNED_CACHE;
deq_func_t dequeue;
enq_multi_func_t enqueue_multi;
deq_multi_func_t dequeue_multi;
odp_queue_t handle;
odp_buffer_t sched_buf;
odp_queue_type_t type;
odp_queue_param_t param;
odp_pktio_t pktin;
odp_pktio_t pktout;
char name[ODP_QUEUE_NAME_LEN];
};
typedef union queue_entry_u {
struct queue_entry_s s;
uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct queue_entry_s))];
} queue_entry_t;
typedef struct queue_table_t {
queue_entry_t queue[ODP_CONFIG_QUEUES];
} queue_table_t;
static queue_table_t *queue_tbl;
#define ODP_CACHE_LINE_SIZE 64
#define ODP_ALIGN_ROUNDUP(x, align)\
((align) * (((x) + align - 1) / (align)))
#define ODP_CACHE_LINE_SIZE_ROUNDUP(x)\
ODP_ALIGN_ROUNDUP(x, ODP_CACHE_LINE_SIZE)
在上面的代码中, typedef union queue_entry_u ,联合的意义是什么。如果我们采用结构( typedef struct queue_entry_u ),是否有任何缺点?
答案 0 :(得分:8)
unions
有几种用法:
union
可以节省一些内存。它使s
和pad
位于内存中的相同位置。如果您知道只需要其中一个,那么您可以使用union
。 pad
,您实际上是在s
的字节上进行迭代。 unions
一般也适用于投射。通过使用union
将语句序列化为字节数组的语法更为漂亮。 union
填充s
的大小以适应缓存行。这样,如果queue_entry_s
的大小是高速缓存行s
长度的精确倍数,那么pad
将位于完全相同的内存中而不会浪费空间。否则pad
将占用比s
更多的内存,union
的大小将始终是缓存行长度的精确倍数。据说,如果您为内存非常低或性能要求非常严格的设备编写嵌入式代码,通常只使用unions
是个好主意。它们非常危险并且很容易被误写,因为它意外地写了代表union
中另一种类型的内存。
答案 1 :(得分:1)
让我们从K& R第二版的联盟定义开始:
union是一个可以保存(在不同时间)对象的变量 不同种类 [...]。工会提供了一种操纵不同的方法 单个存储区域中的各种数据。
问题中的联合包含两个对象:类型为struct queue_entry_s
的结构和一个uint8_t
数组。重要的是要注意这两个对象在内存中重叠。具体而言,结构开始的地址与数组开始的地址相同。如果你写入结构,数组的内容将被更改,如果你写入数组,那么结构的内容将被更改。
然后请注意ODP_CACHE_LINE_SIZE_ROUNDUP
宏取一个大小并计算64的最小倍数,大于或等于该大小。
联合的大小由最大成员的大小决定。例如,如果sizeof(struct queue_entry_s)
为80,那么pad
数组的sizeof将为128,并且union的size将为128.
这最终给我们带来了答案。联合的目的是增加结构使用的内存,以便结构总是使用64字节内存的倍数。
如果您要将typedef union queue_entry_u
更改为typedef struct queue_entry_u
,则会更改内存布局。 s
数组不会在内存中重叠pad
和pad
,而是遵循内存中的s
结构。因此,如果s
占用80个字节而pad
占用128个字节,则typedef struct queue_entry_u
将定义占用208个字节内存的对象。这将浪费内存,并且不符合64的多重要求。