This routine handles all of the miscellaneous things that the previous commands can't handle.
For example deleting a semaphore looks like:
semctl(sem_id, 0, IPC_RMID,
sem_union) ;
Setting the initial value looks something like:
sem_union.val = 1;
semctl(sem_id, 0, SETVAL,
sem_union) ;
There is also a GETVAL call that looks like
semctl(sem_id, 0, GETVAL,
sem_union);
If you have an array of semaphores, then you might use:
unsigned short forks[NUM_PHILOS];
// some code to fill in forks values
semarg.array = forks;
semctl(semid, 0, SETALL,
semarg);
Retrieving current values of an array of semaphores, might look like:
semarg.array = forks;
semctl(semid, 0, GETALL,
semarg);
/* After the #includes, the function prototypes and the global variable, we come to the main function. There the semaphore is created with a call to semget, which returns the semaphore ID. If the program is the first to be called (i.e. it's called with a parameter and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is set to X. */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; static int set_semvalue(void); static void del_semvalue(void); static int semaphore_p(void); static int semaphore_v(void); static int sem_id; int main(int argc, char *argv[]) { int i; int pause_time; char op_char = 'O'; int my_key = getuid(); printf("My userid is %d\n", my_key); srand((unsigned int)getpid()); sem_id = semget((key_t)my_key, 1, 0666 | IPC_CREAT); if (argc > 1) { if (!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } op_char = 'X'; sleep(2); } /* Then we have a loop which enters and leaves the critical section ten times. There, we first make a call to semaphore_p which sets the semaphore to wait, as this program is about to enter the critical section. */ for(i = 0; i < 10; i++) { if (!semaphore_p()) exit(EXIT_FAILURE); printf("%c", op_char);fflush(stdout); pause_time = rand() % 3; sleep(pause_time); printf("%c", op_char);fflush(stdout); /* After the critical section, we call semaphore_v, setting the semaphore available, before going through the for loop again after a random wait. After the loop, the call to del_semvalue is made to clean up the code. */ if (!semaphore_v()) exit(EXIT_FAILURE); pause_time = rand() % 2; sleep(pause_time); } printf("\n%d - finished\n", getpid()); if (argc > 1) { sleep(10); del_semvalue(); } exit(EXIT_SUCCESS); } /* The function set_semvalue initializes the semaphore using the SETVAL command in a semctl call. We need to do this before we can use the semaphore. */ static int set_semvalue(void) { union semun sem_union; sem_union.val = 1; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0); return(1); } /* The del_semvalue function has almost the same form, except the call to semctl uses the command IPC_RMID to remove the semaphore's ID. */ static void del_semvalue(void) { union semun sem_union; if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) fprintf(stderr, "Failed to delete semaphore\n"); } /* semaphore_p changes the semaphore by -1 (waiting). */ static int semaphore_p(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; /* P() */ sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); return(0); } return(1); } /* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1, so that the semaphore becomes available. */ static int semaphore_v(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; /* V() */ sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_v failed\n"); return(0); } return(1); }
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <signal.h> union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; int my_semaphore = -1; void start_process(); // ******************************************************** void accessSema(increment){ struct sembuf sops; sops.sem_num = 0; sops.sem_op = increment; sops.sem_flg = SEM_UNDO; if (semop(my_semaphore, &sops, 1) < 0) perror("my_semaphore"); return; } // ********************************************** void free_resources() { union semun semarg; if (my_semaphore >= 0) if (semctl(my_semaphore, 0, IPC_RMID, semarg) < 0) { perror("free_resources: my_semaphore"); } } //********************************************************** int main(int argc, char * argv[]) { union semun semarg; int i, pid, status; // ------------------------------ // Get semaphore for display critical region my_semaphore = semget(IPC_PRIVATE, 1, 0666); if (my_semaphore < 0) { free_resources(); perror("semget(my_semaphore)"); exit(-1); } printf("created my_semaphore %d\n", my_semaphore); // Set a single value to zero --- waiting on this will suspend // a task until this gets incremented semarg.val = 0; if (semctl(my_semaphore, 0, SETVAL, semarg) < 0) { perror("semctl_1(my_semaphore)"); free_resources(); exit(-1); } // ------------------------------- pid = fork(); if (pid < 0) { perror("fork err"); free_resources(); exit(-1); } if (pid == 0) { start_process(); return 0; } printf("Creating child: %d\n", pid); // Let the process go for 5 seconds sleep(5); printf("parent done waiting, now set child in action\n"); accessSema(6); // Note I don't have to just add 1 // Go collect the child body pid = wait(&status); printf("All done waiting for child\n"); free_resources(); }//End of main void start_process() { printf("Child calling accessSema(-6)\n"); accessSema(-6); // I can wait for more than 1 printf("Child returns from accessSema\n"); sleep(4); printf("Child terminating\n"); }
Try running this an then run the ipcs program to see what resources you own. Note the error that occurs because of the IPC_EXCL flag.
Try to understand what is going on with each of the semget calls. We will go over it in class.
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int sem1, sem2, sem3, sem4;
key_t ipc_key;
int my_uid = getuid();
ipc_key = ftok(".", 'A');
if ((sem1 = semget(ipc_key, 3, IPC_CREAT | 0666)) == -1)
{
perror("semget: IPC_CREAT | 0666");
}
printf("sem1 identifier %d\n", sem1);
if ((sem2 = semget(ipc_key, 3, IPC_CREAT |
IPC_EXCL|0666)) == -1)
{
perror("semget: IPC_CREAT | IPC_EXCL|0666");
}
printf("sem2 identifier %d\n", sem2);
if ((sem3 = semget(my_uid, 3, IPC_CREAT |
IPC_EXCL|0600)) == -1)
{
perror("semget: IPC_CREAT | IPC_EXCL|0666");
}
printf("sem3 identifier %d\n", sem3);
if ((sem4 = semget(IPC_PRIVATE, 3, 0600)) == -1)
{
perror("semget: IPC_PRIVATE, 0666");
}
printf("sem4 identifier %d\n", sem4);
}
You can leave resources tied up with IPC. They won't go away until the system
is rebooted. If you want to see what you have tied up issue the command:
ipcs
If you want to delete your semaphores, use the command:
ipcrm
The script below will remove all of your semaphores if you don't want to issue separate commands for each semaphore.
#!/bin/csh -f set l = `ipcs -s | grep "$USER"| cut -c12-19` foreach s ( $l ) echo $s ipcrm sem $s end if ($#l != 0 ) echo $#l semaphore\(s\) for $user removed
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <signal.h> union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #define NUM_PHILOS 5 int semid = -1; /* Semaphore IPC ID */ int sem_display = -1; // semaphore for display output // ******************************************************** // used to guarantee only one dining philosopher doing printf at a time void accessDisplay(increment){ struct sembuf sops; sops.sem_num = 0; sops.sem_op = increment; sops.sem_flg = SEM_UNDO; if (semop(sem_display, &sops, 1) < 0) perror("semop for display"); return; } // ************************************************ // print out which forks are on the table void update_display(int philo, int eating) { int i; union semun semarg; unsigned short forks[NUM_PHILOS]; printf("update_display: philo(%d) eating(%d)\n", philo, eating); accessDisplay(-1); semarg.array = forks; if (semctl(semid, 0, GETALL, semarg) < 0) { perror("semctl(update_display)"); } printf("------------------------------------------------------\n"); for (i=0; i < NUM_PHILOS; i++) { if (i < 10) printf("%d ", i); else printf("%d ",i); } printf("\n"); for (i=0; i < NUM_PHILOS; i++) { if (forks[i]) printf("--E "); else printf(" "); } printf("\n"); accessDisplay(1); } // ******************************************************** // Called to get 2 adjacent forks or to put them down on the table void accessForks(int rightFork, int increment){ struct sembuf sops[2]; int leftFork = rightFork+1; if (leftFork >= NUM_PHILOS) leftFork = 0; sops[0].sem_num = leftFork; sops[0].sem_op = increment; sops[0].sem_flg = SEM_UNDO; sops[1].sem_num = rightFork; sops[1].sem_op = increment; sops[1].sem_flg = SEM_UNDO; if (semop(semid, sops, 2) < 0) perror("semop for 2 forks"); return; } void getForks(int rightFork){ accessForks(rightFork, -1); } void putForks(int rightFork){ accessForks(rightFork, 1); } // **************************************************** // Here is the dining philosopher code void start_philo( int number) { int nSleep; srand(number); // seed random number generator for (; ; ) { // Start out Thinking nSleep = abs(rand()) %4; sleep(nSleep); // Enter Eating mode getForks(number); update_display(number,1); // we are now eating nSleep = abs(rand()) %8; sleep(nSleep); // we are about to start thinking putForks(number); update_display(number,0); }// End of for loop for Philosopher } // ********************************************** void free_resources() { union semun semarg; if (semctl(semid, 0, IPC_RMID, semarg) < 0) { perror("free_resources: semid "); } if (sem_display >= 0) if (semctl(sem_display, 0, IPC_RMID, semarg) < 0) { perror("free_resources: sem_display"); } } //********************************************************** int main(int argc, char * argv[]) { union semun semarg; unsigned short forks[NUM_PHILOS]; int children[NUM_PHILOS]; int i, pid, status; for (i=0; i < NUM_PHILOS; i++) { forks[i] = 1; children[i] = 0; } // ------------------------------- // Get semaphore for 5 forks semid = semget(IPC_PRIVATE, 5, 0666); if (semid < 0) { perror("semget(semid)"); exit(-1); } printf("created semid %d\n", semid); semarg.array = forks; if (semctl(semid, 0, SETALL, semarg) < 0) { perror("semctl(semid)"); free_resources(); exit(-1); } printf("Initialized fork semaphore\n"); // ------------------------------ // Get semaphore for display critical region sem_display = semget(IPC_PRIVATE, 1, 0666); if (sem_display < 0) { free_resources(); perror("semget(sem_display)"); exit(-1); } printf("created sem_display %d\n", sem_display); semarg.val = 1; if (semctl(sem_display, 0, SETALL, &semarg) < 0) { perror("semctl(sem_display)"); free_resources(); exit(-1); } // ------------------------------- // Finally start up our philosophers for (i = 0; i < NUM_PHILOS; i++) { pid = fork(); if (pid < 0) { perror("fork err"); free_resources(); exit(-1); } if (pid == 0) { start_philo(i); return 0; } children[i] = pid; printf("Creating child: %d\n", pid); } // Let the philosophers go for 20 seconds sleep(20); printf("After 20 Seconds, we Terminate\n"); for (i=0; i < NUM_PHILOS; i++) kill(children[i], SIGTERM); // Go collect the Philosopher Bodies for ( ;;) { pid = 0; // For each child still around, execute a call to wait for (i = 0; i < NUM_PHILOS; i++) { if (children[i]) { pid = wait(&status); break; } } // if we didn't find any child, then exit this loop if (pid == 0) break; printf("freeing up child %d\n", pid); for (i = 0; i < NUM_PHILOS; i++) { if (children[i] == pid) children[i] = 0; } } printf("All done waiting for children\n"); // Find out if we have any dishonest philosophers // Make sure we get all of our forks back // SEM_UNDO should make this happen semarg.array = forks; for (i=0; i < NUM_PHILOS; i++) forks[i] = 0; // make sure GETALL is really filling fork if (semctl(semid, 0, GETALL, semarg) < 0) { perror("semctl(main)"); } for (i=0; i < NUM_PHILOS; i++) { if (forks[i] != 1) printf("Fork %d was not returned %d\n", i, forks[i]); } free_resources(); }//End of main
/* workers.c -- Workers/Tools Demo */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <time.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #define N_SEC 3 /* Run simulation for n seconds */ static int semid = -1; /* Semaphore IPC ID */ /* * Worker List : */ static struct { char *name; /* Worker name */ pid_t PID; /* Process ID */ } workers[] = { { "Paul", 0 }, { "Alfred", 0 }, { "Robert", 0 }, { "Adam", 0 } }; /* * Tool names : */ static char *tools[] = { "Hammer", "Screwdriver", "Chisel" }; /* * In this case we just want to notify the control * semaphore (#3). The control semaphore prevents * other processes from reporting something to * stdout until the present process is finished its * reporting to stdout. This keeps the report serialized. */ static void notifyControl(void) { int z; static struct sembuf sops = { 3, 1, 0 }; z = semop(semid,&sops,1); if ( z == -1 ) { perror("semop(notify ctl)"); exit(13); } } /* * This function randomly selects 1 to 3 tools that * will be required. When hammers or chisels are * selected, only require one tool. For screwdrivers * allow one or two screwdrivers to be required. */ static struct sembuf * identifyToolsNeeded(int *n) { int x, y, sem; static struct sembuf sops[3]; /* * Determine the number of tools required: */ *n = rand() % 3 + 1; /* 1 to 3 */ /* * Now uniquely define the tools needed: */ for ( x=0; x<*n; ++x ) { do { sem = rand() % 3; for ( y=0; y<x; ++y ) if ( sops[y].sem_num == sem ) { /* Already used */ sem = -1; break; } } while ( sem == -1 ); sops[x].sem_num = sem; /* Tool required */ if ( sem == 1 ) /* Allow up to 2 screwdrivers */ sops[x].sem_op = rand() % 2 + 1; else /* All other tools, only one each */ sops[x].sem_op = -1; sops[x].sem_flg = 0; /* No flags */ } /* * The control semaphore : */ sops[*n].sem_num = 3; /* Sem #3 controls stdout */ sops[*n].sem_op = -1; /* Wait operation */ sops[*n].sem_flg = 0; /* No flags */ return sops; } /* * This function is used to report our tools & state: */ void reportTools(int n,struct sembuf *sops,char *name,char flg) { int x; int nt; /* Number of tools */ char *oper; /* Operation */ char buf[1024]; /* Formatting buffer */ if ( flg == 'W' ) oper = "waiting for tools"; else if ( flg == 'X' ) oper = "using tools"; else oper = "returning tools"; sprintf(buf,"Worker %s is %s\n", name,oper); /* * Only report the tools for the "using" * status report: */ if ( flg == 'X' ) { for ( x=0; x<n; ++x ) { nt = abs(sops[x].sem_op); sprintf(buf+strlen(buf), " %d %s%s\n", nt, tools[sops[x].sem_num], nt == 1 ? "" : "s"); } } /* * Write all of our text lines atomically to * standard output : */ write(1,buf,strlen(buf)); } /* * This function is run by the child process to * simulate one worker needing and returning tools: */ static void toolTime(int workerx,time_t t0) { char *name = workers[workerx].name; pid_t pid = getpid(); /* Get our process ID */ int x; /* Work int */ time_t tn; /* Current time */ int n_tools; /* Number of tools required */ struct sembuf *sops = 0;/* Tool list */ /* * Start random number generator : */ srand((unsigned)pid); /* * Loop for N_SEC seconds : */ for (;;) { /* * Check for quitting time : */ time(&tn); if ( tn - t0 >= N_SEC ) return; /* Quitting time! */ /* * Identify the tools that we need: */ sops = identifyToolsNeeded(&n_tools); reportTools(n_tools,sops,name,'W'); /* * Wait for tools to become available: * Note: n_tools+1 includes ctl semaphore. */ semop(semid,sops,n_tools+1); reportTools(n_tools,sops,name,'X'); notifyControl(); /* Done with stdout */ /* * Pretend to work : */ sleep(1); /* * Turn wait sem_ops into notifies for * the tools only (we don't use the control * semaphore here): */ for ( x=0; x<n_tools; ++x) sops[x].sem_op = -sops[x].sem_op; /* * Return the tools to the toolbox : * [Notify operation] */ reportTools(n_tools,sops,name,'R'); semop(semid,sops,n_tools); } } /* * Main program : */ int main(int argc,char **argv) { int z; /* Return code */ int x; /* Work index */ time_t t0; /* Start time */ union semun semarg; /* semctl() arg */ /* Initial counts of tools in toolbox: */ static ushort icounts[] = { 3, /* Hammers */ 2, /* Screwdrivers */ 1, /* Chisel */ 1 /* Control semaphore */ }; ushort tcounts[4]; /* Final tool counts */ int status; /* Termination status */ pid_t chPID; /* Child process ID */ /* * Get a private set of semaphores : */ semid = semget(IPC_PRIVATE,4,0600); if ( semid == -1 ) { perror("semget()"); exit(1); } /* * Initialize the semaphore counts : */ semarg.array = icounts; z = semctl(semid,0,SETALL,semarg); if ( z == -1 ) { perror("semctl(SETALL)"); exit(1); } /* * Record our start time : */ time(&t0); /* * Now create four worker processes : */ for ( x=0; x<4; ++x ) { fflush(stdout); fflush(stderr); if ( !(chPID = fork()) ) { /* Child process: */ toolTime(x,t0); return 0; } else if ( chPID == (pid_t)(-1) ) { fprintf(stderr,"%s: fork(x=%d)\n", strerror(errno),x); return 13; } workers[x].PID = chPID; } /* * Now wait for all processes to end : */ do { chPID = wait(&status); for ( x=0; x<4; ++x ) if ( workers[x].PID == chPID ) { workers[x].PID = 0; break; } /* * See if they have all terminated : */ for ( x=0; x<4; ++x ) if ( workers[x].PID != 0 ) break; } while ( x < 4 ); printf("All workers have quit.\n"); /* * Obtain all semaphore counts : */ semarg.array = tcounts; z = semctl(semid,0,GETALL,semarg); if ( z == -1 ) { perror("semctl(GETALL)"); exit(1); } /* * Check our tool counts : */ for ( x=0; x<3; ++x ) if ( tcounts[x] != icounts[x] ) printf("Now have %d %ss. Had initially %d.\n", tcounts[x], tools[x], icounts[x]); else printf("All %d %ss are accounted for.\n", tcounts[x], tools[x]); puts("Simulation Complete."); /* * Remove the semaphore set: */ z = semctl(semid,0,IPC_RMID,semarg); if ( z == -1 ) { perror("semctl(IPC_RMID)"); exit(1); } return 0; }
Create a set of programs: such that you have a datafile and upto N users can simultaneously read your data file. However, you have another program that updates the file, and only one user can update the file at a time. In fact when someone is updating the file, no one else can be reading the file. Suggested solution:
Define a semaphore associated with a path (see the ftok statement above)
reader.c - attempts to access the semaphore. If it doesn't already exist, then it is created. If it is created, then it is initialized to N. The reader decrements the semaphore by 1 before reading the data.
writer.c - does the same thing as the reading in terms of creating the semaphore and initializing it. However, the writer decrements the semaphore by N before updating the file. Consider the difference of decrementing the semaphore by 1 N times versus decrementing the semaphore by N one time.
For both the reader.c and the writer.c programs, you leave the semaphore around (don't delete at the end of your program).
When you are finally done running your system, you can write a program that deletes the semaphore.