Using Shared Memory

Once the shared memory is attached, your process can use it like any other region of memory. However, since multiple processes can see this same region of memory, care must be exercised when changing its content.

In the globvar utility, one semaphore is used as the locking semaphore. Whenever the shared memory is searched or modified, the globvar utility waits on the semaphore first (recall that it was initialized to the value of 1). This ensures that no more than one process at a time will be working with the shared memory. When the task has been completed, the semaphore is notified to release the lock.

It should be noted that some values were accessed in the shared memory without the locking semaphore. Examine lines 15–19 in Listing 25.4. These lines declare the structure used for the global memory.

Code Listing 25.4. globvar.h—The Global globvar Utility Definitions
1:   /* globvar.h */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <string.h>
7:   #include <errno.h>
8:   #include <sys/types.h>
9:   #include <sys/ipc.h>
10:  #include <sys/shm.h>
11:  #include <sys/sem.h>
12:
13:  #define GLOBVARENV  "GLOBVAR"   /* Environment variable */
14:
15:  typedef struct {
16:      int     semid;              /* Semaphore's IPC ID */
17:      int     size;               /* Size of the vars[] array */
18:      char    vars[1];            /* Start of variable storage */
19:  } GlobVars;
20:
21:  extern int shmid;               /* IPC ID of shared memory region */
22:  extern int shm_size;            /* Size of shared memory region */
23:  extern GlobVars *globvars;      /* Shared memory region */
24:  extern int semid;               /* IPC ID of the locking semaphore set */
25:
26:  extern void create_vars(int shm_size);
27:  extern void attach_vars(void);
28:  extern char *get_var(const char *varname);
29:  extern void set_var(const char *varname,const char *value);
30:  extern void destroy_vars(void);
31:  extern void glob_lock(void);
32:  extern void glob_unlock(void);
33:  extern void unset_var(const char *varname);
34:
35:  #ifndef HAVE_SEMUN              /* Does sys/sem.h define this? */
36:
37:  union semun {
38:      int     val;                /* Value */
39:      struct  semid_ds *buf;      /* IPC_STAT info */
40:      u_short *array;             /* Array of values */
41:  };
42:
43:  #endif
44:
45:  /* End globvar.h */

The members semid and size are established when the global pool is initially created. After this, these values never change. Because these values never change, they are safe to reference without a locking semaphore. Dynamic content begins at the member vars[] within the GlobVars structure. To access its content safely, you must use the locking semaphore in the utility program.

The source module globlk.c is shown in Listing 25.5, which implements the shared memory locking routines.

