Solved

create multiple shared memory

Posted on 2004-10-14
21
697 Views
Last Modified: 2008-01-09
Hi all experts,

I've created a shared memory for processes to access data(read only) from it. However, as there are some function that will only accessing certain types of data, i would like to create multiple shared memory, to narrow down the size of shared memory for lookup. Each shared memory will contain different types of information to be accessed. is this possible? is yes, how do i go about creating them?

0
Comment
Question by:elsia
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 6
  • 6
21 Comments
 
LVL 17

Expert Comment

by:rstaveley
ID: 12306706
What's your operating system?

In windows you use different names. In Linux you use different keys (typically using ftok for the key).
0
 

Author Comment

by:elsia
ID: 12306774
I am using Linux and developing a Pro*C program. I defined a variable for that using ftok but it can only generate 1 key. how do i generate different keys so that i can create multiple shared memory?

#define TRANSITION_SHM_KEY ftok(".", '2')

main() {
  create_shm (TRANSITION_SHM_KEY, 1000000, &shm_id);
  load_data (shm_id, &line_count);
}

void create_shm (key_t key, size_t size, int *id)
{
   int shm_id;

   shm_id = shmget (key, size, IPC_CREAT|IPC_EXCL|SHM_MODE);
   if (shm_id == -1)
   {
      perror ("Can't create share memory");
      return (1);
   }
   else
  {
      printf("\nSHM_ID:%x\n",key);
  }
  *id = shm_id;
}

load_data() {
   trans_param = (TRANS_PARAM *) shmat (shm_id, (void *) NULL, 0);
   if (trans_param == (TRANS_PARAM *) NULL)
   {
       perror ("shmat");
       return (1);
   }
 /* and so on..*/
}
0
 
LVL 12

Assisted Solution

by:stefan73
stefan73 earned 100 total points
ID: 12306947
Hi elsia,
You can have several blocks of shared memory (different key or shmid), or mount the same block to several addresses at once.
Here is an example of first creating a new block, and then mounting it repeatedly, until its copies occupy the entire address space:

shmem_create.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

#define SIZE (16 << 20) /* 16M */
#define KEY 4711 /* SHMEM key */



