I'm reading the inotify man page here and i struggle to understand the following comment in the example
Some systems cannot read integer variables if they are not properly aligned. On other systems, incorrect alignment may decrease performance. Hence, the buffer used for reading from the inotify file descriptor should have the same alignment as struct inotify_event.
And this is the buffer declaration+definition
char buf[4096]
__attribute__ ((aligned(__alignof__(struct inotify_event))));
I read the following pdf document that gives a basic understanding of memory alignment access issues.
I would like to understand both the inotify comment and also having some hints and links for further understand the alignment problem. For example i always hear that there's no alignment problem for variables allocated on the stack. The problem arise only for buffers whose data gets reinterpreted.
The struct inotify_event
is declared like this:
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask describing event */
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
The problem is with the flexible array member name
. Name has no number inside the braces, that means that &((struct inotify_event*)0)->name[0] == offsetof(struct inotify_event, name)
, ie. the memory for elements inside the name
member starts RIGHT AFTER the structure.
Now if we were to story one inotify event, we need additional space for the name after the structure. Dynamic allocation may look like this:
char name[] = "this is the name of this event";
struct inotify_event *obj = malloc(sizeof(*obj) + (strlen(name) + 1));
obj->wd = smth;
obj->mask = smth2;
obj->len = strlen(name) + 1;
// fun fact: obj->name = &obj[1] . So if you ware to place array of inotify_events, you would overwrite the second member here.
memcpy(obj->name, name, obj->len);
The memory for the name
structure member comes right after struct inotify_event
and is allocated with the same malloc. So If we want to have an array of inotify_events and would copy them including the name, the next struct inotify_event may be unaligned.
Let's assume alignof(struct inotify_event) = 8
and sizeof(struct inotify_event) = 16
and char name[] = "A";
so strlen(name) = 1
(strlen
excludes counting the terminating zero byte) and that we want to store an array of inotfiy_events inside a buffer. First we copy the struct - 16 bytes. Then we copy the name - 2 bytes (including zero byte). If we were to copy the next struct, it would be unaligned, cause we would copy it starting from the 18th byte (sizeof(struct inotfy_event) + strlen(name) + 1
) in the buffer, which is not dividable by alignof(struct inotify_event)
. We need to insert 5 bytes of extra padding and we need to do that manually, after the first array member, so the next struct inotify_event
will be copied into the 24th byte.
However, we need also to notify the user / application code of how much it needs to increment the pointer to get to the next struct array member. So we increment obj->len
with the number of padding bytes. So obj->len
is equal to strlen(name) + 1 + number of padding bytes inserted to make the next array member aligned
or is equal to 0, in case of no name.
Inspect the example code in the manual page. In the loop where we loop through struct inotify_events there is the line:
ptr += sizeof(struct inotify_event) + event->len
The ptr is a char*
pointer to the current / next array member. We need to increment the pointer not only by sizeof(struct inotify_event)
but also by the number of strlen(name) + 1
bytes + inserted padding to the next array member. That way we can keep the array member aligned to their needed alignment. In the next position is the next struct inotify_event
.
For more information browse about pointer arithmetics in C and flexible array struct member.