1 / 57

Posix:XSI Inter Process Communication

Posix:XSI Inter Process Communication. Semaphore Sets, Message Queues, and Shared Memory. A common set of tools for Inter-Process Communication introduced in AT&T System V.2. Posix:XSI IPC. Semaphore Sets. Message Queues. Shared Memory.

gafna
Download Presentation

Posix:XSI Inter Process Communication

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Posix:XSIInter Process Communication Semaphore Sets, Message Queues, and Shared Memory

  2. A common set of tools for Inter-Process Communication introduced in AT&T System V.2

  3. Posix:XSI IPC Semaphore Sets Message Queues Shared Memory These three constructs have a number of commonalities.

  4. mechanism Posix function meaning message queues msgctl control msgget create or access msgrcv receive message msgsnd send a message semaphore set semctl control semget create or access semop execute operation shared memory shmat attach memory shmctl control shmdt detach memory shmget create or access

  5. IPC Identifiers • Posix:XSI identifies each IPC object with a unique non-zero integer • value. This value is returned by the get function for the object. • When creating or accessing an IPC object, you must specify a key • to designate the particular object to be created or accessed. Keys • are picked in one of three ways: • Let the system pick the key • Pick a key directly • Ask the system to generate a key from a given path by calling ftok • This allows independent processes to derive the same key based • on a commonly known path. • #include <sys/ipc.h> • key_t ftok(const char *path, int fd);

  6. Accessing IPC Resources From the Shell POSIX:XSI defines shell extensions for examining and deleting IPC resources. ipcs [-qms] [ a | -bcot ] With no parameters, the ipcs command displays an abbreviated set of information about all message queues, shared memory, and semaphore sets . Not on the Macs

  7. ipcrm [ -q msgid | -Q msgkey | -s semid | -S semkey | -m shmid | -M shmkey ] removes an individual IPC object. Not on the Macs

  8. Semaphore Sets • Notion of semaphores as invented by Dijkstra • Used to manage access to critical sections of code where we need exclusive access to a resource • Not the same as semaphores we studied in Threads, although they appear to be similar.

  9. A semaphore set is an array of semaphore elements. • Although not directly accessible by applications using semaphores, • each semaphore element consists of • a non-negative integer representing the value • of the semaphore (semval) • the process id of the last process to manipulate • the semaphore (sempid) • the number of processes waiting for the semaphore • value to increase (semncnt) • The number of processes waiting for the semaphore • to equal zero (semzcnt)

  10. A semaphore is an integer variable with two atomic operations, wait and signal. • A binary semaphore can only have the values 0 or 1 • wait is sometimes called down, P, or Lock • signal is sometimes called up, V, unlock, or post

  11. Given a semaphore, sv • Wait • if sv > 0 , decrement sv. If sv == 0, wait • Signal • increment sv

  12. this works as long as sv is initially = 0. process one process two wait (&sv); b1; … a1; signal (&sv); … if process two gets here first, it waits Suppose process one must execute statement a1 before process b executes statement b1. Use a semaphore to order these two statements:

  13. What happens in the following case, if the semaphores S and Q are both initially set to 1. Process one Process two for ( ; ; ) { wait (&Q); b1; signal (&S); } for ( ; ; ) { wait (&S); a1; signal (&Q); } guarantees that one process is never more than one iteration ahead of the other.

  14. Process two Process one for ( ; ; ) { wait (&Q); b1; signal (&S); } for ( ; ; ) { wait (&S); a1; signal (&Q); } blocked 1 1 S 0 1 0 1 Q 1 2 0 1 guarantees that one process is never more than one iteration ahead of the other. What happens if both semaphores are initially 0? What happens if S is initially 8 and Q is initially 2?

  15. Process two Process one for ( ; ; ) { wait (&Q); b1; signal (&S); } for ( ; ; ) { wait (&S); a1; signal (&Q); } 8 S Q 2 Process one can execute a1 up to 8 times ahead of process 2. Consider this as a producer-consumer problem with 8 slots

  16. semget( ) the key is like a file name. Processes can cooperate using a shared semaphore if they agree on a common key. int semget ( key_t key, int num_sems, int sem_flags); semget creates a new semaphore or obtains the key of an existing semaphore. The semaphore identifier is returned. The identifier is like a file descriptor, and is used within the process to access the semaphore. Returns -1 if the call fails. the number of semaphores required. It is almost always equal to 1. like the open flags on a file: IPC_PRIVATE: only this process can use the semaphore IPC_CREAT: create a new semaphore if one doesn’t exist IPC_EXCL: make sure semaphore is unique If a process attempts to create a semaphore that already exists, it receives a handle to the existing semaphore, unless sem_flags specifies both IPC_CREAT and IPC_EXCL.

  17. Example #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #define PERMS S_IRUSR|S_IWUSR #define SET_SIZE 3 int semid; if ((semid = semget(IPC_PRIVATE, SET_SIZE, PERMS)) < 0) perror (“Could not create semaphore…”); . . . in this example, the system produces the key. It can only be used by this (and child) processes. the set contains 3 semaphores, 0, 1, and 2

  18. Example in this example, two programs agree on the key, which is provided in the call. If the semaphore already exists, the call returns a handle to the existing semaphore. #include <stdio.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/sem.h> #include <string.h> #include <errno.h> #define PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH #define SET_SIZE 1 #define KEY ((key_t)99887) int semid; if ((semid = semget(KEY, SET_SIZE, PERMS | IPC_CREAT)) < 0) fprintf(stderr, “Error creating sempahore”); use PERMS | IPC_CREAT | IPC_EXCL and the call will return with an error if the semaphore already exists.

  19. Example key_t mykey; int semid; if (argc != 3) { fprintf(stderr, "Usage %s pathname id\n", argv[0]); return 1; } if ((mykey = ftok(argv[1], atoi(argv[2]))) == (key_t)-1) { fprintf(stderr, "Failed to derive key from filename %s:%s\n", argv[1], strerror(errno)); return 1; } if ((semid = semget(mykey, SET_SIZE, PERMS | IPC_CREAT)) == -1) { fprintf(stderr, "Failed to create semaphore with key %d:%s\n", (int)mykey, strerror(errno)); return 1; } In this example the program gets a path (the path must exist) and creates a key. Now use the key to create the semaphore set

  20. semop( ) the number of elements in the array the semaphore id returned from semget( ) sem_ops is a pointer to an array of semaphore operations int semop ( int sem_id, struct sembuf *sem_ops, size_t num_sem_ops); struct sembuf { short sem_num; short sem_op; short sem_flg; } // the semaphore number, usually zero // the operation to perform (see next slide) The semop function performs all of the operations specified in the sem_ops array atomically. If any of the individual element operations would cause the process to block, the process blocks and none of the operations are performed. // usually set to SEM_UNDO. Allows the os to // release a semaphore if the process holding it // terminates

  21. sem_op values if sem_op is a positive number, it is added to the semaphore and all processes waiting for the semaphore to increase are awakened. This corresponds to releasing resources controlled by the semaphore. if sem_op is 0, and the semaphore element is not 0, the calling process is blocked and the count of the number of processes waiting for the semaphore is incremented. If the semaphore’s value is zero the call returns immediately. if sem_op is a negative number, it means that we want to use a resource controlled by the semaphore. The value in sem_op is added to the semaphore, provided that the result would not be negative. If the result would be negative, the calling process is blocked until the semaphore value is increased.

  22. Example Consider the following function written to set up the semop structure sembuf: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> void set_sembuf (struct sembuf *s, int num, int op, int flg) { s->sem_num = (short)num; s->sem_op = op; s->sem_flg = flg; return; }

  23. Process 2 Process 3 Process 1 A B Tape Drive

  24. The following code allow the processes to access the tape drive in a Mutually exclusive manner. struct sembuf { short sem_num; short sem_op; short sem_flg; } S is a two element semaphore set that represents the tape drive. Both are initially set to zero. S[ 0] represents tape A S[ 1 ] represents tape B struct sembuf get_tapes[2]; struct sembuf release_tapes[2]; setsembuf(&(get_tapes[0]), 0, -1, 0); setsembuf(&(get_tapes[1]), 1, -1, 0); setsembuf(&(release_tapes[0]), 0, 1, 0); setsembuf(&(release_tapes[1]), 1, 1, 0); wait on tape A wait on tape B release tape A release on tape A semop(S, get_tapes, 1); <use tape A> semop(S, release_tapes, 1); Process 1:

  25. The following code allow the processes to access the tape drive in a Mutually exclusive manner. struct sembuf { short sem_num; short sem_op; short sem_flg; } S is a two element semaphore set that represents the tape drive. Both are initially set to zero. S[ 0] represents tape A S[ 1 ] represents tape B struct sembuf get_tapes[2]; struct sembuf release_tapes[2]; setsembuf(&(get_tapes[0]), 0, -1, 0); setsembuf(&(get_tapes[1]), 1, -1, 0); setsembuf(&(release_tapes[0]), 0, 1, 0); setsembuf(&(release_tapes[1]), 1, 1, 0); wait on tape A wait on tape B release tape A release tape B semop(S, get_tapes, 2); <use tapes A and B> semop(S, release_tapes, 2); Process 2:

  26. The following code allow the processes to access the tape drive in a Mutually exclusive manner. struct sembuf { short sem_num; short sem_op; short sem_flg; } S is a two element semaphore set that represents the tape drive. Both are initially set to zero. S[ 0] represents tape A S[ 1 ] represents tape B struct sembuf get_tapes[2]; struct sembuf release_tapes[2]; setsembuf(&(get_tapes[0]), 0, -1, 0); setsembuf(&(get_tapes[1]), 1, -1, 0); setsembuf(&(release_tapes[0]), 0, 1, 0); setsembuf(&(release_tapes[1]), 1, 1, 0); wait on tape A wait on tape B release tape A release tape B semop(S, get_tapes + 1, 1); <use tape B> semop(S, release_tapes + 1, 1); Process 3:

  27. semctl Each element in a semaphore set must be initialized with semctl before it is used. int semctl ( int sem_id, int sem_num, int command, union semun arg); The Semaphore to set, usually 0 the semaphore set identifier from semget 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 }; Commonly Used comands: GETVAL – return the value of the semaphore GETPID – return process ID of last element manipulating semaphore GETNCNT – return number of processes waiting for an element to increase GETZCNT – return number of processes waiting for element to become 0 SETVAL – initialize a semaphore to a known value IPC_RMID – delete a semaphore ID IPC_STAT – copy info from the semaphore set data structure into _buf IPC_SET – write values from _buf into the semaphore set data structure

  28. Big Example

  29. The senum union #ifndef _SEMUN_H #define _SEMUN_H union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *_buf; }; #endif the semun union is not always defined, so we will define it in a .h file. The only field we care about is val. The implementation of semctl differs from OS to OS. Check the semctl man pages to see what the semun union should look like.

  30. Functions

  31. this function initializes the semaphore. this is the value we want to set in the semaphore. Although we are using a binary semaphore ( 0 or 1) the semaphore system calls are more general. We don’t care abut the other fields in the union. 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 SETVAL command sets the initial value in the semaphore. the semaphore id, returned by semget.

  32. this function deletes a semaphore delete static void del_semvalue(void) { union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) printf("Failed to delete a semaphore!\n"); }

  33. this function claims the semaphore for this process by setting the semaphore to 0. Any other process wanting the semaphore is blocked. static int semaphore_p(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) == -1) { printf("semaphore_p failed.\n"); return(0); } return (1); } the operation is subtract 1 from the semaphore let the os release the semaphore

  34. this function releases the semaphore by adding one to it ( goes from 0 to 1 ). static int semaphore_v(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) == -1) { printf("semaphore_v failed.\n"); return (0); } return(1); } the operation is add one to the semaphore.

  35. Create Program 1 like this … int main (int argc, char *argv[]) { int i; // loop counter int pause_time; // random sleep time char op_char = '1'; // character to print // seed the random number generator with the pid srand((unsigned int)getpid()); // get a semaphore ID using the key 1234 sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); // Initialize the semaphore if (!set_semvalue()) { printf("Failed to initialize semaphore.\n"); exit(EXIT_FAILURE); } remember, this is like opening a file. We give it a name all parties agree to and the call returns a “handle”. Use IPC_CREAT so that a semaphore is created only if one does not exist with that name..

  36. // This loop will enter and leave the critical section 10 times // with random sleep times 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); if (!semaphore_v()) exit (EXIT_FAILURE); pause_time = rand() & 3; sleep(pause_time); } get the semaphore in the critical section, print out ‘1’, sleep for some random time, and print out ‘1’ again. release the semaphore

  37. printf("\n%d - finished... \n", getpid()); sleep(10); del_semvalue(); exit (EXIT_SUCCESS); }

  38. The second program can be written with a little cut and paste. int main (int argc, char *argv[]) { int i; // loop counter int pause_time; // random sleep time char op_char = '2'; // character to print // seed the random number generator with the pid srand((unsigned int)getpid()); // get a semaphore ID using the key 1234 sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); change the print character the major difference between this program and the first one is that we don’t initialize the semaphore. We assume it has been done.

  39. 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); if (!semaphore_v()) exit (EXIT_FAILURE); pause_time = rand() & 3; sleep(pause_time); } printf("\n%d - finished... \n", getpid()); exit (EXIT_SUCCESS); } the for loop is identical… … and when we are done we don’t worry about deleting the semaphore. sem1.c

  40. Shared Memory • A range of addresses created by the IPC facility for one process • Other processes can “attach” the same shared memory segment into their own address space. • Applications use this memory just as if it had been allocated by malloc • When one application writes to shared memory in this range, the change is immediately available to any other process that has access to the same shared memory Fastest!

  41. Synchronization Shared memory doesn’t provide any synchronization facilities. It is up to the programmer to synchronize access. This might mean using * signals * semaphores * a message queue * a named pipe

  42. shmget() this function is used to create a shared memory segment the amount of memory required, in bytes #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); returns a shared memory identifier or -1 a programmer defined key permission bits and IPC_CREAT

  43. shmat( ) this function attaches the shared memory segment to the process’s address space. #include <sys/shm.h> void *shmat(int shm_id, const void *shm_addr, int shmflg); returns a pointer to the shared memory. if non-zero, the segment is attached for read-only. If 0, it is attached read/write. the shared memory identifier gotten from shmget() address in the process’s address space where the shared memory is to be attached. Normally this is a NULL pointer, allowing the system to determine where. This is the recommended form.

  44. shmdt() this function detaches the shared memory segment #include <sys/shm.h> int shmdt(const void *shm_addr); returns 0 if successful or -1 if not pointer to the shared memory segment to be detached.

  45. shmctl() #include <sys/shm.h> int shmctl(int shm_id, int command, struct shmid_ds *buf); returns 0 if successful or -1 if fails struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; } the shared memory segment’s identifier IPC_STAT Sets the data in the shmid structure to reflect the values associated with the shared memory. IPC_SET Sets the values associated with the shared memory to those provided in the data structure. IPC_RMID Delete the shared memory

  46. Example a b c d e . . . \n \0 read write Consumer Producer shared memory

  47. The Producer #define SHMSZ 27 // the size of the shared memory segment #define SHMKY 5678 // the key of the shared memory segment main () { char c; int shm_id; char *shm, *s; // create a shared memory segment. if ((shm_id = shmget((key_t)SHMKY, SHMSZ, IPC_CREAT | 0666)) < 0 ) { perror("shmget"); exit(1); } // attach the shared memory segment, shm points to it if ((shm = shmat(shm_id, NULL, 0)) == (char *)-1) { perror("shmat"); exit(1); }

  48. // Now write out a string of characters // Add a line feed and Null terminate the string. s = shm; for (c = 'a'; c <= 'z'; c++) { *s++ = c; } *s++ = '\n'; *s = '\0'; // Finally, wait until the consumer writes a '*' // in the first character position to signal that // it is done. while(*shm != '*') { sleep(1); } printf("All done ... data has been read!\n"); exit(0); }

  49. The Consumer #define SHMSZ 27 // the size of the shared memory block #define SHMKY 5678 // the key of the shared memory block main () { int shm_id; char *shm, *s; if ((shm_id = shmget((key_t)SHMKY, SHMSZ, 0666)) < 0 ) { perror("shmget"); exit(1); } if ((shm = shmat(shm_id, NULL, 0)) == (char *)-1) { perror("shmat"); exit(1); } for (s = shm; *s != '\0'; s++) { putchar(*s); } *shm = '*'; exit(0); }

  50. Message Queues • Message queues are like named pipes, but without the complexity • They simplify blocking and synchronization • You can “look ahead” for urgent messages • However, there are some system imposed limitations in the size of a message (MSGMAX) and the size of a message queue (MSGMNB)

More Related