Code Listing 25.5. globlk.c—The Semaphore Locking Routines
1:   /* globlk.c */
2:
3:   #include "globvar.h"
4:
5:   static struct sembuf wait = { 0, -1, SEM_UNDO };
6:   static struct sembuf notify = { 0, +1, SEM_UNDO };
7:
8:   /*
9:    * Perform a semaphore operation :
10:   */
11:  static void
12:  do_semop(struct sembuf *op) {
13:      int z;                      /* status code */
14:
15:      do  {
16:          z = semop(globvars->semid,op,1);
17:      } while ( z == -1 && errno == EINTR );
18:
19:      if ( z ) {
20:          fprintf(stderr,"%s: semop(2)
",strerror(errno));
21:          exit(1);
22:      }
23:  }
24:
25:  /*
26:   * Wait on semaphore to lock shared memory :
27:   */
28:  void
29:  glob_lock(void) {
30:
31:      do_semop(&wait);
32:  }
33:
34:  /*
35:   * Notify semaphore to unlock shared memory :
36:   */
37:  void
38:  glob_unlock(void) {
39:
40:      do_semop(&notify);
41:  }

Lines 5 and 6 define the semaphore lock (wait) and unlock (notify) operations. The function do_semop() performs the actual semaphore operation by calling upon semop(2) in line 16. The functions glob_lock() and glob_unlock() are simply wrapper routines for the do_semop() function.

Listing 25.6 shows the module globget.c that fetches the value of a global variable.

Code Listing 25.6. globget.c—The Source Module That Looks Up a Global Variable in Shared Memory
1:   /* globget.c */
2:
3:   #include "globvar.h"
4:
5:   /*
6:    * Return the string pointer for a variable's value :
7:    */
8:   char *
9:   get_var(const char *varname) {
10:      char *cp;                   /* Scanning pointer */
11:      int nlen = strlen(varname); /* Length of variable name */
12:
13:      for ( cp = &globvars->vars[0]; *cp; cp += strlen(cp) + 1 )
14:          if ( !strncmp(varname,cp,nlen) && cp[nlen] == '=')
15:              return cp + nlen + 1; /* Pointer to it's value */
16:
17:      return NULL;                /* Variable not found */
18:  }

The main() program calls the glob_lock() routine before calling get_var() shown in Listing 25.6. The function get_var() then searches the shared memory for the variable requested in varname. The global variables are stored in shared memory as a list of null terminated strings, in the form VARIABLE=VALUE. The end of the variable list is marked by an additional null byte.

Listing 25.7 shows the main() program that calls get_var().

Code Listing 25.7. globvar.c—The Main Program for the globvar Utility
1:   /* globvar.c */
2:
3:   #include "globvar.h"
4:
5:   int shmid = -1;                 /* IPC ID of shared memory */
6:   GlobVars *globvars = NULL;      /* Shared memory region */
7:
8:   /*
9:    * Usage instructions :
10:   */
11:  static void
12:  usage(void) {
13:
14:      puts("globvar [-i] [-s size] [-e] [-u] [-r] [-c]"
15:          " var... var=value...");
16:      puts("Options:");
17:      puts("    -i          Initialize new globvar pool");
18:      puts("    -s size     Size of this pool, in bytes");
19:      puts("    -e          Dump all values (after changes)");
20:      puts("    -u          Unset all named variables");
21:      puts("    -r          Remove this pool of values");
22:      puts("    -c          Clear all variables");
23:      puts("    -h          This info.");
24:  }
25:
26:  /*
27:   * Main program :
28:   */
29:  int
30:  main(int argc,char **argv) {
31:      int rc = 0;                 /* Return code */
32:      int optch;                  /* Option character */
33:      int cmdopt_i = 0;           /* -i to create var pool */
34:      int cmdopt_c = 0;           /* -c to clear variables */
35:      int cmdopt_r = 0;           /* -r to remove pool */
36:      int cmdopt_e = 0;           /* -D to dump the variables */
37:      int cmdopt_u = 0;           /* -u to unset named variables */
38:      int cmdopt_h = 0;           /* -h usage option */
39:      int cmdopt_s = 4096;        /* Default for -s */
40:      char *cp, *ep;              /* Character pointers */
41:      unsigned long ul;           /* Converted ulong */
42:      const char cmdopts[] = "hirs:ecu";
43:
44:      /*
45:       * Parse command line options :
46:       */
47:      while ( (optch = getopt(argc,argv,cmdopts)) != -1 )
48:          switch ( optch ) {
49:          case 'c':              /* -c to clear variables */
50:              cmdopt_c = 1;
51:              break;
52:
53:          case 'i':              /* -i initialize a new pool */
54:              cmdopt_i = 1;
55:              break;
56:
57:          case 'e':              /* -e to dump all variables like env */
58:              cmdopt_e = 1;
59:              break;
60:
61:          case 'r':              /* -r to remove the pool */
62:              cmdopt_r = 1;
63:              break;
64:
65:          case 's':              /* -s size; affects -i */
66:              ul = strtoul(optarg,&ep,0);
67:              if ( *ep ) {
68:                  fprintf(stderr,"Bad size: -s %s
",optarg);
69:                  rc = 1;
70:              } else
71:                  cmdopt_s = (int) ul;
72:              break;
73:
74:          case 'u':              /* -u to unset all listed variables */
75:              cmdopt_u = 1;
76:              break;
77:
78:          case 'h':              /* -h to request help */
79:              cmdopt_h = 1;
80:              break;
81:
82:          default  :
83:              rc = 1;
84:          }
85:
86:      /*
87:       * Give usage display if errors or -h :
88:       */
89:      if ( cmdopt_h || rc ) {
90:          usage();
91:          if ( rc )
92:              return rc;
93:      }
94:
95:      /*
96:       * Create/Access global variable pool :
97:       */
98:      if ( cmdopt_i ) {
99:          /*
100:          * Create a new shared memory variable pool :
101:          */
102:         create_vars(cmdopt_s);
103:         printf("%d
",shmid);
104:
105:     } else if ( (cp = getenv(GLOBVARENV)) != NULL ) {
106:         /*
107:          * Extract IPC key from GLOBVAR environment variable :
108:          */
109:         ul = strtoul(cp,&ep,0);
110:         if ( *ep ) {
111:             fprintf(stderr,"%s has bad IPC key
",cp);
112:             return 1;
113:         }
114:
115:         shmid = (int)ul;
116:         attach_vars();
117:     }
118:
119:     /*
120:      * Do we have enough information to find the pool?
121:      */
122:     if ( !globvars ) {
123:         fprintf(stderr,"You must use -i or define"
124:             " environment variable %s.
",GLOBVARENV);
125:         return 1;
126:     }
127:
128:     /*
129:      * -c clears all variables :
130:      */
131:     if ( cmdopt_c ) {
132:         glob_lock();
133:         globvars->vars[0] = globvars->vars[1] = 0;
134:         glob_unlock();
135:     }
136:
137:     /*
138:      * Now process variable requests :
139:      */
140:     for ( ; optind < argc; ++optind ) {
141:         cp = strchr(argv[optind],'='),
142:
143:         glob_lock();
144:
145:         if ( !cp ) {
146:             /*
147:              * Just have a variable name, so return value or unset :
148:              */
149:             if ( !cmdopt_u ) {
150:                 if ( (cp = get_var(argv[optind])) != NULL ) {
151:                     puts(cp);   /* Just emit value of variable */
152:                 } else {
153:                     fprintf(stderr,"Variable %s not found
",argv[optind]);
154:                     rc = 1;
155:                 }
156:             } else
157:                 unset_var(argv[optind]);
158:
159:         } else {
160:             /*
161:              * Change the variable's value :
162:              */
163:             *cp = 0;
164:             set_var(argv[optind],++cp);
165:         }
166:
167:         glob_unlock();
168:     }
169:
170:     /*
171:      * Dump all variables (for debugging) :
172:      */
173:     if ( cmdopt_e ) {
174:         glob_lock();
175:         for ( cp=&globvars->vars[0]; *cp; cp+=strlen(cp)+1 )
176:             puts(cp);
177:         glob_unlock();
178:     }
179:
180:     /*
181:      * If -r option, destroy the global variable pool :
182:      */
183:     if ( cmdopt_r )
184:         destroy_vars();
185:
186:     return rc;
187: }

The get_var() function is called when a variable name is listed on the command line by itself (lines 150 and 151). Note that the shared memory is locked in line 143 and unlocked in line 167. Using the locking semaphore permits several processes to update the same global variable pool without corruption.

Listing 25.8 shows the module globset.c that implements the variable assignment functions.

Code Listing 25.8. globset.c—The Implementation of the globvar Variable Assignment Functions
1:   /* globset.c */
2:
3:   #include "globvar.h"
4:
5:   /*
6:    * Change the value of a global variable :
7:    */
8:   void
9:   set_var(const char *varname,const char *value) {
10:      int z;                          /* status code */
11:      char *var = get_var(varname);   /* Locate variable if it exists */
12:      char *cp;                       /* utility char pointer */
13:      int nlen = strlen(varname);     /* Length of variable name */
14:      int vlen = strlen(value);       /* Length of variable's value */
15:      int in_use = 0;                 /* Bytes in use */
16:      int avail = globvars->size;     /* Bytes available */
17:
18:      if ( var ) {                     /* Does variable exist? */
19:
20:          in_use = (int)( var - &globvars->vars[0] ) + 1;
21:          avail -= in_use;            /* Bytes available for new value */
22:
23:          z = strlen(var + nlen + 1); /* Length of current string */
24:          if ( vlen > avail + z )
25:              goto nospc;             /* Insufficient space */
26:
27:          /*
28:           * Now delete the variable :
29:           */
30:          var = var - nlen - 1;       /* Point to start of entry */
31:
32:          for ( cp=var+strlen(var)+1; *cp; var += z, cp += z ) {
33:              z = strlen(cp) + 1;     /* Length of next value */
34:              memmove(var,cp,z);      /* Move it up */
35:          }
36:
37:      } else {
38:          /*
39:           * Find end of global storage :
40:           */
41:          for ( var = &globvars->vars[0]; *var; var += strlen(var) + 1 )
42:              ;
43:
44:          in_use = (int)( var - &globvars->vars[0] ) + 1;
45:          avail -= in_use;            /* Bytes available for new value */
46:
47:          if ( nlen + 1 + vlen > avail )
48:              goto nospc;
49:      }
50:
51:      /*
52:       * Append VARIABLE=VALUE to end of shared region :
53:       */
54:      strcpy(var,varname);    /* Variable name */
55:      var += nlen;            /* Point past variable name */
56:      *var++ = '=';           /* The equal sign */
57:      strcpy(var,value);      /* The variable's value */
58:      var[vlen+1] = 0;        /* 2 null bytes mark the end */
59:
60:      return;                 /* Successful */
61:
62:      /*
63:       * Insufficient space to store this variable :
64:       */
65:  nospc:
66:      fprintf(stderr,"%s: %s='%s'
",strerror(ENOSPC),varname,value);
67:      exit(1);
68:  }

The set_var() routine first looks up the variable in line 11. If the variable exists in the pool, the space for the new value is computed in lines 20 and 21. If the new value fits into the space remaining, the current variable is deleted by a memory move loop in lines 32–35. After this point, the remaining code treats the variable as if it were a new value.

If the variable does not exist yet, the space calculations are performed in lines 44–48. The new variable and value is appended to the shared memory region in lines 54–57. Line 58 puts a second null byte at the end to mark the end of the variables list.

Listing 25.9 shows the module that is responsible for removing a variable by name, invoked by the globvar -u option.

Code Listing 25.9. globun.c—The Unset Feature of globvar Is Implemented by globun.c
1:   /* globun.c */
2:
3:   #include "globvar.h"
4:
5:   /*
6:    * Unset a variable :
7:    */
8:   void
9:   unset_var(const char *varname) {
10:      int z;                          /* status code */
11:      char *var = get_var(varname);   /* Locate variable if it exists */
12:      char *cp;                       /* utility char pointer */
13:      int nlen = strlen(varname);     /* Length of variable name */
14:
15:      if ( !var )
16:          return;                     /* Variable is already unset */
17:
18:      /*
19:       * Now delete the variable :
20:       */
21:      var = var - nlen - 1;       /* Point to start of entry */
22:
23:      for ( cp=var+strlen(var)+1; *cp; var += z, cp += z ) {
24:          z = strlen(cp) + 1;     /* Length of next value */
25:          memmove(var,cp,z);      /* Move it up */
26:      }
27:
28:      *var = 0;                   /* two nulls mark the end of vars */
29:
30:      return;                     /* Successful */
31:  }

The variable name is searched in line 11. If it is not found, the function returns in line 16. Otherwise, the for loop of lines 23–26 moves the strings to replace the area formerly occupied by the deleted variable. Line 28 adds the second null byte to mark the new end of the variable list.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.175.180