Getting information about the layout of a shared memory object
If you need to get information about the layout of a memory object (for example, to program a DMA engine or IOMMU), you can send a _MEM_OBJ_INFO message to the memory manager.
The _MEM_OBJ_INFO message lets you get the physical addresses associated with a shared memory object, if it was populated via shm_ctl() or shm_ctl_special(), without mapping it or calling mlock(). It can replace multiple calls to mem_offset().
struct _mem_obj_info {
_Uint16t type;
_Uint16t zero;
_Int32t fd;
_Uint64t offset;
_Uint64t length;
};
struct _mem_obj_segment {
_Uint64t offset;
_Uint64t paddr;
_Uint64t length;
};
struct _mem_obj_info_reply {
__FLEXARY(struct _mem_obj_segment, segments);
};
typedef union {
struct _mem_obj_info i;
struct _mem_obj_info_reply o;
} mem_obj_info_t;
Allocate a message with enough space for the expected information, and then set up the input (i) part of the message as follows:
- type
- The type of the message, _MEM_OBJ_INFO.
- zero
- Not used; set this to 0.
- fd
- A file descriptor for the shared memory object, from a call to shm_open(),
- offset
- The offset where you want to start in the shared memory object.
- length
- The number of bytes, starting at offset, that you want to retrieve the information for. You can set this to a large number (e.g., SIZE_MAX) if you want the layout of the whole object.
Send the message to the memory manager's connection, which is identified by MEMMGR_COID. If an error occurs, you'll get an error code; otherwise the memory manager fills in the output (o) part of the message and returns the number of array members that it filled. Each member of the returned array includes the offset, physical address and length of the segment.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/memmsg.h>
int main(void)
{
uint64_t obj_size = 8 * __PAGESIZE;
long i;
// Create a shared memory object.
int fd = shm_open("/myshmem", O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fd == -1) {
perror("shm_open");
return 1;
}
// Populate the object with physical memory.
if (shm_ctl(fd, SHMCTL_ANON, 0, obj_size) == -1) {
perror("shm_ctl");
return 1;
}
// Allocate a message with enough space for eight segments.
mem_obj_info_t *msg = malloc(sizeof(struct _mem_obj_segment) * 8);
if (msg == NULL) {
perror("malloc");
return 1;
}
// Send the message.
msg->i.type = _MEM_OBJ_INFO;
msg->i.zero = 0;
msg->i.fd = fd;
msg->i.offset = 0;
msg->i.length = SIZE_MAX;
long nsegs = MsgSend(MEMMGR_COID, msg, sizeof(*msg), msg, sizeof(*msg));
if (nsegs == -1) {
perror("MsgSend");
return 1;
}
// Print the result.
printf("Observed results:\n");
for (i = 0; i < nsegs; i++) {
printf(" offset=%lx paddr=%lx len=%lx\n",
msg->o.segments[i].offset,
msg->o.segments[i].paddr,
msg->o.segments[i].length);
}
i = nsegs -1;
if ((i >= 0) &&
(msg->o.segments[i].offset + msg->o.segments[i].length == obj_size)) {
printf ("We reached the end of the shared memory object.\n");
}
return 0;
}