Controlling Semaphores

The previous sections showed how semaphore sets could be created and accessed. This section will show you how you can query the set that you have and make changes to it.

Querying Semaphore Sets

The semctl(2) function provides the IPC_STAT command to allow you to retrieve information about the semaphore set. Of particular interest is the value that indicates how many semaphores are in the set and the permission information. Information for IPC_STAT is returned in the structure semid_ds, which is shown in the following synopsis:

struct semid_ds {
    struct ipc_perm sem_perm;  /* operation permission struct */
    struct sem *sem_base;      /* pointer to first semaphore in set */
    u_short sem_nsems;         /* number of sems in set */
    time_t  sem_otime;         /* last operation time */
    time_t  sem_ctime;         /* last change time */
};

The structure definition for ipc_perm is repeated for your convenience, as follows:

struct ipc_perm {
    ushort  cuid;      /* creator user id */
    ushort  cgid;      /* creator group id */
    ushort  uid;       /* user id */
    ushort  gid;       /* group id */
    ushort  mode;      /* r/w permission */
    ushort  seq;       /* sequence # (to generate unique msg/sem/shm id) */
    key_t   key;       /* user specified msg/sem/shm key */
};

In order to receive this information, you must make use of the fourth argument, of type semun. The POSIX standard states that you must define the union semun in your own code. Many releases of UNIX define it for you in the include files. The union is defined in the include file semop.h for the utility, which is shown in Listing 24.3.

Code Listing 24.3. semop.h—The Include File That Is Used by the semop Utility Program
1:   /* semop.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 <pwd.h>
10:  #include <grp.h>
11:  #include <sys/stat.h>
12:  #include <sys/ipc.h>
13:  #include <sys/sem.h>
14:
15:  #ifndef HAVE_SEMUN              /* Does sys/sem.h define this? */
16:
17:  union semun {
18:      int     val;                /* Value */
19:      struct  semid_ds *buf;      /* IPC_STAT info */
20:      u_short *array;             /* Array of values */
21:  };
22:
23:  #endif
24:
25:  #define MAX_NSET    16          /* Max value for n_sem */
26:
27:  extern int semid;               /* Semaphore IPC ID */
28:  extern int n_sem;               /* # of semaphores to a set */
29:  extern struct semid_ds sembuf;  /* The last IPC_STAT info */
30:
31:  extern void get_set(int optch,key_t key,int createflg);
32:  extern void ctl_semop(int optch,const char *optarg,
33:      int sems[],int array[],int flags[],int n);
34:  extern void ctl_stat(int optch,int rptflag);
35:  extern void ctl_chmod(int optch,mode_t mode);
36:  extern void ctl_chown(int optch,const char *user_id);
37:  extern void ctl_chgrp(int optch,const char *group);
38:  extern void ctl_rmid(int optch);
39:  extern void ctl_getval(int optch,int semx);
40:  extern void ctl_getall(int optch);
41:  extern void ctl_setval(int optch,int semx,int value);
42:  extern void ctl_setall(int optch,int array[]);
43:  extern void ctl_get(int optch,int cmd,int semx);
44:
45:  extern void usage(void);
46:  extern int cvt2ulong(const char *str,unsigned long *ulp);
47:  extern int cvt2array(const char *str,int array[],const char *delim);
48:  extern int cvt2semops(const char *str,int sems[],int array[],int flags[]);
49:  extern void report(int optch,key_t key,int flags[]);
50:
51:  /* End semop.h */

The union is only compiled if the C macro HAVE_SEMUN is not defined. This is defined in the Makefile as the compiler command-line argument -DHAVE_SEMUN. Remove this option if you need the union defined.

Listing 24.4 shows the how the semctl(2) function is called for the IPC_STAT command.

