Hugo Modesto
Hugo Modesto

Reputation: 63

How do i change the value of a position of an array inside a struct that is allocated in shared memory(mmap) in c?

I am trying to change the value of a position of an array inside a struct that is allocated in shared memory I have this struct:

typedef struct Paths {
     int *path;
     int totalDistance;
     int quantity;
}Path;

And i have this algorithm:

void runAlgorithm(int** distances,int nCities, int executionTime, int nProcesses){
int size = nCities+2 * sizeof(int);
int protection = PROT_READ | PROT_WRITE;
int visibility = MAP_ANONYMOUS | MAP_SHARED;
PtPath shmem = (PtPath)mmap(NULL, size, protection, visibility, 0, 0);
*shmem = createPath(nCities);
randomPath(shmem);
setPathTotalDistance(shmem, distances);
sem_unlink("job_ready");
sem_unlink("job_done");
sem_t *job_ready = sem_open("job_ready", O_CREAT, 0644, 0);
sem_t *job_done = sem_open("job_done", O_CREAT, 0644, 0);

int *pids = (int*)calloc(nProcesses, sizeof(int));
Path path, shortestPath;

//Workers Processes
for(int i = 0 ; i < nProcesses ; i++){
    pids[i] = fork();
    if(pids[i] == 0){
        shortestPath = createPath(nCities);
        randomPath(&shortestPath); //inicializa caminho aleatorio
        setPathTotalDistance(&shortestPath, distances); //problema aqui 
        while(1){
            sem_wait(job_ready);                
            mutation(&shortestPath, distances);
            if(shortestPath.totalDistance < shmem->totalDistance){
                printPath(shmem);
                printPath(&shortestPath);
                copyPath(&shortestPath, shmem);
            }
            sem_post(job_done);
        }
        exit(0);
    }
}

//Parent Process
int elapsed, terminate = 1;
time_t start = time(NULL), end;
printf("Elapsed time: \n");
while(terminate){
    end = time(NULL);
    elapsed = difftime(end,start);
    printElapsedTime(elapsed);
    if(elapsed >= executionTime)
        terminate = 0;
    else{
        sem_post(job_ready);
        sem_wait(job_done);
    }
}
printPath(shmem);
sem_close(job_ready);
sem_close(job_done);

// Kill worker processes
for (int i=0; i<nProcesses; i++) {
    printf("Killing %d\n", pids[i]);
    kill(pids[i], SIGKILL);
}
}

This is the code of createPath:

Path createPath(int quantity){
Path newPath;

if(quantity < 0)
    quantity = 0;
newPath.path = (int*)calloc(quantity, sizeof(int));
newPath.quantity = quantity;
newPath.totalDistance = 0;

return newPath;
}

And this is the copyPath code:

void copyPath(PtPath from, PtPath to){
for(int i = 0 ; i < from->quantity ; i++){
    //printf("posI: %d\n",to->path[i]);
    to->path[i] = from->path[i];
}
to->totalDistance = from->totalDistance;
to->quantity = from->quantity;
}

So my problem is i'm trying to copy the shortestPath to the shared memory and it copies nothing, but it does copy the totalDistance What am i doing wrong and where?

Upvotes: 1

Views: 83

Answers (1)

JiaHao Xu
JiaHao Xu

Reputation: 2748

Since you want to use shared memory, then I recommand you to rewrite the structure since the address of the shared memory can be changed when mmaping (not possible here) or mremap.

IMHO, it is always better to use offset inside shared memory than using pointer and assuming the address won't be changed, even if you can sure the address won't change now.

So:

struct Paths
{
    int totalDistance;
    int quantity;
    int path[0];
};

Since path is stored inlined with Paths continuously, there is no need for int *path.

When you allocate memory using mmap, the size should be sizeof(Paths) plus the size you need for storing path.

size_t size = sizeof(Paths) + /* Additional size needed for storing path */;
int protection = PROT_READ | PROT_WRITE;
int visibility = MAP_ANONYMOUS | MAP_SHARED; 
PtPath shmem = (PtPath) mmap(NULL, size, protection, visibility, -1, 0);

When you accessing path from Paths, the compiler will use offset from the address of Paths to access it, so as long as you hold a valid pointer to a shared memory with valid Paths, you can access it no matter it is mmaped ormremaped.

Coping the data from newPath will be just:

Path newPath = createPath();
for(int i = 0 ; i < shmem->quantity ; i++)
    shmem->path[i] = newPath.path[i];

This method has a slight advantage over the method used in the question: it does not need any additional pointer, which might be useful if you allocate a lot of Path.

Now, it is better to use void initializePath(Path*, int) instead of Path createPath(int):

void initializePath(Path *newPath, int quantity)
{
    if (quantity < 0)
        quantity = 0;
    newPath->quantity = quantity;
    newPath->totalDistance = 0;

    randomPath(shmem);       
    setPathTotalDistance(shmem, distances);
}

And you can have another function Path* allocatePath(int) to allocate Path:

Path* allocateSharedPath(int quantity)
{
    size_t size = sizeof(Paths) + quantity * sizeof(int);
    int protection = PROT_READ | PROT_WRITE;
    int visibility = MAP_ANONYMOUS | MAP_SHARED; 

    Path *shmem = (Path*) mmap(NULL, size, protection, visibility, -1, 0);

    if (shmem == NULL)
        /* Handle failure */;

    return shmem;
}

Path* allocatePath(int quantity)
{
    size_t size = sizeof(Paths) + quantity * sizeof(int);

    Path *mem = (Path*) calloc(size, 1);

    if (mem == NULL)
        /* Handle failure */;

    return mem;
}

BTW, if you want to store something else between quantity and path or you want to store path before Paths you can do this:

struct Paths
{
    int totalDistance;
    int quantity;
    offset_t offset_to_path;
};

The size required to allocate is sizeof(Paths) + /* Additional size needed for storing path */ + /* sizeof whatever else you need to store */. When initializing, initialize offset_to_path to pointer_to_path - pointer_to_Paths so that you can access it by adding offset_to_path to the pointer to Paths.

Upvotes: 1

Related Questions