3

I have created a data structure in my C program as follows,

typedef struct {
  int *array;
  size_t used;
  size_t size;
} Array;

void initArray(Array *a, size_t initialSize) {
  a->array = (int *)malloc(initialSize * sizeof(int));
  a->used = 0;
  a->size = initialSize;
}

void insertArray(Array *a, int element) {
  if (a->used == a->size) {
    a->size *= 2;
    a->array = (int *)realloc(a->array, a->size * sizeof(int));
  }
  a->array[a->used++] = element;
}

void freeArray(Array *a) {
  free(a->array);
  a->array = NULL;
  a->used = a->size = 0;
}

Then I'm adding some data to that data structure from an external text file using following method,

Array read_ints (const char* file_name)
{
  Array numbers;
  initArray(&numbers,5);

  FILE* file = fopen (file_name, "r");
  int i = 0;
  int count = 0;

  fscanf (file, "%d,", &i);  
  insertArray(&numbers,i);  
  while (!feof (file))
    {  
      //printf ("%d ", i);
      fscanf (file, "%d,", &i);
      insertArray(&numbers,i);      
    }
  fclose (file);
  return numbers;        
}

Now what I need to do is, I need to make the 'Array' data structure a shared memory portion so that the both child and parent processes on my program could access that data structure. I have no idea on how to make it a shared memory. I'm aware that the shmget() system call can be used to get a shared memory in UNIX environment. But i cannot see how to use that system call on this scenario. Please help me.

  • 1
    Standard Warning : Please [do not cast](http://stackoverflow.com/q/605845/2173917) the return value of `malloc()` and family in `C`. – Sourav Ghosh Apr 17 '15 at 06:05
  • @SouravGhosh Thanks for that additional knowledge. But please help me with the question that I asked. – Shiran S Ekanayake Apr 17 '15 at 06:12
  • 1
    See also [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong). That's simply not the right way to do it: `while (fscanf(file, "%d", &i) == 1) insertArray(&numbers, i);` would be correct. – Jonathan Leffler Apr 17 '15 at 06:26
  • Are you creating the memory before or after forking? What is the child told about the shared memory? Are the parent and child running the same executable. As to creating shared memory, you need to agree on the key, read the data as you do so you know how big a chunk of memory you need, then create the shared memory and copy the array into the shared memory. You then need to coordinate access to the shared memory. Etc. It is simple but tedious use of the `shm*()` functions, plus `ftok()`, perhaps. But details depend in part on the information you've not given, such as process structure. – Jonathan Leffler Apr 17 '15 at 06:31
  • @JonathanLeffler I want to create the shared memory before forking. What I'm doing is I store some integer values on that data structure using an external file and I want to make that data structure a shared memory after storing that values, so that the child processes can access those values and do some calculations on them. Here parent and child are not running the same executable. Code segments of parent and child may differ. – Shiran S Ekanayake Apr 17 '15 at 06:48
  • If you are under some debian or red hat and deriveds you can `fread` and `fwrite` on `/dev/shm` – David Ranieri Apr 17 '15 at 07:02
  • 2
    Given that parent and child are separate binaries, they're going to have to agree on something to get the correct shared memory segment. The parent creates the segment after reading the data from the file, then forks and execs the child. The child either knows or is told which shared memory segment to use. The `ftok()` function requires the two processes to agree on a file name. Or the parent can pass the exec'd child a string to identify the shared memory segment (as a segment ID, or as the name of the file, or …). Go to it: `shmget()`, `shmat()`, `shmdt()`, `shmctl()`… – Jonathan Leffler Apr 17 '15 at 07:29
  • @JonathanLeffler Could you please provide an example as an answer so that I can understand it more clearly. I need an example rather than explaining theories behind it. – Shiran S Ekanayake Apr 17 '15 at 07:37

1 Answers1

2

Main code — shm-master.c

#include "posixver.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include "so-stderr.h"

enum { DEFAULT_SHM_SIZE = 65536 };
enum { DEFAULT_FTOK_ID = 0 };
static const char default_filename[] = "/etc/passwd";

static const char usestr[] = "[-adx][-f file][-s size][-i id]";
static const char optstr[] = "adf:s:x";

int main(int argc, char **argv)
{
    int aflag = 0;
    int xflag = 0;
    int dflag = 0;
    int id = DEFAULT_FTOK_ID;
    size_t size = DEFAULT_SHM_SIZE;
    const char *file = default_filename;
    int opt;

    err_setarg0(argv[0]);
    while ((opt = getopt(argc, argv, optstr)) != -1)
    {
        switch (opt)
        {
        case 'a':
            aflag = 1;
            break;
        case 'd':
            dflag = 1;
            break;
        case 'f':
            file = optarg;
            break;
        case 'i':
            id = atoi(optarg);
            break;
        case 's':
            size = strtoull(optarg, 0, 0);
            if (size == 0)
                err_error("Invalid size (%s) evaluates to zero\n", optarg);
            break;
        case 'x':
            xflag = 1;
            break;
        default:
            err_usage(usestr);
        }
    }

    if (aflag + dflag + xflag > 1)
        err_error("%d of 3 mutually exclusive options -a, -d and -x specified\n", aflag + dflag + xflag);

    printf("ID: %d, File: %s\n", id, file);
    key_t key = ftok(file, id);
    printf("Key: 0x%.8" PRIX64 "\n", (uint64_t)key);
    int shmflg = S_IRUSR | S_IWUSR;
    if (!aflag && !dflag)
        shmflg |= IPC_CREAT;
    if (xflag)
        shmflg |= IPC_EXCL;

    int shmid = shmget(key, size, shmflg);
    if (shmid < 0)
        err_syserr("Failed to get shared memory ID: ");
    printf("ShmID: %d\n", shmid);

    if (dflag)
    {
        struct shmid_ds buf;
        int rc = shmctl(shmid, IPC_RMID, &buf);
        if (rc < 0)
            err_syserr("Failed to delete shared memory: ");
        printf("Shared memory removed\n");
    }
    else
    {
        void *space = shmat(shmid, 0, 0);
        if (space == (void *)-1)
            err_syserr("Failed to attach to shared memory: ");
        printf("Shared memory allocated at 0x%" PRIXPTR "\n", (uintptr_t)space);
        memset(space, '\0', size);
        int rc = shmdt(space);
        if (rc != 0)
            err_syserr("Failed to detach from shared memory: ");
        printf("Detached from shared memory\n");
    }

    return 0;
}

Library code — so-stderr.h

#ifndef SO_STDERR_H_INCLUDED
#define SO_STDERR_H_INCLUDED

extern void err_setarg0(const char *arg0);
extern void err_error(const char *fmt, ...);
extern void err_syserr(const char *fmt, ...);

#endif /* SO_STDERR_H_INCLUDED */

Library code — so-stderr.c

#include "so-stderr.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const char *argv0 = "**undefined**";

void err_setarg0(const char *arg0)
{
  argv0 = arg0;
}

void err_error(const char *fmt, ...)
{
  fprintf(stderr, "%s: ", argv0);
  va_list args;
  va_start(args, fmt);
  vfprintf(stderr, fmt, args);
  va_end(args);
  exit(EXIT_FAILURE);
}

void err_syserr(const char *fmt, ...)
{
  int errnum = errno;
  fprintf(stderr, "%s: ", argv0);
  va_list args;
  va_start(args, fmt);
  vfprintf(stderr, fmt, args);
  va_end(args);
  if (errnum != 0)
    fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
  putc('\n', stderr);
  exit(EXIT_FAILURE);
}

Configuration header — posixver.h

You can adjust the higher version number to 700 (for POSIX 2008/2013) on many systems, but it probably isn't a good idea on Mac OS X, even with 10.10.3 Yosemite.

#ifndef JLSS_ID_POSIXVER_H
#define JLSS_ID_POSIXVER_H

#if !defined(_XOPEN_SOURCE) && !defined(_POSIX_C_SOURCE)
#if __STDC_VERSION__ >= 199901L
#define _XOPEN_SOURCE 600
#else
#define _XOPEN_SOURCE 500
#endif

#endif

In all cases, the 'real' code includes (a few) comments and other explanations of what's going on. The production stderr.h and stderr.c are more complex than the minimal version shown, but for many purposes, what's shown is equivalent to the production version.

Example run

$ ./shm-master -H
./shm-master: invalid option -- 'H'
Usage: ./shm-master [-adx][-f file][-s size][-i id]
$ ./shm-master -ax
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -dx
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -da
./shm-master: 2 of 3 mutually exclusive options -a, -d and -x specified
$ ./shm-master -dax
./shm-master: 3 of 3 mutually exclusive options -a, -d and -x specified
$ ipcs -m | grep -v '^0x00000000 '

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x620010f7 0          root       660        557920     4
0x63002725 32769      root       666        82164      3

$ ./shm-master -x
ID: 0, File: /etc/passwd
Key: 0x0000009F
ShmID: 44793901
Shared memory allocated at 0x7F29AC43A000
Detached from shared memory
$ ipcs -m | grep -v '^0x00000000 '

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x620010f7 0          root       660        557920     4
0x63002725 32769      root       666        82164      3
0x0000009f 44793901   jleffler   600        65536      0

$ ./shm-master -d
ID: 0, File: /etc/passwd
Key: 0x0000009F
ShmID: 44793901
Shared memory removed
$ ipcs -m
$ grep -v '^0x00000000 '

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x620010f7 0          root       660        557920     4
0x63002725 32769      root       666        82164      3

$ ./shm-master -f /home/jleffler/soq/shm-master -a
./shm-master: Failed to get shared memory ID: (2: No such file or directory)
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
$ ./shm-master -f /home/jleffler/soq/shm-master -d
./shm-master: Failed to get shared memory ID: (2: No such file or directory)
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
$ ./shm-master -f /home/jleffler/soq/shm-master -x
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44826669
Shared memory allocated at 0x7FA1488CA000
Detached from shared memory
$ ./shm-master -f /home/jleffler/soq/shm-master -d
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44826669
Shared memory removed
$ ./shm-master -f /home/jleffler/soq/shm-master -x
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44859437
Shared memory allocated at 0x7F93005EC000
Detached from shared memory
$ shmid=$(./shm-master -f /home/jleffler/soq/shm-master -a sed -n '/ShmID: /s///p')
$ ipcs -m -i $shmid

Shared memory Segment shmid=44859437
uid=199484      gid=5000        cuid=199484     cgid=5000
mode=0600       access_perms=0600
bytes=65536     lpid=31202      cpid=31200      nattch=0
att_time=Fri Apr 17 11:37:06 2015
det_time=Fri Apr 17 11:37:06 2015
change_time=Fri Apr 17 11:37:06 2015

$ ./shm-master -f /home/jleffler/soq/shm-master -d
ID: 0, File: /home/jleffler/soq/shm-master
Key: 0x00010FB9
ShmID: 44859437
Shared memory removed
$

Incidentally, the ipcs option -i id is a Linux extension over the POSIX specification for ipcs, and the option is not available on, for example, Mac OS X (BSD). The nearest equivalent would be something like ipcs -m -a | grep "$shmid", which isn't perfect. The grep -v '^0x00000000 ' operations eliminate the private shared memory segments (there were a lot of them in use on the machine I did the testing on).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278