Code Listing 24.4. semstat.c—Source Module That Uses the IPC_STAT Command of semctl(2)
1:   /* semstat.c */
2:
3:   #include "semop.h"
4:
5:   struct semid_ds sembuf;     /* Used for IPC_STAT/IPC_SET */
6:
7:   /*
8:    * Return user ID string :
9:    */
10:  static char *
11:  user_id(uid_t uid) {
12:      struct passwd *pw = getpwuid(uid);
13:
14:      return !pw ? "?" : pw->pw_name;
15:  }
16:
17:  /*
18:   * Return group ID string :
19:   */
20:  static char *
21:  group_id(gid_t gid) {
22:      struct group *gr = getgrgid(gid);
23:
24:      return !gr ? "?" : gr->gr_name;
25:  }
26:
27:  /*
28:   * Get status on semaphore set :
29:   */
30:  void
31:  ctl_stat(int optch,int reportflg) {
32:      int z;
33:      union semun un;
34:
35:      un.buf = &sembuf;
36:
37:      z = semctl(semid,0,IPC_STAT,un);
38:      if ( z == -1 ) {
39:          fprintf(stderr,"%s: semctl(semid=%d,IPC_STAT)
",
40:              strerror(errno),semid);
41:          exit(1);
42:      }
43:
44:      if ( reportflg == 1 ) {
45:          printf("  -%c { 
"
46:              "    sem_nsems = %d
"
47:              "    sem_perm { 
"
48:              "      cuid = %d (%s)
"
49:              "      cgid = %d (%s)
",
50:              optch,
51:              (int)sembuf.sem_nsems,
52:              (int)sembuf.sem_perm.cuid,
53:              user_id(sembuf.sem_perm.cuid),
54:              (int)sembuf.sem_perm.cgid,
55:              group_id(sembuf.sem_perm.cgid));
56:          printf(
57:              "      uid  = %d (%s)
"
58:              "      gid  = %d (%s)
"
59:              "      mode = 0%03o
"
60:              "      key  = 0x%08lX
"
61:              "    };
"
62:              "  };
",
63:              (int)sembuf.sem_perm.uid,
64:              user_id(sembuf.sem_perm.uid),
65:              (int)sembuf.sem_perm.gid,
66:              group_id(sembuf.sem_perm.gid),
67:              (int)sembuf.sem_perm.mode & 0777,
68:              (long)sembuf.sem_perm.key);
69:          fflush(stdout);
70:      }
71:
72:      /*
73:       * Check that our idea of set size agrees with actual :
74:       */
75:      if ( reportflg == -1 )          /* -a option call? */
76:          n_sem = sembuf.sem_nsems;   /* Yes, adjust for actual count */
77:
78:      else if ( n_sem != sembuf.sem_nsems ) {
79:          fflush(stdout);
80:          fprintf(stderr,"  WARNING: # semaphores in set is %d
",
81:              sembuf.sem_nsems);
82:          fflush(stderr);
83:          n_sem = sembuf.sem_nsems;   /* Adjust for actual count */
84:      }
85:  }

The section of code that is important to the discussion is found in lines 31–42. The union named un is declared in line 33. The address of the external buffer of type struct semid_ds is pointed to in the union in line 35. The union un is passed as the fourth argument to the semctl(2) function in line 37.

The struct semid_ds sembuf is declared externally in this module in line 5. The values placed here are also used by other modules that perform the IPC_SET operation.

The following example uses the utility program ./semop to create a new semaphore set, and invokes IPC_STAT on it.

$ ./semop -k0xFEEDF00D -c3 -s
  -c 0xFEEDF00D => IPC ID 196608
  -c 3 : Created semaphore set -k 0xFEEDF00D
  -s {
    sem_nsems = 3
    sem_perm {
      cuid = 1001 (ehg)
      cgid = 1001 (ehg)
      uid  = 1001 (ehg)
      gid  = 1001 (ehg)
      mode = 0640
      key  = 0xFEEDF00D
    };
  };
$

The options -k and -c create the set. Option -s requests the IPC_STAT command, and the most important values are reported to standard output. The mode value was affected by the current umask(2) in effect, due to the umask(2) calls that were made in lines 17–19 of Listing 24.1. The mask that was in effect was

$ umask
0027
$

Keep this semaphore set around for the subsequent sections.

Changing Semaphore Access

After the semaphore set is created, it may be necessary to modify the ownership of the semaphore or change its permission bits. The IPC_SET command of the semctl(2) function allows you to make these changes. Listing 24.5 shows how IPC_SET is used.

