250 likes | 397 Views
CS162B: Semaphores (and Shared Memory). Jacob T. Chan. Pthreads : The Problem. It is not readily available in all systems Windows doesn’t have it Pthreads only work in the program they are in It won’t work outside of its program It does not deal with two different programs
E N D
CS162B: Semaphores (and Shared Memory) Jacob T. Chan
Pthreads: The Problem • It is not readily available in all systems • Windows doesn’t have it • Pthreads only work in the program they are in • It won’t work outside of its program • It does not deal with two different programs • What if you have two different programs that need to communicate with one another? • Files are okay and useful, but risky (file permissions and overwrites, which lead to RACE CONDITIONS) • Objective: ensure that other shared resources are not accessed simultaneously
Semaphores and Shared Memory • No need to invoke libraries (no more –lpthread) • Use the following headers: • #include <sys/types.h> • #include <sys/ipc.h> • #include <sys/sem.h> • #include <sys/shm.h> • These headers should be present in any semaphore-shared memory related operation/s
Semaphore • System of visual signaling that uses flags held in both hands • Shared resource • An integer that operates on multiple processes via queue-like system • With simultaneous operations running arbitrarily • Includes rest of data that allows facilitates such operations
Implementing Semaphores intsemid; // need to remember this int key = A_LARGE_CONSTANT; // ex. 1234 int n = 1; // or another number semid = semget(key, n, IPC_CREAT | 0666); • 1 key = 1 semaphore *array* • n = number of elements in the array; if n > existing array size, then it will result in failure. • last argument indicates flags and permissions. • IPC_CREAT = create if necessary • 0666 = complete access for everyone (equivalent to chmod 777) • Returns-1 on error. Otherwise, it returns a semaphore ID
More on semget() • You can use the same key for different programs • This will return the same semaphore array • At least you won’t use different keys for different arrays • BUT, it is risky • You might be using a key that is already in use by another unrelated program • Example: running central servers (with the same port, but we aren’t discussing network socketing YET)
Accessing Semaphores structsembufsema[2]; // perform 2 operations • [0]: operation to wait for semaphore to become 0 (block if another process or thread is accessing the semaphore) • [1]: operation to increment semaphore intopNum = 2; // like I said, perform 2 operations
Accessing Semaphores sema[0].sem_num = 0; // index of 1st element in array sema[0].sem_op = 0; // wait if semaphore != 0 sema[0].sem_flg = SEM_UNDO; //allocate space for undo operation sema[1].sem_num = 0; sema[1].sem_op = 1; // increment semaphore by 1 sema[1].sem_flg = SEM_UNDO | IPC_NOWAIT; • If same sem_num, it means that you operate on SAME semaphore in array (which is the usual thing)
Accessing Semaphores intopResult = semop(semid, sema, opNum); • Will return -1 on error. • SEM_UNDO= Allocate space for an undo operation if process terminates abnormally. • Prevents semaphores to be locked forever (until the system reboots) • IPC_NOWAIT= Force error if operation must wait. • Although sema has 2 elements, opNumcan be set to 1 if you ONLY need the 1st operation performed (the wait and get blocked operation)
Accessing Semaphores • If sema[0] is run, calling process blocks if semaphore is NOT 0 • Implication: another process/thread has control over the semaphore • It will not block if semaphore is 0 already • Reason: remember it’s the waiting operation? • If it is not blocked anymore, sema[1] will be performed next • Semaphore is increased and semop() returns • If semop() returns a value that is not -1, then the process has gained control!
After the access… • Do what you want to do! • The process shared has access already to the shared memory that semaphore was guarding • Obviously, SHARED resource is involved, so after the process does its thing, it must release the resource (otherwise, a deadlock occurs) • Illustration: Using an object you just borrowed
The Release opNum = 1; // perform 1 operation sema[0].sem_num = 0; // index of 1st element in array sema[0].sem_op = -1; // decrement semaphore by 1 sema[0].sem_flg = SEM_UNDO | IPC_NOWAIT; opResult = semop(semid, sema, opNum); • Note: There are many other ways to indicate control of a semaphore.
The Release • After releasing, the other process that were waiting for it can now gain control of it • But what is that resource that the semaphore needs to protect?
Shared Memory • Shared resource that can be accessed like a string (array of characters) • Does not make the actual file, but only the string themselves • This is stored in a shared memory (hence, the name)
Initializing Shared Memory intshmid; intkey = A_LARGE_CONSTANT; // ex. 1234 int size = ANOTHER_CONSTANT; //preferably a power of 2 shmid= shmget(key, size, IPC_CREAT | 0666); • 1 key = 1 shared memory segment • size = size of segment to be read • Will return -1 on error.
Using Same Keys • Just like semaphores, you can use same key for a different program • This way, calling shmget() will return the same memory segment • But there is also that same risk that the key is used by another unrelated program (especially true when running a central server), which is the same as your semaphores
Accessing Shared Memory #include <string.h> // for strcpy char *sharedMem; char *buffer; • buffer is character array with fixed size sharedMem= shmat(shmid, NULL, 0); • Will return -1 on error. • Use if((int) sharedMem == -1) to check if shared memory is not allocated
Reading/Writing to Shared Memory • Writing to memory using strcpy (char *destination, char *source); strcpy(sharedMem, buffer); • Writing from memory using strcpy (char *destination, char *source); strcpy(buffer, sharedMem); • Note: Data written in shared memory or the buffer to be read is ASSUMED to end in ‘\0’ because otherwise, there will be a segmentation fault
Inter-Process Communication • When semaphores and shared memory is used, there is inter-process communication • Works like pipes, except that they take turns in writing and reading to shared memory • Summary: • Initialize shared resources (semaphore and shared memory) • Gain control of semaphore • Access shared memory • Release semaphore
Illustration (Application) • Using the toilet when taking a dump • Going to the barber shop • And many more… these are just some real-life illustrations of semaphores and shared memory
Lab 10: producer.c and consumer.c • Create two c programs, named producer.c and consumer.c, that does the following: • producer.c will be run on the command line using a key provided by the user. The other program, consumer.c, will run also, using this key. • NOTE: Key is supposed to be unique. To check if a semaphore key is already taken, use the ipcs command. • The command ipcs displays all the semaphore keys that are currently in use by shared memory, semaphores, and message queues. Use this for your consumer and producer programs • The producer.c program will be invoked as follows: ./producer key filename
Lab 10: producer.c and consumer.c • The key will represent the key to be used for writing in the shared memory, while the filename is the name of the file that is to be written to shared memory • The writing to shared memory will only happen on a per block basis (1 block = 4096 bytes) • Accessing the producer and consumer must be synchronized using a semaphore set (so there should be a semaphore set included!) • This is to ensure that the producer will only write data to shared memory if consumer consumes data (like for example, if a block is already consumed, only will the producer produce) • Use semget() to create a new semaphore in the semaphore set
Lab 10: producer.c and consumer.c • The shared memory must be organized as follows: char data[4096]; int size; //size written to shared memory • Producer allocates the shared memory using the system call shmget() • This returns an int address location, which will be used for using shmat() • The shared memory can be written to or read from after its address is obtained using shmat() • When both producer and consumer are done with processing the file, shared memory should be deleted using shmdt() • Semaphores should be deleted by the producer after successful processing of the file using shmctl()
Lab 10: producer.c and consumer.c • NOTE: Both semaphore and shared memory SHOULD use the same key value (to maintain consistency) • Thesame goes for running consumer.c, except it will be the read part of the program • NOTE: consumer.c should be able to write results to a file named output.txt • Basically, this is like copying to a file, except with shared memory • After copying the file, producer should DELETE THE PREVIOUS FILE to show that it has been transferred to the consumer side (we are CUTTING the file, not copying it) • Test your program this way (suggestion): • Copy your producer.c program to a new folder and consumer.c in another new folder • Then copy a file from producer to the folder with consumer.c in it • This is like piping, except that we do it using semaphores
Lab 10: producer.c and consumer.c • You SHOULD be able to handle errors, like wrong arguments, file opening errors, semaphore errors, etc. • Legal stuff • Include a Certificate of Authorship, along with your producer.c and consumer.c programs • Filename: CS162B_Lab10_<Section>_<Surname>_<ID Number>.tar • Deadline: Next Week Midnight • Tuesday for Section A • Thursday for Section B