#define assert_fail(exp,msg) \
        do{\
                if((exp)!=1) { \
                        fprintf(stderr,"Failure in %s:%d - %s\n%s %s\n",\
                        __FILE__,__LINE__,msg,#exp,strerror(errno)); \
                        exit(99); \
                } \
        } while(0)




int main(){
        int shmid;

        shmid = shmget(KEY,SIZE,0700 | IPC_CREAT); /* New block with key 4711 */
        assert_fail(shmid>0,"shmget IPC_CREAT");

        printf("Created shmid = %d\n",shmid);

        return 0;
}

Now you can mount the same block repeatedly:

shmem_multimap.c:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

#define SIZE (16 << 20) /* 16M */
#define KEY 4711 /* SHMEM key */



#define assert_fail(exp,msg) \
        do{\
                if((exp)!=1) { \
                        fprintf(stderr,"Failure in %s:%d - %s\n%s\n%d",\
                        __FILE__,__LINE__,msg,#exp,errno); fflush(stderr); \
                        fprintf(stderr,"=%s\n",strerror(errno)); \
                        exit(99); \
                } \
        } while(0)




int main(){
    int shmid;
    int blocks;
    void* addr,*oaddr=0;
    unsigned long delta=0;
    unsigned long sdelta=0;
    int* blockbox[4096];
    int i,rnd;
   
    srand(time(0));
    rnd = rand();
    printf("SIZE = 0x%08x (%dMB)\n",SIZE,(SIZE>>20));
   
    shmid = shmget(KEY,SIZE,0);
    assert_fail(shmid>0,"shmget");
    printf("Got shmid = %d\n",shmid);
   
    for(blocks=0;;blocks++){
        addr = shmat(shmid,0,0700);
        if(!(addr!=NULL && addr != (void*)-1))
            putchar('\n');

        if(errno == EMFILE) /* Too many - let's check */
            break;

        assert_fail(addr != (void*)-1,"shmat failure");
        if(oaddr)
            delta = (unsigned long)oaddr - (unsigned long)addr;
       
        if((long)delta < 0L)
            delta = -((long)delta);
       
        sdelta += delta;
       
        printf("Attached %4d blocks (%4dMB) addr=0x%0*x ",blocks,(SIZE>>20)*blocks,
        2*sizeof(void*),addr);

        printf("delta = 0x%08x (%2dMB) ",delta,delta >> 20);

        printf("sdelta = 0x%08x (%4dMB)\r",sdelta,sdelta >> 20);

        fflush(stdout);
        oaddr = addr;
        blockbox[blocks] = (int*)addr;
    }
   
    printf("Rand seed = %d\n",rnd);
   
    blockbox[0][0] = rnd;
   
    for(i=0;i<blocks;i++)
        if(blockbox[i][0] != rnd)
        printf("blockbox[%3d] = %d\n",i,blockbox[i][0]);
    printf("Memory blocks OK\n");
   
   
    return 0;
}







Cheers!

Stefan
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 17

Accepted Solution

by:
rstaveley earned 150 total points
ID: 12307010
Make a second key from the same path, using a different project identifier.

e.g.

#define TRANSITION_SHM_KEY_2 ftok(".", '2') /* Project identifier is '2' */
#define TRANSITION_SHM_KEY_3 ftok(".", '3') /* Project identifier is '3' */

But be wary of using "." as the path for the file used for the key. You will get a different key in a different working directory. It would be better to use a fully-qualified path IMHO. Also, for a bullet-proof design, create a file which is uniquely associated with your project for the key and use that. Otherwise another project might happen to use the same directory for its key.

e.g.

(1) Create a key file (a zero length file created by touch in this case)

    touch /home/elsia/mystuff/mykey.dat

(2) Use the fully-qualified path to that file

#define KEYPATHNAME "/home/elsia/mystuff/mykey.dat"
#define TRANSITION_SHM_KEY_2 ftok(KEYPATHNAME, '2') /* Project identifier is '2' */
#define TRANSITION_SHM_KEY_3 ftok(KEYPATHNAME, '3') /* Project identifier is '3' */

NB: ftok must use a file that exists and is accessible on your filesystem. I think it does something clever with INODEs to get the key.
0
 
LVL 12

Expert Comment

by:stefan73
ID: 12307030
elsia,

You should also be aware that shmem is similar to files, that is, a block survives when a process calls exit(). Cehck with the "ipcs" command if a block already exists.

Your program will fail if the shared memory block already exists. You probably don't want that. So, you should first try to get a block with a key via shmget(), and if you fail (check proper errno), create a new block:

int shm_open(char* key, size_t size){
    int shmid;
    key_t num_key = ftok(".",key);

    shmid = shmget(num_key, size, IPC_EXCL | SHM_MODE);

    if(shmid < 0 && errno == ENOENT)
        shmid = shmget(num_key, size, IPC_EXCL | SHM_MODE | IPC_CREAT);
       
   
    if(shmid < 0 ){
        perror("shmget");
        fail();
    }
           
    return shmid;
}

With shmat and a NULL address, you cannot use pointers in your shared memory, as the address your memory block will map to is arbitrary. If you want to use pointers, you must use an explicit address. But then shmat can fail when some other block is already at this address.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12307066
Tell me, Stefan, what's the benefit of using do {...} while (0) in your macro? I vaguely remember seeing this used by AN Other, but I can't remember finding out what the rationale was.
0
 
LVL 12

Expert Comment

by:stefan73
ID: 12308964
rstaveley,
The macro behaves just like a single C instruction.

When you either define a macro as
#define something(val) { step1; step2; }
#define something step1; step2

and then do

if(conditon)
    something(123);

The 2nd case will give you a nasty surprise. And you can't do

if(conditon)
    something(123);
else
    something_else();

in both cases. Using a do{ }while(0) wrapper works. Unfortunately, you cannot generate values this way; but there is a nice GCC extension for this:
http://gcc.gnu.org/onlinedocs/gcc-3.4.2/gcc/Statement-Exprs.html#Statement-Exprs

Stefan
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12309034
Thanks Stefan! That's a good trick. I knew there would be method in your madness ;-)
0
 

Author Comment

by:elsia
ID: 12315986
hi stefan,

i tried running your script and it throws me invalid argument error:
SIZE = 0x01000000 (16MB)
Got shmid = 77935
Attached    0 blocks (   0MB) addr=0xe320c000 delta = 0x00000000 ( 0MB) sdelta = 0x00000000 (   0MB)
Failure in shmem_multimap.c:195 - shmat failure
addr != (void*)-1
22=Invalid argument

can you explain more about what is shmem_multimap.c doing and why do you need rand()?
0
 

Author Comment

by:elsia
ID: 12316012
hi rstaveley,

I tried creating multiple key from the same path, using a different project identifier but it doesnt work.. it is still refering to the same key althought the identifier is different. why is that so?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12316590
When you provide a fully qualified path with filename to the following, do you not get two different keys?

--------8<--------
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main(int argc,const char *argv[])
{
key_t key1,key2;

        key1 = ftok(*++argv,'1');
        key2 = ftok(*argv,'2');
        printf("key1 %x\n",key1);
        printf("key2 %x\n",key2);
}
--------8<--------

e.g.

   ./a.out /home/.
0
 

Author Comment

by:elsia
ID: 12316706
#define KEYPATHNAME "/share_mem/loadshm.dat"
#define KEYPATHNAME2 "/share_mem/loadshm.pc"
#define TRANSITION_SHM_KEY_2 ftok(KEYPATHNAME, '1')
#define TRANSITION_SHM_KEY_3 ftok("KEYPATHNAME2,'2')

void create_shm (key_t key, size_t size, int *id)
{
   int shm_id;

   shm_id = shmget (key, size, IPC_CREAT|IPC_EXCL|SHM_MODE);
   if (shm_id == -1)
   {
      perror ("Can't create share memory");
      return (1);
   }
   else
  {
      printf("Shared memory successfully created. SHM_ID:%x\n",key);
  }
  *id = shm_id;
}

void loadshm()
{
   char temp_key[25]="TRANSITION_SHM_KEY";
   key_t tmp;
   int r2;

   for (r2=2;r2<10;r2++)
   {
      sprintf(temp_key,"%s_%d",temp_key,r2);
      printf("\ntemp_key:%s\n",temp_key);
      tmp = (key_t)temp_key;
      result = create_share_mem (tmp, 1000000, &shm_id);

      if (result != 0)
      {
         printf ("%s Can't create share memory key = [%x]\n", generate_log_time (dtm, sizeof(dtm)), tmp);
         return (result);
      }

      strncpy(temp_key,NULL,strlen(temp_key));
      strcpy(temp_key,"TRANSITION_SHM_KEY");
   }       
}

main() {
  loadshm ();
}

the OUTPUT on screen is:
-------------------------------
temp_key:TRANSITION_SHM_KEY_2
Shared memory successfully created. SHM_ID:7b041f70

temp_key:TRANSITION_SHM_KEY_3
Can't create share memory : File exists
15/10/2004 15:02:02 Can't create share memory key = [7b041f70]

when i do a ipcs, i found that the mode of my shared memory is set to C.. which means that it needs to be cleared when the first attach is run

m  114799 0x7b041f70 -Cr--------   arbor13  arboradm

how do i change that mode?
0
 
LVL 12

Expert Comment

by:stefan73
ID: 12316809
Hmmm, maybe only Solaris supports multiple shmats of the same block.

But you can still mount a block read-only?
0
 
LVL 12

Expert Comment

by:stefan73
ID: 12316834
elsia,
> it needs to be cleared when the first attach is run

That makes sense - not to clear a newly created shmem block is a security problem. You could get memory previously released from another process.

I think you just need to shmat it for writing (shmat()'ing it for reading before writing something there doesn't make much sense.)

Consider populating the start of the block with a structure like:

struct shmem_header{
    char initialized;
    ...
}

then your read processes will know that it's virgin memory when initialized is 0.
0
 
LVL 12

Expert Comment

by:stefan73
ID: 12316852
elsia,
> can you explain more about what is shmem_multimap.c doing and why do
> you need rand()?

rand() was just to see that a write of a number into one attached block was reflected in all the other blocks, too.

About the invalid argument:
     EINVAL
           The shmid argument is not a valid shared memory  iden-
           tifier.

...that's probably the reason. You first need to run the other program to create a block with the 4711 key.
0
 

Author Comment

by:elsia
ID: 12317525
ok.. creating multiple key from different path, using a different project identifier is working now.. the reason why it wasnt working prevoiusly is because i created the shared mem in a loop. why cant i do that? it is redundant to manually create 9 shared memory and populate each with different data. how can i loop the shared memory creation and data loading using for loop?

i tried this but failed to create the 2nd memory:
transition.h
-------------
#define TRANSITION_ARRAY_SIZE (1000000)
#define KEYPATHNAME "/share_mem/mykey.dat"
#define KEYPATHNAME2 "."
:
#define TRANSITION_SHM_KEY_1 ftok(KEYPATHNAME, '1')
#define TRANSITION_SHM_KEY_2 ftok(KEYPATHNAME2, '2')
:

loadsharemem.pc
---------------------
int create_share_mem (key_t key, size_t size, int *id)
{
   int shm_id;
   shm_id = shmget (key, size, IPC_CREAT|IPC_EXCL|SHM_MODE);
   if (shm_id == -1)
   {
      perror ("Can't create share memory : shmget : ");
      return (1);
   }
   else
  {
      printf("Shared memory created successfully! shm key:%x\n",key);
  }
  *id = shm_id;
   return (0);
}

int main() {
for (i=1;i<10;i++)
{
   sprintf(temp_key,"%s_%d",temp_key,i);
   printf("\ntemp_key:%s\n",temp_key);
   tmp_r[i] = create_share_mem ((key_t)temp_key, 1000000, &shmid[i]);
   if (tmp_r[i] != 0)
   {
      printf ("Can't create share memory key = [%x] \n"(key_t)temp_key);
   }

   printf (" Now loading data for shmid [%d] ...\n",shmid[i]);
   tmp_r[i] = load_data (shmid[i], &line_count);
   if (tmp_r[i] != 0)
   {
       printf ("Can't load tabfile to share memory.\n");
   }
   printf ("Load data success, total records = [%d] share memory key = [%x]\n",line_count,(key_t)temp_key);

   strncpy(temp_key,NULL,strlen(temp_key));
   strcpy(temp_key,"TRANSITION_SHM_KEY");
}
}

the OUTPUT was:
---------------------
temp_key:TRANSITION_SHM_KEY_1
Shared memory created successfully! shm key:7b041f80
Now loading data for shmid [249967] ...
****
ORIGIN=[100], int_flag=[0]
sms_flg=[#], target=[40000]
key combination=[1000#]
****
ORIGIN=[100], int_flag=[1]
sms_flg=[#], target=[40010]
key combination=[1001#]
Load data success, total records = [2] share memory key = [7b041f80]

temp_key:TRANSITION_SHM_KEY_2
Can't create share memory : File exists
Can't create share memory key = [7b041f80] size = [20000264]


0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12317724
Try the following:

server.c
--------8<--------
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>

int main(int argc,const char *argv[])
{
const char *filename;
key_t key1,key2;
struct stat statbuf;
int shmid1,shmid2;
#define SIZE 512
volatile char *buf1 = NULL,*buf2 = NULL;

        if (argc != 2)
                return (fprintf(stderr,"Usage: %s {keyfile}\n",argv[0]),1);
        filename = argv[1];
        if (stat(filename,&statbuf) != 0)
                return (fprintf(stderr,"Error: Unable to stat keyfile %s\n",filename),1);
        fprintf(stderr,"Using keyfile %s (inode %x)\n",filename,statbuf.st_ino);
        key1 = ftok(filename,'1');key2 = ftok(filename,'2');
        fprintf(stderr
                ,"Shared memory key:\n"
                 "\tkeyfile: %s\n"
                 "\tkey '1': %x (project %x)\n"
                 "\tkey '2': %x (project %x)\n"
                ,filename,key1,'1',key2,'2');
        if ((shmid1 = shmget(key1,SIZE,IPC_CREAT|IPC_EXCL|0666)) == -1) {
                if (errno == EEXIST) {
                        if ((shmid1 = shmget(key1,SIZE,0666)) == -1)
                                return (perror("shmget existing key1"),1);
                        fprintf(stderr,"Got existing shmid1 %x\n",shmid1);
                }
                else
                        return (perror("shmget create key1"),1);
        }
        else
                fprintf(stderr,"Created shmid1 %x\n",shmid1);
        if ((shmid2 = shmget(key2,SIZE,IPC_CREAT|IPC_EXCL|0666)) == -1) {
                if (errno == EEXIST) {
                        if ((shmid2 = shmget(key2,SIZE,0666)) == -1)
                                return (perror("shmget existing key2"),1);
                        fprintf(stderr,"Got existing shmid2 %x\n",shmid2);
                }
                else
                        return (perror("shmget create key2"),1);
        }
        else
                fprintf(stderr,"Created shmid2 %x\n",shmid2);
        if ((buf1 = (char*)shmat(shmid1,NULL,0)) == NULL)
                return (perror("shmat shmid1"),1);
        if ((buf2 = (char*)shmat(shmid2,NULL,0)) == NULL)
                return (perror("shmat shmid2"),1);
        strcpy((char*)buf1,"Server has written into project '1'");
        strcpy((char*)buf2,"Server has written into project '2'");
        printf("The buffer for project '1' contains: %s\n",buf1);
        printf("The buffer for project '2' contains: %s\n",buf2);
        sleep(2);
        printf("The buffer for project '1' contains: %s\n",buf1);
        printf("The buffer for project '2' contains: %s\n",buf2);
}
--------8<--------

client.c
--------8<--------
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>

int main(int argc,const char *argv[])
{
const char *filename;
key_t key1,key2;
struct stat statbuf;
int shmid1,shmid2;
#define SIZE 512
volatile char *buf1 = NULL,*buf2 = NULL;

        if (argc != 2)
                return (fprintf(stderr,"Usage: %s {keyfile}\n",argv[0]),1);
        filename = argv[1];
        if (stat(filename,&statbuf) != 0)
                return (fprintf(stderr,"Error: Unable to stat keyfile %s\n",filename),1);
        fprintf(stderr,"Using keyfile %s (inode %x)\n",filename,statbuf.st_ino);
        key1 = ftok(filename,'1');key2 = ftok(filename,'2');
        fprintf(stderr
                ,"Shared memory key:\n"
                 "\tkeyfile: %s\n"
                 "\tkey '1': %x (project %x)\n"
                 "\tkey '2': %x (project %x)\n"
                ,filename,key1,'1',key2,'2');
        if ((shmid1 = shmget(key1,SIZE,0666)) == -1)
                return (perror("shmget key1"),1);
        fprintf(stderr,"Got existing shmid1 %x\n",shmid1);
        if ((shmid2 = shmget(key2,SIZE,0666)) == -1)
                return (perror("shmget key2"),1);
        fprintf(stderr,"Got existing shmid1 %x\n",shmid1);
        if ((buf1 = (char*)shmat(shmid1,NULL,0)) == NULL)
                return (perror("shmat shmid1"),1);
        if ((buf2 = (char*)shmat(shmid2,NULL,0)) == NULL)
                return (perror("shmat shmid2"),1);
        strcpy((char*)buf1,"Client has written into project '1'");
        strcpy((char*)buf2,"Client has written into project '2'");
}
--------8<--------

Build:
   gcc server.c -o server
   gcc client.c -o client

Execute:
    ./server . & sleep 1;./client .

(i.e. run the server in background mode, so you can run the client while the server is running, I am using the current directory as the keyfile in this example - you can of course use another file)

This leaves the shared memory in the system. The server is responsible for creating it if it isn't already created. It gives read+write permissions to the server and client. In this exmaple bothe the server and the client write, but only the server reads.

BTW... I've not bothered illustrating how to use shmctl to remove the shared memory (with IPC_RMID). You can, however, and should use the command line tool ipcrm to remove the shmids, when you've finished with this.

Does this help?
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12317814
BTW... if you are wondering what those 0666 (octal numbers are), they provide read+write permissions to user, group and all. You can of course change these to 0600, if you want to restrict read+write permissions to the user only (good idea if you are on a shared server).

I think that you were bitten by permissions and/or failing to remove the shared memory. The IPC_EXCL flag makes shmget fail if shared memory with the provided key already exists. You could use IPC_CREAT on its own in the server, but you probably ought to be aware of whether the shared memory is being reused or not, which is why my illustration uses IPC_CREAT|IPC_EXECL and then tests for errno EEXISTS, with a message that lets you know if the memory is newly created or being reused.
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 12317874
Here's how to get rid of the stderr stuff when you run that test.

./server . 2> /dev/null & sleep 1;./client . 2> /dev/null

You should get an output like the following:

--------8<--------
[rstaveley@mims shm]$ ./server . 2> /dev/null & sleep 1;./client . 2> /dev/null
[1] 26362
The buffer for project '1' contains: Server has written into project '1'
The buffer for project '2' contains: Server has written into project '2'
[rstaveley@mims shm]$ The buffer for project '1' contains: Client has written into project '1'
The buffer for project '2' contains: Client has written into project '2'

[1]+  Exit 73                 ./server . 2>/dev/null
--------8<--------

The output can be confusing if you are not used to running applications in background mode, but in essence you see the shared memory displayed by the server twice; once before the client has written to it and once afterwards.
0
 

Author Comment

by:elsia
ID: 12337221
ok.. finally got my shared memory running.. but i am facing some problem with the memory size. i tried to load about 15000 records into 5 of the 9 shared memory created but the program throw a core dump error.

When i tried to increase the shared memory size from 1000000 to 1500000, i got an Invalid Argument error.
shmget (key, 1000000, IPC_CREAT|IPC_EXCL|SHM_MODE);


I tried to increase the number of my array too and i end up getting:
error 1809: size of aggregate exceeds compiler limits.

Have checked the user limit in unix and it's unlimited.. What is wrong? How do i fix it?


0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

726 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question