I am trying to implement a memory allocator which can be called by multiple processes at once, so I used shared memory to keep the information of the first and last memory block alocated, both are put in the first 32 bytes of the shared memory and after that I tried to just have a simple allocation from the getpagesize() offset in the shared memory to see if it works but I keep getting a Segmentation Fault when using it to allocate a simple integer variable and I cannot figure out why.
#include <unistd.h>
#include <string.h>
#include <pthread.h>
/* Only for the debug printf */
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
typedef char ALIGN[16];
union header {
struct {
size_t size;
unsigned is_free;
union header *next;
} s;
/* force the header to be aligned to 16 bytes */
ALIGN stub;
};
typedef union header header_t;
header_t *get_free_block(size_t size)
{
char shm_name[] = "memalloc";
int shm_fd;
shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (shm_fd < 0)
{
perror(NULL);
return NULL;
}
size_t shm_size = 10 * getpagesize();
if (ftruncate(shm_fd, shm_size) == -1)
{
perror(NULL);
shm_unlink(shm_name);
return NULL;
}
char* shm_ptr = mmap(0, shm_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shm_ptr == MAP_FAILED)
{
perror(NULL);
shm_unlink(shm_name);
return NULL;
}
header_t *head;
if (shm_ptr)
head = (header_t*)shm_ptr;
else
head = NULL;
header_t *tail;
if (head)
tail = (header_t*)(shm_ptr + 16);
else
tail = NULL;
header_t *curr = head;
while(curr) {
/* see if there's a free block that can accomodate requested size */
if (curr->s.is_free && curr->s.size >= size)
return curr;
curr = curr->s.next;
}
return NULL;
}
void *malloc(size_t size)
{
char shm_name[] = "memalloc";
int shm_fd;
shm_fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (shm_fd < 0)
{
perror(NULL);
return NULL;
}
size_t shm_size = 10 * getpagesize();
if (ftruncate(shm_fd, shm_size) == -1)
{
perror(NULL);
shm_unlink(shm_name);
return NULL;
}
char* shm_ptr = mmap(0, 32, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shm_ptr == MAP_FAILED)
{
perror(NULL);
shm_unlink(shm_name);
return NULL;
}
header_t *head;
if (shm_ptr)
head = (header_t*)shm_ptr;
else
head = NULL;
header_t *tail;
if (head)
tail = (header_t*)(shm_ptr + 16);
else
tail = NULL;
size_t total_size;
void *block;
header_t *header;
if (!size)
return NULL;
header = get_free_block(size);
if (header) {
/* Woah, found a free block to accomodate requested memory. */
header->s.is_free = 0;
return (void*)(header + 1);
}
/* We need to get memory to fit in the requested block and header from OS. */
total_size = sizeof(header_t) + size;
int nr_pagesize = 1;
block = mmap(0, total_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, nr_pagesize * getpagesize());
if (block == MAP_FAILED)
{
perror(NULL);
shm_unlink(shm_name);
return NULL;
}
header = block;
header->s.size = size;
header->s.is_free = 0;
header->s.next = NULL;
if (!head)
head = header;
if (tail)
tail->s.next = header;
tail = header;
return (void*)(header + 1);
}
Here is the test program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int *x = (int*)malloc(sizeof(int));
return 0;
}
The commands I used on terminal are:
gcc -o memalloc.so -fPIC -shared memalloc.c
export LD_PRELOAD=$PWD/memalloc.so
gcc helloworld.c -o hello
and after compiling the test program I get the Segmentation Fault.
Related
I'm trying to write a program that uses counting semaphores, a mutex, and two threads. One thread is a producer that writes items to shared memory. Each item has a sequence number, timestamp, checksum, and some data. The consumer thread copies the original checksum from an item then calculates its own checksum from the item's data and compares the two to make sure the data wasn't corrupted.
My program runs, however, it reports incorrect checksums far more than correct checksums. I did some print statements to see what was going on, and it looks like the item's data is changing between writing to shared memory and reading from it. The item's stored checksum is also changing, and I have no idea what is causing this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
#include <stdint.h>
#include <semaphore.h>
#include <time.h>
#include <pthread.h>
typedef struct{
int seqNo;
uint16_t checksum;
uint32_t timeStamp;
uint8_t data[22];
}Item;
char* shm_name = "buffer";
int shm_fd;
uint8_t *shm_ptr;
pthread_t producers;
pthread_t consumers;
pthread_mutex_t mutex;
sem_t *empty, *full;
int shmSize;
int in = 0;
int out = 0;
//method for initializing shared memory
void CreateSharedMemory(){
shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0644);
if (shm_fd == -1) {
fprintf(stderr, "Error unable to create shared memory, '%s, errno = %d (%s)\n", shm_name,
errno, strerror(errno));
return -1;
}
/* configure the size of the shared memory segment */
if (ftruncate(shm_fd, shmSize) == -1) {
fprintf(stderr, "Error configure create shared memory, '%s, errno = %d (%s)\n", shm_name,
errno, strerror(errno));
shm_unlink(shm_name);
return -1;
}
printf("shared memory create success, shm_fd = %d\n", shm_fd);
}
uint16_t checksum(char *addr, uint32_t count)
{
register uint32_t sum = 0;
uint16_t *buf = (uint16_t *) addr;
// Main summing loop
while(count > 1)
{
sum = sum + *(buf)++;
count = count - 2;
}
// Add left-over byte, if any
if (count > 0)
sum = sum + *addr;
// Fold 32-bit sum to 16 bits
while (sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return(~sum);
}
Item CreateItem(){
Item item;
uint16_t cksum;
int j = 0;
time_t seconds;
seconds = time(NULL);
item.seqNo = j;
item.timeStamp = seconds; //FIX this
for(int i = 0; i < 22; ++i){
item.data[i] = rand() % 256;
}
cksum = checksum(&item.data[0], shmSize-2);
item.checksum = cksum;
++j;
return item;
}
void* producer() {
shm_fd = shm_open(shm_name, O_RDWR, 0644);
shm_ptr = (uint8_t *)mmap(0, 32, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
while(1) {
Item tempItem = CreateItem();
tempItem.seqNo = in;
sem_wait(empty);
pthread_mutex_lock(&mutex);
while (((in + 1)%shmSize) == out)
; // waiting
if(in < shmSize) {
//&shm_ptr[counter] = item;
\
memcpy(&shm_ptr[in], &tempItem, 32);
printf("%d\n", tempItem.seqNo);
in = (in + 1) % shmSize;
printf("Producer: %x\n", tempItem.checksum);
}
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(full);
}
}
void* consumer() {
uint16_t cksum1, cksum2;
shm_fd = shm_open(shm_name, O_RDWR, 0644);
shm_ptr = (uint8_t *)mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
while(1) {
sem_wait(full);
pthread_mutex_lock(&mutex);
while (in == out)
; // waiting
if(out > 0) {
Item tempItem;
memcpy(&tempItem, &shm_ptr[out], 32);
cksum1 = tempItem.checksum;
cksum2 = checksum(&tempItem.data[0], shmSize-2);
if (cksum1 != cksum2) {
printf("Checksum mismatch: expected %02x, received %02x \n", cksum2, cksum1);
}
else{
printf("removed from shm\n");
}
//printf("Checksums match !!! \n");
out = (out + 1)%shmSize;
}
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(empty);
}
}
int main(int argc, char **argv){
sem_unlink(&empty);
sem_unlink(&full);
shm_unlink(shm_name);
shmSize = atoi(argv[1]);
out = shmSize;
if(shmSize < 0){
printf("Error: Size of buffer cannot be negative. ");
return -1;
}
pthread_mutex_init(&mutex, NULL);
empty = sem_open("/empty", O_CREAT, 0644, shmSize);
full = sem_open("/full", O_CREAT, 0644, 0);
CreateSharedMemory();
pthread_create(&producers, NULL, producer, NULL);
pthread_create(&consumers, NULL, consumer, NULL);
pthread_exit(NULL);
Driver's mmap() entry point not getting called.
This is the source code of my device driver:
struct miscdevice my_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mymma",
.fops = &my_fops,
};
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.mmap = my_mmap,
};
static int __init my_module_init(void)
{
return my_init();
}
static void __exit my_module_exit(void)
{
my_exit();
}
int my_init(void)
{
int ret =0;
if ((ret = misc_register(&my_dev)))
{
printk(KERN_ERR "Unable to register \"my mma\" misc device\n");
return ret;
}
printk("kernel module installed\n");
return ret;
}
But my driver's mmap() entry point is not getting called.
This is the user space program calling it:
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
int main(){
int fd=open("/dev/mymma",O_RDONLY);
if(fd<0)
exit(0);
printf("helllo\n");
int N=5;
int *ptr = mmap ( NULL, N*sizeof(int),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, fd, 0 );
if(ptr == MAP_FAILED){
printf("Mapping Failed\n");
return 1;
}
for(int i=0; i<N; i++)
ptr[i] = i*10;
for(int i=0; i<N; i++)
printf("[%d] ",ptr[i]);
printf("\n");
int err = munmap(ptr, 10*sizeof(int));
if(err != 0){
printf("UnMapping Failed\n");
return 1;
}
return 0;
}
Provide the mmap() entry point of your driver.
I can notice that the device node is opened RDONLY but you are calling mmap() with PROT_READ/WRITE. Moreover MAP_ANONYMOUS makes mmap() ignore the file descriptor: you are merely allocating some space in memory. That is why you don't reach your driver.
I tried to use perf_event_open() to track all the store instructions to get their access address. I found only when I set attr.precise_ip > 0, I can get the non-zero address. But when I ran the same process on vm instead of host, the error massage was "Operation not supported", I can fix this problem by setting precise_ip = 0 on vm, but now I only get bunch of addresses equal to zero. I don't understand why precise_ip is related to the sample addrress which is not pointed out on document, and I also don't understand why I can't set precise_ip = 1 on vm while I can do it on host. Is there anybody can help me??
FYI: I use - cpu host option when I start vm using qemu-system-x86_64
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#define PERF_PAGES (1 + (1 << 16))
struct perf_sample {
struct perf_event_header header;
__u64 ip;
__u32 pid, tid; /* if PERF_SAMPLE_TID */
__u64 addr; /* if PERF_SAMPLE_ADDR */
__u64 weight; /* if PERF_SAMPLE_WEIGHT */
/* __u64 data_src; /\* if PERF_SAMPLE_DATA_SRC *\/ */
__u64 phy_addr;
};
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{
return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}
void workload()
{
int i,c=0;
for(i=0;i<100000000;i++)
{
c+=i*i;
c-=i*100;
c+=i*i*i/100;
}
}
int startup()
{
struct perf_event_attr attr;
memset(&attr,0,sizeof(struct perf_event_attr));
attr.type = PERF_TYPE_RAW;
attr.size = sizeof(struct perf_event_attr);
attr.config = 0x82d0;
attr.config1 = 0;
attr.sample_period = 1000;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_ADDR | PERF_SAMPLE_PHYS_ADDR ;
attr.disabled = 0;
//attr.inherit = 1;
attr.exclude_kernel = 1;
attr.exclude_hv = 1;
attr.exclude_callchain_kernel = 1;
attr.exclude_callchain_user = 1;
attr.precise_ip = 1; // when i set attr.precise_ip = 0 , all the addr = 0;
int fd=perf_event_open(&attr,0,-1,-1,0);
if(fd<0)
{
perror("Cannot open perf fd!");
return -1;
}
return fd;
}
void scan_thread(struct perf_event_mmap_page *p)
{
char *pbuf = (char *)p + p->data_offset;
__sync_synchronize();
printf("%d,\n", p->data_size);
if(p->data_head == p->data_tail) {
return;
}
struct perf_event_header *ph = (void *)(pbuf + (p->data_tail % p->data_size));
struct perf_sample* ps;
switch(ph->type) {
case PERF_RECORD_SAMPLE:
ps = (struct perf_sample*)ph;
// assert(ps != NULL);
if(ps == NULL)
{
printf("null\n");
}
if(ps!= NULL && ps->addr != 0) {
printf("ip %lx\n", ps->ip);
printf("tid %d\n", ps->tid);
printf("addr: %lx \n", ps->addr);
}
//printf("addr, %lx\n", ps->addr);
//printf("phy addr, %lx\n", ps->phy_addr);
break;
default:
printf("type %d\n", ph->type);
break;
}
}
int main()
{
int fd = startup();
size_t mmap_size = sysconf(_SC_PAGESIZE) * PERF_PAGES;
struct perf_event_mmap_page *p = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// start to perf
ioctl(fd,PERF_EVENT_IOC_ENABLE,0);
int a= 0;
while(1)
{
// uint64_t instructions;
// read(fd,&instructions,sizeof(instructions));
// printf("instructions=%ld\n",instructions);
// sleep(1);
workload();
scan_thread(p);
sleep(1);
}
}
I implement a memory mapping via mmap. My Kernel module writes something into this memory and a userspace application read this. In short I allocate 0x10000 memory (with kcalloc on kernel side and with mmap on userspace side). Then I write something to the address offsets 0x0, 0xf00 and 0xf000 using memcpy. On kernelside I can read back the memory correctly. But on userspace side the content of the first 0x1000 Bytes are repetitive through the whole memory (16 times). But why?
Her comes the code of the kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/mm.h>
#define DEV_MODULENAME "expdev"
#define DEV_CLASSNAME "expdevclass"
static int majorNumber;
static struct class *devClass = NULL;
static struct device *devDevice = NULL;
#ifndef VM_RESERVED
# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
struct mmap_info
{
char *data;
int reference;
};
static void
dev_vm_ops_open( struct vm_area_struct *vma )
{
struct mmap_info *info;
// counting how many applications mapping on this dataset
info = (struct mmap_info *)vma->vm_private_data;
info->reference++;
}
static void
dev_vm_ops_close( struct vm_area_struct *vma )
{
struct mmap_info *info;
info = (struct mmap_info *)vma->vm_private_data;
info->reference--;
}
static int
dev_vm_ops_fault( struct vm_area_struct *vma,
struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
info = (struct mmap_info *)vma->vm_private_data;
if (!info->data)
{
printk("No data\n");
return 0;
}
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
return 0;
}
static const struct vm_operations_struct dev_vm_ops =
{
.open = dev_vm_ops_open,
.close = dev_vm_ops_close,
.fault = dev_vm_ops_fault,
};
int
fops_mmap( struct file *filp,
struct vm_area_struct *vma)
{
vma->vm_ops = &dev_vm_ops;
vma->vm_flags |= VM_RESERVED;
vma->vm_private_data = filp->private_data;
dev_vm_ops_open(vma);
return 0;
}
int
fops_close( struct inode *inode,
struct file *filp)
{
struct mmap_info *info;
info = filp->private_data;
free_page((unsigned long)info->data);
kfree(info);
filp->private_data = NULL;
return 0;
}
int
fops_open( struct inode *inode,
struct file *p_file)
{
struct mmap_info *info;
char *data;
info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
// allocating memory on the heap for the data
data = kcalloc(0x10000,sizeof(char),GFP_KERNEL);
if( data==NULL )
{
printk(KERN_ERR "insufficient memory\n");
/* insufficient memory: you must handle this error! */
return ENOMEM;
}
info->data = data;
printk(KERN_INFO " > ->data: 0x%16p\n",info->data);
memcpy(info->data, "Initial entry on mapped memory by the kernel module", 52);
memcpy((info->data)+0xf00, "Somewhere", 9);
memcpy((info->data)+0xf000, "Somehow", 7);
printk(KERN_INFO " > ->data: %c%c%c\n", // the output here is correct
*(info->data+0xf000+0),
*(info->data+0xf000+1),
*(info->data+0xf000+2));
/* assign this info struct to the file */
p_file->private_data = info;
return 0;
}
static const struct file_operations dev_fops =
{
.open = fops_open,
.release = fops_close,
.mmap = fops_mmap,
};
static int __init
_module_init(void)
{
int ret = 0;
// Try to dynamically allocate a major number for the device
majorNumber = register_chrdev(0, DEV_MODULENAME, &dev_fops);
if (majorNumber<0)
{
printk(KERN_ALERT "Failed to register a major number.\n");
return -EIO; // I/O error
}
// Register the device class
devClass = class_create(THIS_MODULE, DEV_CLASSNAME);
// Check for error and clean up if there is
if (IS_ERR(devClass))
{
printk(KERN_ALERT "Failed to register device class.\n");
ret = PTR_ERR(devClass);
goto goto_unregister_chrdev;
}
// Create and register the device
devDevice = device_create(devClass,
NULL,
MKDEV(majorNumber, 0),
NULL,
DEV_MODULENAME
);
// Clean up if there is an error
if( IS_ERR(devDevice) )
{
printk(KERN_ALERT "Failed to create the device.\n");
ret = PTR_ERR(devDevice);
goto goto_class_destroy;
}
printk(KERN_INFO "Module registered.\n");
return ret;
// Error handling - using goto
goto_class_destroy:
class_destroy(devClass);
goto_unregister_chrdev:
unregister_chrdev(majorNumber, DEV_MODULENAME);
return ret;
}
static void __exit
_module_exit(void)
{
device_destroy(devClass, MKDEV(majorNumber, 0));
class_unregister(devClass);
class_destroy(devClass);
unregister_chrdev(majorNumber, DEV_MODULENAME);
printk(KERN_INFO "Module unregistered.\n");
}
module_init(_module_init);
module_exit(_module_exit);
MODULE_LICENSE("GPL");
here comes the code of the application
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define PAGE_SIZE (0x10000)
int main ( int argc, char **argv )
{
int fd;
char *address = NULL;
time_t t = time(NULL);
char *sbuff;
int i;
sbuff = (char*) calloc(PAGE_SIZE,sizeof(char));
fd = open("/dev/expdev", O_RDWR);
if(fd < 0)
{
perror("Open call failed");
return -1;
}
address = mmap( NULL,
PAGE_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
0);
if (address == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
printf("%s: first userspace read\n",tbuff);
memcpy(sbuff, address,80);
printf("Initial message: %s\n", sbuff);
memcpy(sbuff, address+0xf00,80);
printf("Initial message: %s\n", sbuff);
memcpy(sbuff, address+0xf000,80);
printf("Initial message: %s\n", sbuff);
for(i=0; i<PAGE_SIZE; i++)
{
printf("%16p: %c\n",address+i, (char)*(address+i));
}
if (munmap(address, PAGE_SIZE) == -1)
{
perror("Error un-mmapping the file");
}
close(fd);
return 0;
}
and this is the output of the application:
0x7fe61b522000: I
0x7fe61b522001: n
0x7fe61b522002: i
0x7fe61b522003: t
0x7fe61b522004: i
0x7fe61b522005: a
0x7fe61b522006: l
...
0x7fe61b522f00: S
0x7fe61b522f01: o
0x7fe61b522f02: m
0x7fe61b522f03: e
0x7fe61b522f04: w
0x7fe61b522f05: h
0x7fe61b522f06: e
0x7fe61b522f07: r
0x7fe61b522f08: e
...
0x7fe61b523000: I
0x7fe61b523001: n
0x7fe61b523002: i
0x7fe61b523003: t
0x7fe61b523004: i
0x7fe61b523005: a
0x7fe61b523006: l
...
0x7fe61b523f00: S
0x7fe61b523f01: o
0x7fe61b523f02: m
0x7fe61b523f03: e
0x7fe61b523f04: w
0x7fe61b523f05: h
0x7fe61b523f06: e
0x7fe61b523f07: r
0x7fe61b523f08: e
...
0x7fe61b524000: I
0x7fe61b524001: n
0x7fe61b524002: i
0x7fe61b524003: t
0x7fe61b524004: i
0x7fe61b524005: a
0x7fe61b524006: l
...
It seems to me, that the repetition comes with the size of one page. But this makes no sense to me.
EDIT 1:
Add Somewhere to the output. Note: Only Somehow never occurs!
EDIT 2:
Corrected fault handler. This now considered the offset of the calling vmf. Now it runs like a charm. Thanks to Tsyvarev!
static int
dev_vm_ops_fault( struct vm_area_struct *vma,
struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
info = (struct mmap_info *)vma->vm_private_data;
if (!info->data)
{
printk("No data\n");
return 0;
}
page = virt_to_page((info->data)+(vmf->pgoff*PAGE_SIZE));
get_page(page);
vmf->page = page;
return 0;
}
But on userspace side the content of the first 0x1000`
0x1000 is a size of the page mapped with
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
Callback .fault of structure vm_operations_struct is called for every page (4096 bytes), which is accessed by the user but not mapped yet.
So your code just map first 4096 bytes (0x1000) of data to every page which user space accesses.
I'm trying to use CK_LIST from http://concurrencykit.org/ across multiple processes but when I do, the values inside the list nodes are garbage. But the list values are correct when I use the list only from one process.
Here is an example of using CK_LIST in a struct with in a single process.
#include <stdio.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map
{
CK_LIST_HEAD(list, list_node) list;
};
struct list_node
{
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
int main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
node = malloc(sizeof(struct list_node));
if(node == NULL)
{
perror("malloc");
return -1;
}
CK_LIST_INIT(&map->list);
int rtrn = asprintf((char **)&node->data, "test");
if(rtrn < 0)
{
perror("asprintf");
return -1;
}
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
CK_LIST_FOREACH(node2, &map->list, list_entry)
{
printf("out: %s\n", node2->data);
}
return 0;
}
But when I try to use the list between two separate processes the value of node->data is garbage and causes the process using it to crash. Below is an example.
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map
{
CK_LIST_HEAD(list, list_node) list;
};
struct list_node
{
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
static int create_shared(void **pointer, int size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if(*pointer == MAP_FAILED)
{
perror("mmap:");
return -1;
}
return 0;
}
int main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
int rtrn = create_shared((void **)&map, sizeof(struct shared_map));
if(rtrn < 0)
{
printf("Can't create shared mapping\n");
return -1;
}
CK_LIST_INIT(&map->list);
pid_t pid;
pid = fork();
if(pid == 0)
{
/* Child. */
node = malloc(sizeof(struct list_node));
if(node == NULL)
{
perror("malloc");
return -1;
}
int rtrn = asprintf((char **)&node->data, "test");
if(rtrn < 0)
{
perror("asprintf");
return -1;
}
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
}
else if(pid > 0)
{
/* Parent. */
sleep(1); // Make sure child runs first.
CK_LIST_FOREACH(node2, &map->list, list_entry)
{
printf("out: %s\n", node2->data);
}
}
else
{
perror("fork");
return -1;
}
return 0;
}
CK_LIST is listed as a multi-reader single-writer linked list so I thought I only had to lock on writes not reads. So why does node->data become garbage when using it between processes as opposed to when used by a single process?
Fortunately, this has nothing to do with CK and just to do with where your memory is coming from.
Your allocation in the child comes from malloc and lives in the child's address space. The child and parent do not share the same address space (being separate processes), so your malloc(3)-obtained storage for the node and data are not at addresses within the parent's address space.
You need to allocate the memory for the node and node->data from some shared space as well. I made a minimal set of changes to the code to get it to work for demonstration purposes. Hopefully obviously, you'd want your API to be less fragile in terms of magic numbers to mmap.
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ck_queue.h>
struct shared_map {
CK_LIST_HEAD(list, list_node) list;
};
struct list_node {
void *data;
CK_LIST_ENTRY(list_node) list_entry;
};
static int
create_shared(void **pointer, int size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (*pointer == MAP_FAILED) {
perror("mmap:");
return -1;
}
return 0;
}
int
main(void)
{
struct list_node *node, *node2;
struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
struct shared_map *map = &mapping;
int rtrn = create_shared((void **)&map, sizeof(struct shared_map) + sizeof(struct list_node) + 5);
if (rtrn < 0) {
printf("Can't create shared mapping\n");
return -1;
}
CK_LIST_INIT(&map->list);
pid_t pid;
pid = fork();
if (pid == 0) {
/* Child. */
node = (struct list_node *)(map + 1);
node->data = node + 1;
memcpy(node->data, "test", 5);
CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
} else if (pid > 0) {
/* Parent. */
sleep(1); // Make sure child runs first.
CK_LIST_FOREACH(node2, &map->list, list_entry) {
printf("out: %s\n", (char *)node2->data);
}
} else {
perror("fork");
return -1;
}
return 0;
}
Fundamentally, I just changed the size of the mmap to include space for the list_node and the C string attached to node->data and made the child set that up.