Tuesday, October 14, 2008

interprocess communication IPC (2) - shared memory

I use the IPC-shared memory to help solve UVa problem 495: fib freeze.
The same algorithm is ranked 3rd ! 
The idea is as follows:


  • the main process fork a child to precompute the Fib numbers up to 5000.

  • the main process at the same time read in all input n!

  • as soon as the child finish, main process begin table-lookup and output

  • the shared memory is a char [][], each char[] contains a factorial for the corresponding n

  • i use semaphore (here actually act as a mutex) to synchronize the shared memory access


    • the child process get resource and release until it finishes computing 5000!

    • the main process then access the resource to lookup the result
The program flow:


  1. shmget: to request a shared memory block from the system

  2. shmat: to link the pointer to the shared memory

  3. semget: to create a semaphore (IPC_CREAT)

  4. semctl: to initialize the semaphore (SET_VAL)

  5. fork: to generate a child process

  6. semop: to use semaphore to maintain the resource access

  7. semctl: to remove semaphore ( IPC_RMID)

  8. shmdt: to detatch the pointer from the shared memory

  9. shmctl: to delete the shared memory ( IPC_RMID)

shmget很容易出错,如果是异常退出,可能需要更换key,重新run, (考虑用ipcrm -m #shmid kill)


最大可分配的memory及相关的参数可在man shmget中找到


e.g., /proc/sys/kernal/shmmax  maximum memory

#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "BigInt.h"
#define MAXSZ  5000

struct databuf {
char memo [MAXSZ+1][1050];
};

int main(int argc, char *argv[])
{
key_t key = 0x18845;
int shmid;
int semid;
int mode;

/* make the key: */
/*
if ((key = ftok("plotter.c", 'R')) == -1) {
perror("ftok");
exit(1);
}*/


/* connect to (and possibly create) the segment: */
if ((shmid = shmget(key, sizeof(databuf), 0600 |IPC_CREAT)) == -1) {
printf("shmid %d %d\n", shmid,errno);
perror("shmget");
switch(errno) {
case EACCES:
printf("access permit\n");break;
case EEXIST:
printf("segment exist\n"); break;
case EINVAL:
printf("einval\n");break;
case ENFILE:
printf("enfile\n");break;
case ENOENT:
printf("enoent\n");break;
case ENOMEM:
printf("no memory\n");break;
case ENOSPC:
printf("enospc\n");break;
case EPERM:
printf("eperm\n");break;
default:
printf("other\n");break;
}

exit(1);
}
/* attach to the segment to get a pointer to it: */
databuf* data = (databuf*)shmat(shmid, (void *)0, 0);

if (data == (databuf *)(-1)) {
perror("shmat");
exit(1);
}
//memset(data, 0, sizeof(data));

struct sembuf P = {0, -1, 0}; /* set to allocate resource */
struct sembuf V = {0, 1, 0}; /* free resource */

/*
if ((key = ftok("fibfreeze.c", 'J')) == -1) {
perror("ftok");
exit(1);
}*/


/* create a semaphore set with 1 semaphore: */
if ((semid = semget(0x99543, 1, 0666 | IPC_CREAT)) == -1) {
perror("semget");
exit(1);
}
//printf("sem id %d\n", semid);

/* initialize semaphore #0 to 1: */
if (semctl(semid, 0, SETVAL, 1) == -1) {
perror("semctl");
exit(1);
}

pid_t kid = fork();
if(kid == 0) {
//printf("kid process \n");
//kid process, computer fib number up to 5000
BigInt a1(0), a2(1), a;
semop(semid, &P, 1);
strcpy(data->memo[0], "0");
strcpy(data->memo[1], "1");

for(int i = 2; i <= MAXSZ; ++i){
a = a1 + a2;
strcpy(data->memo[i], a.toString().c_str());
//printf("calc m[%d]=%s\n", i, data->memo[i] );
//printf("calc %d, len %d\n", i, strlen(data->memo[i]));
a1 = a2;
a2 = a;
}
semop(semid, &V, 1);
exit(0);
} else {
//printf("parent process\n");
std::vector<int> n;
int tmp;

while(scanf("%d ", &tmp) != EOF)
n.push_back(tmp);

//wait();
semop(semid, &P, 1);
int N = n.size();
for(int i = 0; i < N; ++i)
printf("The Fibonacci number for %d is %s\n", n[i], data->memo[n[i]]);
semop(semid, &V, 1);
}

/* remove semaphore */
if (semctl(semid, 0, IPC_RMID, 0) == -1) {
perror("semctl");
exit(1);
}
//printf("free semaphores done\n");

/* detach from the segment: */
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}
/* remove shared memory */
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl shmid");
exit(1);
}
//printf("free memory segment done\n");
return 0;
}