Code Listing 24.5. semchmod.c—Source Module That Uses the IPC_SET Command of semctl(2)
1:   /* semchmod.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_chmod(int optch,mode_t mode) {
7:       int z;
8:       union semun un;
9:
10:      un.buf = &sembuf;                   /* Pointer to buffer */
11:      sembuf.sem_perm.mode = mode;        /* Change the mode */
12:
13:      z = semctl(semid,0,IPC_SET,un);     /* Change mode value */
14:      if ( z == -1 ) {
15:          fprintf(stderr,"%s: semctl(semid=%d,IPC_SET)
",
16:              strerror(errno),semid);
17:          exit(1);
18:      }
19:
20:      printf("  -%c 0%03o
",optch,mode);
21:      fflush(stdout);
22:  }

The function ctl_chmod() is called upon to change the permission bits (mode) of the semaphore set within the utility. Again, the union is declared in line 8, and the buffer pointer is established in line 10. The permission bits in the external variable sembuf are altered in line 11. Line 13 invokes the semctl(2) function to cause the permission bit changes to occur.

The following example accesses the previous semaphore set and changes the permissions using the -m option. Note the careful use of the leading zero in 0600 to specify the value in octal notation. The -s option follows to display the new values for this set:

$ ./semop -k0xFEEDF00D -a -m 0600 -s
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -m 0600
  -s {
    sem_nsems = 3
    sem_perm {
      cuid = 1001 (ehg)
      cgid = 1001 (ehg)
      uid  = 1001 (ehg)
      gid  = 1001 (ehg)
      mode = 0600
      key  = 0xFEEDF00D
    };
  };
$

Indeed the output shows that the new mode value is 0600. Additional source code for the utility that allows the owner and group to be altered is shown in Listing 24.6.

Code Listing 24.6. semchown.c—Source Code That Changes Owner and Group of a Semaphore Set
1:   /* semchown.c */
2:
3:   #include "semop.h"
4:
5:   static uid_t
6:   srch_uid(const char *user_id) {
7:       struct passwd *pw = getpwnam(user_id);
8:
9:       if ( !pw )
10:          return (uid_t)(-1);
11:      return pw->pw_uid;
12:  }
13:
14:  void
15:  ctl_chown(int optch,const char *user_id) {
16:      uid_t uid = srch_uid(user_id);
17:
18:      if ( uid == (uid_t)(-1) ) {
19:          fprintf(stderr,"Unknown userid: -%c %s
",optch,user_id);
20:          exit(1);
21:      }
22:
23:      sembuf.sem_perm.uid = uid;  /* Change userid */
24:
25:      /* Cheat: change uid by using ctl_chmod() */
26:      ctl_chmod(optch,sembuf.sem_perm.mode);
27:  }
28:
29:  static gid_t
30:  srch_gid(const char *group_id) {
31:      struct group *gr = getgrnam(group_id);
32:
33:      if ( !gr )
34:          return (gid_t)(-1);
35:      return gr->gr_gid;
36:  }
37:
38:  void
39:  ctl_chgrp(int optch,const char *group) {
40:      gid_t gid = srch_gid(group);
41:
42:      if ( gid == (gid_t)(-1) ) {
43:          fprintf(stderr,"Unknown group: -%c %s
",optch,group);
44:          exit(1);
45:      }
46:
47:      sembuf.sem_perm.gid = gid;  /* Change group */
48:
49:      /* Cheat: change gid by using ctl_chmod() */
50:      ctl_chmod(optch,sembuf.sem_perm.mode);
51:  }

The functions ctl_chown() and ctl_chgrp() are called for the utility options -x and -y, respectively. The actual changes are made in lines 23 and 47. The function calls in lines 26 and 50 cheat by pretending to change the mode value, but pass the existing mode value instead. This causes the changes in variable sembuf to be written to the semaphore set using IPC_SET.

Querying the Value of a Semaphore

The semctl(2) command GETVAL allows a program to query the current value of a specific semaphore within a set. Listing 24.7 shows a source module that performs this function for the semop utility program.

Code Listing 24.7. semgetval.c—Source Module That Uses GETVAL with semctl(2)
1:   /* semgetval.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_getval(int optch,int semx) {
7:       int z;
8:       union semun un;
9:
10:      z = semctl(semid,semx,GETVAL,un);
11:      if ( z == -1 ) {
12:          fprintf(stderr,"%s: -%c %d
",strerror(errno),optch,semx);
13:          exit(1);
14:      }
15:
16:      printf("  -%c %d => %d
",optch,semx,z);
17:      fflush(stdout);
18:  }

The ctl_getval() function is called by the -g n option of the utility. The value n represents the zero-based semaphore number within the set, which is passed to ctl_getval() in the argument semx in Listing 24.7. The semctl(2) call is made in line 10. Since the semaphore value cannot be negative, the value -1 represents an error return value, which is tested for in line 11. Otherwise, the semaphore number and its value are reported in line 16.

The following example reports the values of semaphore 0 and semaphore 2:

$ ./semop -k0xFEEDF00D -a -g0 -g2
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -g 0 => 0
  -g 2 => 0
$

Query the Entire Semaphore Set of Values

Querying any semaphore is a snapshot view of the values, since these values could be changing. If you need a snapshot of all of the semaphores, the GETALL control function provides a consistent result. Listing 24.8 shows how the GETALL command is used.

Code Listing 24.8. semgetall.c—Source Module That Uses the GETALL Command with semctl(2)
1:   /* semgetall.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_getall(int optch) {
7:       int z;
8:       int x;
9:       u_short array[MAX_NSET];
10:      union semun un;
11:
12:      un.array = &array[0];
13:      z = semctl(semid,0,GETALL,un);
14:      if ( z == -1 ) {
15:          fprintf(stderr,"%s: -%c
",strerror(errno),optch);
16:          exit(1);
17:      }
18:
19:      for ( x=0; x<n_sem; ++x )
20:          printf("  -%c : semaphore # %d = %u
",optch,x,array[x]);
21:
22:      fflush(stdout);
23:  }

The function ctl_getall() is called when the ./semop option -G is encountered. An array is declared in line 9 to receive all of the semaphore values. The semun union is pointed to this array in line 12. The GETALL command is invoked in line 13. If the call succeeds, the results are reported in lines 19 and 20.

The following shows GETALL in action:

$ ./semop -k0xFEEDF00D -a -G
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -G : semaphore # 0 = 0
  -G : semaphore # 1 = 0
  -G : semaphore # 2 = 0
$

Since you have not yet initialized this set of semaphores with values, you know that these are the default values for new semaphores under FreeBSD. Some UNIX platforms use different defaults, however.

Change the Value of a Semaphore

Sometimes it is necessary to adjust a specific semaphore to a specific value. This can be accomplished with the semctl(2) SETVAL command, as you can see in Listing 24.9.

Code Listing 24.9. semsetval.c—Source Module That Invokes the semctl(2) SETVAL Command
1:   /* semsetval.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_setval(int optch,int semx,int value) {
7:       int z;
8:       union semun un;
9:
10:      un.val = value;
11:      z = semctl(semid,semx,SETVAL,un);
12:      if ( z == -1 ) {
13:          fprintf(stderr,"%s: -%c %d=%d
",
14:              strerror(errno),optch,semx,value);
15:          exit(1);
16:      }
17:
18:      printf("  -%c %d=%d
",optch,semx,value);
19:      fflush(stdout);
20:  }

The ctl_setval() function is invoked when the -v n=x option is encountered. The semaphore number n is passed as the argument semx, while the value x is passed as the argument value. The union has the value assigned to its val member, which is then passed in the call in line 11. An example of the SETVAL being used is shown as follows:

$ ./semop -k0xFEEDF00D -a -v2=13 -G
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -v 2=13
  -G : semaphore # 0 = 0
  -G : semaphore # 1 = 0
  -G : semaphore # 2 = 13
$

Using the -v option, semaphore 2 in the set was assigned the new value of 13. This was reported by the -G option, which followed on the command line.

Change the Entire Semaphore Set of Values

The semctl(2) function allows the application writer to establish the values of all semaphores in one atomic operation, using the SETALL command. This command is used in Listing 24.10.

Code Listing 24.10. semsetall.c—Source Module That Uses the semctl(2) SETALL Command
1:   /* semsetall.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_setall(int optch,int array[]) {
7:       int z;
8:       int x;
9:       u_short ua[MAX_NSET];
10:      union semun un;
11:
12:      for ( x=0; x<n_sem; ++x )
13:          ua[x] = (u_short)array[x];
14:
15:      un.array = &ua[0];
16:      z = semctl(semid,0,SETALL,un);
17:      if ( z == -1 ) {
18:          fprintf(stderr,"%s: -%c %d,%d,%d
",
19:              strerror(errno),optch,array[0],array[1],array[2]);
20:          exit(1);
21:      }
22:
23:      printf("  -%c %d,%d,%d
",optch,array[0],array[1],array[2]);
24:      fflush(stdout);
25:  }

The ./semop utility calls ctl_setall() when the option -V is encountered. This option is followed by a comma-separated list of initial values for the entire semaphore set. The number of values must exactly match the set. The array of values is passed in the argument array in line 6. A conversion from int type to u_short type is made in lines 12 and 13 for the purpose of the semctl(2) call in line 16. The address of the array is established in the union member array (line 15).

The following example changes all three semaphores in the existing set that you have been using:

$ ./semop -k0xFEEDF00D -a -V9,8,7 -G
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -V 9,8,7
  -G : semaphore # 0 = 9
  -G : semaphore # 1 = 8
  -G : semaphore # 2 = 7
$

The option -V9,8,7 sets the values for semaphores 0, 1, and 2 in the set of three.

Querying the Process ID for a Semaphore

When you are debugging a complex set of applications that modify a set of semaphores, it is sometimes useful to be able to determine which process was the last one to modify a semaphore. The GETPID command of the semctl(2) system call performs this function. The source code in Listing 24.11 combines code that uses GETPID and some other information return commands.

Code Listing 24.11. ctlget.c—Source That Uses the GETPID Command of semctl(2)
1:   /* ctlget.c */
2:
3:   #include "semop.h"
4:
5:   void
6:   ctl_get(int optch,int cmd,int semx) {
7:       int z;
8:       union semun un;
9:
10:      z = semctl(semid,semx,cmd,un);
11:      if ( z == -1 ) {
12:          fprintf(stderr,"%s: -%c %d
",
13:              strerror(errno),
14:              optch,
15:              semx);
16:          exit(1);
17:      }
18:
19:      printf("  -%c %d => %d
",optch,semx,z);
20:      fflush(stdout);
21:  }

The command GETPID is passed in the argument cmd of function ctl_get() in line 6 when the -p or -P options are used on the command line. The specific semaphore number is specified in the argument semx, which is later used in the call to semctl(2) in line 10. Since the value being returned for all acceptable commands cannot be negative, the value -1 still identifies an error return in line 11. The returned value is reported in line 19, when the semctl(2) call is successful.

The following example shows how to report the process ID for semaphore number 2 and 0 using the -p option:

$ ./semop -k0xFEEDF00D -a -p2 -p0
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -p 2 => 2347
  -p 0 => 2346
$

If you are following along and doing these commands on your system, you may see zeros reported instead. Zero values indicate that no process has done a wait, zero, or notify operation on your semaphore (you will do this later in the chapter). This example was preceded by a few semaphore operations to provide non-zero process ID results.

The following example uses the -P convenience option to invoke GETPID on each semaphore in the set:

$ ./semop -k0xFEEDF00D -a -P
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -p 0 => 2346
  -p 1 => 2348
  -p 2 => 2347
$

Query the Number of Processes Waiting for Notifies

The GETNCNT command allows your process to query how many processes are waiting on a particular semaphore. Some applications may be able to make use of this information in order to gauge the workload being processed.

The GETNCNT requests are invoked with the -n option. The module shown in Listing 24.11 handles this information request. The following command lists how many processes are waiting on the third semaphore (semaphore number 2):

$ ./semop -k0xFEEDF00D -a -n2
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -n    => 0
$

The returned value of zero indicates that no processes are currently waiting on this semaphore.

Query the Number of Processes Waiting for Zero

In addition to waiting and notifying semaphores, a process can also perform a wait for zero operation. This might be used to report that a particular resource is exhausted, for example. The GETZCNT command allows the caller to determine how many processes are waiting for zero on a particular semaphore. The GETZCNT operation is also handled by the code shown in Listing 24.11. The following example shows how the -z option is used to report the number of processes waiting for zero on semaphore 0:

$ ./semop -k0xFEEDF00D -a -z0
  -a 0xFEEDF00D => IPC ID 196608
  There are 3 semaphores in this set.
  -z    => 0
$

In this example, there are no processes waiting for a zero on semaphore 0 of this set.

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

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