GWU CS 259 Brad Taylor Spring 2004 1
• IPC Overview
• Semaphore Sets
• Shared Memory
• Message Queues
• Producer-Consumer Synchronization
• Project Status Briefs (45% code review)
• Assignment 4: deferred to next week, finish project coding this week!
GWU CS 259 Brad Taylor Spring 2004 2
• IPC: What means exist? Why is it necessary?
• What is the terminology?
• How does it work?
• How to deal with multiple processes?
• Concurrency … in yet another form
GWU CS 259 Brad Taylor Spring 2004 3
• An Independent process is and does not affect another process execution
• A Cooperating process can affect or be affected by execution of another process
• Process cooperation Benefits :
– Information sharing
– Computation speed-up
– Modularity
– Convenience
• Cooperation Costs :
– Complexity
– Overhead: Memory, Synchronization, Transmission
• Analogy: Team vs. Individual task accomplishment
GWU CS 259 Brad Taylor Spring 2004 4
• Mechanisms allowing processes to communicate and synchronize actions
• Semaphore & Shared Memory methods extended
• Message system: processes communicate with others without resorting to shared variables
• 3 Different, but similar means, to accomplish same goal
• Shared Memory fastest, but requires external synchronization; OS must maintain link count
• Semaphore sets fast; most useful as generic advisory locking mechanisms
• IPC message facility slowest, but most flexible, providing two operations:
– send(destination, message) – message size fixed or variable
– receive(source, message)
GWU CS 259 Brad Taylor Spring 2004 5
• If P and Q wish to communicate, they need to:
– establish a communication link between them
(uni-/multi-cast)
– exchange messages via send/receive
• Implementation of communication link
– physical (shared memory, hardware bus)
– logical (message queue)
• Other IPC mechanisms covered previously:
– shared variables with locks (2)
– files (3)
– pipes (3)
– signals (5)
– threads {semaphores & shared memory} (6)
– network sockets remain
GWU CS 259 Brad Taylor Spring 2004 6
• How are links established?
• Can a link be associated with more than two processes?
• How many links can there be between every pair of communicating processes?
• What is the capacity of a link?
• Is the size of a message that the link can accommodate fixed or variable?
• Is a link unidirectional or bi-directional?
GWU CS 259 Brad Taylor Spring 2004 7
• Processes must name each other explicitly:
– send (P, message) – send a message to process P
– receive(Q, message) – receive a message from process
Q
• Properties of communication link
– Links established automatically
– Link associated with exactly one pair of communicating processes
– Between each pair there exists exactly one link
– Link may be unidirectional, but usually bi-directional
GWU CS 259 Brad Taylor Spring 2004 8
• Messages directed to and received from mailboxes (aka ports or keys )
– Each mailbox has a unique id
– Processes communicate only through shared mailboxes
• Communication link properties
– Link established only if processes share a common mailbox
– A link may be associated with many processes
– Each pair of processes may share several communication links
– Link may be unidirectional or bi-directional
GWU CS 259 Brad Taylor Spring 2004 9
• Operations
– create a new mailbox
– send and receive messages through mailbox
– destroy a mailbox
• Primitives are defined as:
• send(A, message) – send a message to mailbox A
• receive(A, message) – receive a message from mailbox A
GWU CS 259 Brad Taylor Spring 2004 10
• Mailbox sharing
– P
1
, P
2
, and P
, sends; P
2
3 share mailbox A
– P
1 and P
3 receive
– Who gets the message?
• Solutions
– Allow a link to be associated with at most two processes
– Allow only one process at a time to execute a receive operation
– Allow the system to select receiver arbitrarily (e.g., first available); sender notified who receiver was
– Example: Queue for multiple printer pool
GWU CS 259 Brad Taylor Spring 2004 11
• Message passing may be either blocking or non-blocking
• Blocking is considered synchronous
• Non-blocking is considered asynchronous
• send and receive primitives may be either blocking or non-blocking
GWU CS 259 Brad Taylor Spring 2004 12
• Queue of messages attached to the link; implemented in one of three ways:
– 1. Zero capacity – 0 messages: Sender must wait for receiver (rendezvous)
– 2. Bounded capacity – finite length of n messages: Sender must wait if link full
– 3. Unbounded capacity – infinite length:
Sender never waits
GWU CS 259 Brad Taylor Spring 2004 13
Shared data:
#define BUFFER_SIZE 10 typedef struct {
. . .
} item ; item buffer[BUFFER_SIZE]; int in = 0; int out = 0; int counter = 0;
GWU CS 259 Brad Taylor Spring 2004 14
Producer process item nextProduced; while (1) { while (counter == BUFFER_SIZE)
; /* do nothing */ buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; counter++;
}
GWU CS 259 Brad Taylor Spring 2004 15
Consumer process item nextConsumed; while (1) { while (counter == 0)
; /* do nothing */ nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; counter--;
}
GWU CS 259 Brad Taylor Spring 2004 16
• The statements counter++; counter--; must be performed atomically.
• Atomic operation means an operation that completes in its entirety without interruption
• If both the producer and consumer attempt to update the buffer concurrently, the assembly language statements may get interleaved
• Interleaving depends upon how producer and consumer processes scheduled
• Race condition: The situation where several processes access – and manipulate shared data concurrently: the final value of the shared data depends upon which process finishes last
• To prevent race conditions, concurrent processes must be synchronized
GWU CS 259 Brad Taylor Spring 2004 17
• Motivation: Similar to file locking used as generic advisory locking mechanisms to control:
– access to files
– shared memory
– other resources atomically
• Operations:
– set it
– check it
– wait until it clears then set it ("test-n-set")
– That’s it!
•
GWU CS 259 Brad Taylor Spring 2004 18
• semget() adds new semaphore to current process; if successful, created semaphore ID semid returns int semget(key_t key, int nsems, int semflg);
• Common to the 3 POSIX IPC methods is the key means of identifying the mailbox; ftok() creates a unique one: key_t ftok(const char *path, int id);
– path is a file that this process can read
– id is usually just set to some arbitrary char, like 'A'
– ftok() generates a probably-unique key for semget()
– processes using the same semaphore must generate the same key , so the same parameters should be passed
• nsems is the number of semaphores in this semaphore set
• semflg tells semget() new semaphore set permissions, if a new set is being created or connecting to an existing one (creating permissions for a new set may be done bit-wise or the accessed with IPC_CREAT)
GWU CS 259 Brad Taylor Spring 2004 19
#include <sys/ipc.h>
#include <sys/sem.h> key_t key; int semid; key = ftok("/brad/assignments/hw4", ‘M'); semid = semget(key, 10, 0666 | IPC_CREAT);
• Creates a 10 semaphore set, generates the key with ftok() and, with 666 (rw-rw-rw-) permissions
GWU CS 259 Brad Taylor Spring 2004 20
• All operations that set, get, or test-n-set a semaphore use the semop() system call and structure sembuf int semop(int semid ,struct sembuf *sops, unsigned int nsops); struct sembuf { ushort sem_num; short sem_op; short sem_flg;
};
• semid : key of semaphore set
• sops : pointer to the struct sembuf
• nsop : As an array of struct sembuf s to do many semaphore operations at the same time, the nsop argument addresses this one; if only using one, this argument is 1
• sem_num : semaphore number (in the set) being manipulated
• sem_op :
– Positive: The value of sem_op is added to the semaphore's value: this is how a process uses a semaphore to mark a resource as allocated
– Negative: While absolute value of sem_op > semaphore value, calling process blocks until semaphore value reaches sem_op absolute value; then, absolute value of sem_op is subtracted from semaphore's value: this is how a process releases semaphore-guarded resource
– Zero: This process will wait until the semaphore in question reaches 0
• sem_flg : allows specifying flags to further modify semop() call effects
21
Common includes, all 3 programs:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
• Run seminit.c
to create the semaphore
• Try using ipcs from the command line to verify it exists
• Them run semdemo.c
in a couple of windows to see how they interact
• Finally, use semrm.c
to remove the semaphore
• Might also try removing the semaphore while running semdemo.c
just to see what kinds of errors are generated
GWU CS 259 Brad Taylor Spring 2004 22
int main(void)
{ key_t key; int semid; union semun arg; if ((key = ftok("semdemo.c", 'J')) == -1) { perror("ftok"); exit(1);
}
/* create a semaphore set with 1 semaphore: */ if ((semid = semget(key, 1, 0666 | IPC_CREAT)) == -1) { perror("semget"); exit(1);
}
/* initialize semaphore #0 to 1: */ arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(1);
} return 0;
}
GWU CS 259 Brad Taylor Spring 2004 23
int main(void)
{ key_t key; int semid; struct sembuf sb = {0, -1, 0}; /* set to allocate resource */
{ if ((key = ftok("semdemo.c", 'J')) == -1) perror("ftok"); exit(1);
}
/* grab the semaphore set created by seminit.c: */ if ((semid = semget(key, 1, 0)) == -1) { perror("semget"); exit(1);
} printf("Press return to lock: "); getchar(); printf("Trying to lock...\n");
} if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(1);
} printf("Locked.\n"); printf("Press return to unlock: "); getchar(); sb.sem_op = 1; /* free resource */ if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(1);
} printf("Unlocked\n"); return 0;
GWU CS 259 Brad Taylor Spring 2004 24
int main(void)
{ key_t key; int semid; union semun arg; if ((key = ftok("semdemo.c", 'J')) == -1) { perror("ftok"); exit(1);
}
/* grab the semaphore set created by seminit.c: */ if ((semid = semget(key, 1, 0)) == -1) { perror("semget"); exit(1);
}
/* remove it: */ if (semctl(semid, 0, IPC_RMID, arg) == -1) { perror("semctl"); exit(1);
} return 0;
}
GWU CS 259 Brad Taylor Spring 2004 25
• Motivation: shared memory is what it sounds like: a segment of memory shared between processes: think of the potential!
– Example: allocate a block a player information for a multiplayer game; each process access it at will: fun, fun, fun!
– Fastest form of IPC between processes
– Once shared memory region is mapped into process address space, no further kernel involvement in passing data between processes, contrary to others
– However, synchronization required
• Operations:
– Creating
– Attaching
– Using
– Destroying
– Synchronizing
GWU CS 259 Brad Taylor Spring 2004 26
• shmget() shared memory segment is created and connected to current process; if successful, returns a shared memory segment identifier int shmget(key_t key, size_t size, int shmflg);
• As in Semaphore sets, ftok() generates a probablyunique key for shmget()
• size is the shared memory segment size (bytes)
• shmflg should be set to the permissions of the segment bitwise-ORd with IPC_CREAT if you want to create the segment (always allowable), but can be 0 otherwise
GWU CS 259 Brad Taylor Spring 2004 27
#include <sys/ipc.h>
#include <sys/shm.h> key_t key; int shmid; key = ftok("/brad/travel/hawaii", 'R'); shmid = shmget(key, 1024, 0644 | IPC_CREAT);
• Creates a 1K segment with 644 (rw-r--r--) permissions
GWU CS 259 Brad Taylor Spring 2004 28
• shmat() Before using shared memory, the process must attach to it using this call void *shmat(int shmid, void *shmaddr, int shmflg);
• shmid is shared memory ID from shmget() call
• shmaddr may be used to tell shmat() which specific address to use, but setting it to 0 (strongly preferred) allows the OS choose the address for you
• shmflg can be set to SHM_RDONLY if you only want to read from it, 0 otherwise
GWU CS 259 Brad Taylor Spring 2004 29
#include <sys/ipc.h>
#include <sys/shm.h> key_t key; int shmid; key = ftok("/brad/travel/hawaii", 'R'); shmid = shmget(key, 1024, 0644 | IPC_CREAT); data = shmat(shmid, (void *)0, 0);
• This returns a pointer to the shared memory segment
• Notice that shmat() returns a void pointer; in this case, a char pointer
• It may be treated as anything, depending on what kind of data is there
• Pointers to arrays of structures are just as acceptable as anything else
GWU CS 259 Brad Taylor Spring 2004 30
Reading and Writing:
• Continuing with the data pointer from the previous example: as a char pointer, reading and writing chars from it; also, for simplicity, presume it contains a null-terminated string
• It couldn't be easier: as it's just a string in there, print is like this: printf("shared contents: %s\n", data);
• Storing something in it as easily as this: printf("Enter a string: "); gets(data);
• Of course, other data can be handled in there besides just chars
GWU CS 259 Brad Taylor Spring 2004 31
• Destroying:
– When done with the shared memory segment, the process should detach itself from it using the shmdt() call: int shmdt(void * shmaddr ); shmaddr is the address you got from shmat()
– When no longer used by any processes it must be removed: shmctl(shmid, IPC_RMID, NULL);
• Concurrency:
– As mentioned previously, Semaphores provide good controls for shared memory access
GWU CS 259 Brad Taylor Spring 2004 32
Note: Semaphores omitted for simplicity, but any use of shared memory should include them!
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 1024 /* make it a 1K shared memory segment */ int main(int argc, char *argv[])
{ key_t key; int shmid; char *data; int mode; if (argc > 2) { fprintf(stderr, "usage: shmdemo [data_to_write]\n"); exit(1);
}
GWU CS 259 Brad Taylor Spring 2004 33
/* make the key: */ if ((key = ftok("shmdemo.c", 'R')) == -1) { perror("ftok"); exit(1);
}
/* connect to (and possibly create) the segment: */ if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1) { perror("shmget"); exit(1);
}
/* attach to the segment to get a pointer to it: */ data = shmat(shmid, (void *)0, 0); if (data == (char *)(-1)) { perror("shmat"); exit(1);
}
GWU CS 259 Brad Taylor Spring 2004 34
/* read or modify the segment, based on the command line: */ if (argc == 2) { printf("writing to segment: \"%s\"\n", argv[1]); strncpy(data, argv[1], SHM_SIZE);
} else printf("segment contains: \"%s\"\n", data);
/* detach from the segment: */ if (shmdt(data) == -1) { perror("shmdt"); exit(1);
} return 0;
}
GWU CS 259 Brad Taylor Spring 2004 35
• Motivation: Message Queues provide flexibility
– Although not as fast as semaphores allows cooperating tasks to communicate with each other
– Allows messages to be queued and sent to & received from processes & interrupt service routines
– Multiple processes can sent to & received from a common message queue
– Work similar to a FIFO, but support some additional functionality as, generally, messages are taken off the queue in order put on; however, there are ways to pull certain messages from the queue before reaching front
• Operations:
– Creating
– Sending
– Receiving
– Destroying
GWU CS 259 Brad Taylor Spring 2004 36
• msgget() adds new message queue to current process; if successful, created message queue returns ID semid
(or if fails, returns -1) int msgget(key_t key, int msgflg);
• As in the other IPC methods, ftok() generates a probably-unique key for msgget()
• msgflg tells msgget() how to handle this new queue:
– For creation, this field must be set equal to IPC_CREAT bitwise OR'd with the permissions for this queue
– The queue permissions are same as standard file permissions--queues take on the user-id and group-id of creating process
GWU CS 259 Brad Taylor Spring 2004 37
• Passing information to a message queue uses msgsnd(): int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
• Each message is made up of two parts, which are defined in the template structure struct msgbuf , as defined in sys/msg.h
: struct msgbuf {
}; long mtype;
***OR*** char mtext[1]; struct pirate_msgbuf { long mtype; /* must be positive */ char name[30]; char ship_type; int notoriety; int cruelty; int booty_value;
};
• msgid : key of message queue
• msgp : pointer to data being put on the queue
• msgsz : data size (bytes) being added to the queue
• msgflg : allows setting some optional flag parameters, ignored for by setting to 0
GWU CS 259 Brad Taylor Spring 2004 38
#include <sys/ipc.h>
#include <sys/msg.h> key_t key; int msqid; struct pirate_msgbuf pmb = {2, "L'Olonais", 'S', 80, 10, 12035}; key = ftok("/brad/atsea/pirates", 'b'); msqid = msgget(key, 0666 | IPC_CREAT); msgsnd(msqid, &pmb, sizeof(pmb), 0); /* stick him on the queue
*/
• The mtype field is arbitrarily set to 2: that’s important for receiving
GWU CS 259 Brad Taylor Spring 2004 39
• Receiving information from a message queue uses msgrcv(): int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
• Others defined before; mtype within msgbuf affects msgrcv based on msgtyp value:
– Positive: Get the next message with an mtype equal to the specified msgtyp
– Negative: Retrieve the first message on the queue whose mtype field is less than or equal to the absolute value of the msgtyp argument
– Zero: Retrieve the next message on the queue, regardless of its mtype
• Example:
#include <sys/ipc.h>
#include <sys/msg.h> key_t key; int msqid; struct pirate_msgbuf pmb; /* where L'Olonais is to be kept */ key = ftok("/home/beej/somefile", 'b'); msqid = msgget(key, 0666 | IPC_CREAT); msgrcv(msqid, &pmb, sizeof(pmb), 2, 0); /* get him off the queue! */
GWU CS 259 Brad Taylor Spring 2004 40
2 Programs: kirk.c adds messages to the message queue, and spock.c retrieves them kirk.c:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h> struct my_msgbuf { long mtype; char mtext[200];
}; int main(void)
{ struct my_msgbuf buf; int msqid; key_t key; if ((key = ftok("kirk.c", 'B')) == -1) { perror("ftok"); exit(1);
}
GWU CS 259 Brad Taylor Spring 2004 41
} if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) { perror("msgget"); exit(1);
} printf("Enter lines of text, ^D to quit:\n"); buf.mtype = 1; /* we don't really care in this case */ while(gets(buf.mtext), !feof(stdin)) { if (msgsnd(msqid, (struct msgbuf *)&buf, sizeof(buf), 0) == -1) perror("msgsnd");
} if (msgctl(msqid, IPC_RMID, NULL) == -1) { perror("msgctl"); exit(1);
} return 0;
GWU CS 259 Brad Taylor Spring 2004 42
spock.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
}; struct my_msgbuf { long mtype; char mtext[200]; int main(void)
{ struct my_msgbuf buf; int msqid; key_t key; if ((key = ftok("kirk.c", 'B')) == -1) { /* same key as kirk.c */ perror("ftok"); exit(1);
}
GWU CS 259 Brad Taylor Spring 2004 43
if ((msqid = msgget(key, 0644)) == -1) { /* connect to the queue */ perror("msgget"); exit(1);
} printf("spock: ready to receive messages, captain.\n");
{ for(;;) { /* Spock never quits! */ if (msgrcv(msqid, (struct msgbuf *)&buf, sizeof(buf), 0, 0) == -1) perror("msgrcv"); exit(1);
} printf("spock: \"%s\"\n", buf.mtext);
} return 0;
}
GWU CS 259 Brad Taylor Spring 2004 44
• n processes all competing to use some shared data
• Each process has a code segment, called
critical section, in which the shared data is accessed
• Problem – ensure that when one process is executing in its critical section, no other process is allowed to execute in its critical section
GWU CS 259 Brad Taylor Spring 2004 45
1. Mutual Exclusion: If process P i is executing in its critical section, then no other processes can be executing in their critical sections {remember yellow submarine}
2. Progress: If no process is executing in its critical section and there exist some processes that wish to enter their critical section, then the selection of the processes that will enter the critical section next cannot be postponed indefinitely
3. Bounded Waiting: A bound must exist on the number of times that other processes are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted
Assume that each process executes at a nonzero speed
No assumption concerning relative speed of the n processes
GWU CS 259 Brad Taylor Spring 2004 46
Only 2 processes, P
0 and P
1
General structure of process P i
do { entry section
(other process P j
) critical section exit section reminder section
} while (1);
Processes may share some common variables to synchronize their actions
GWU CS 259 Brad Taylor Spring 2004 47
• Shared variables:
– int turn; initially turn = 0
– turn - i P i
• Process P i can enter its critical section
do {
while (turn != i) ; critical section
turn = j; reminder section
} while (1);
• Satisfies mutual exclusion, but not progress
GWU CS 259 Brad Taylor Spring 2004 48
• Shared variables
– boolean flag[2]; initially flag [0] = flag [1] = false.
– flag [i] = true P i ready to enter its critical section
• Process P i do { flag[i] := true; while (flag[j]) ; critical section flag [i] = false; remainder section
} while (1);
• Satisfies mutual exclusion, but not progress requirement
GWU CS 259 Brad Taylor Spring 2004 49
Combined shared variables of algorithms 1 and 2:
Process P i
do { flag [i]:= true; turn = j; while (flag [j] and turn = j) ; critical section flag [i] = false; remainder section
} while (1);
Meets all three requirements; solves the criticalsection problem for two processes
GWU CS 259 Brad Taylor Spring 2004 50
Critical section for n processes
• Before entering its critical section, thread receives a number. Holder of the smallest number enters the critical section.
• If processes P then P i i and P j receive the same number, if i < j, is served first; else P j is served first.
• The numbering scheme always generates numbers in increasing order of enumeration; i.e., 1,2,3,3,3,3,4,5...
• Notation < lexicographical order (ticket #, process id #)
– (a,b) < c,d) if a < c or if a = c and b < d
– max (a
…, n – 1
,…, a
n-1
) is a number, k, such that k a i for i - 0,
• Shared data boolean choosing[n]; int number[n];
• Data structures are initialized to false and 0 respectively
GWU CS 259 Brad Taylor Spring 2004 51
do { choosing[i] = true; number[i] = max(number[0], number[1], …, number
[n – 1])+1; choosing[i] = false; for (j = 0; j < n; j++) { while (choosing[j]) ; while ((number[j] != 0) && (number[j,j] < number[i,i])) ;
} critical section number[i] = 0; remainder section
} while (1);
GWU CS 259 Brad Taylor Spring 2004 52
• Synchronization tool not requiring busy waiting
• Semaphore S an integer variable, accessed only by two atomic operations wait ( S ): while S 0 do no-op ;
S --; signal ( S ):
S++;
• Each Semaphore has an associated queue
• Can define a non-blocking version of wait( S )
GWU CS 259 Brad Taylor Spring 2004 53
Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes
Let S and Q be two semaphores initialized to 1
P
0
wait(S);
wait(Q);
P
1
wait(Q);
wait(S);
signal(S);
signal(Q);
signal(Q) signal(S);
Starvation – indefinite blocking: a process may never be removed from the semaphore queue in which it is suspended
GWU CS 259 Brad Taylor Spring 2004 54
• Semaphore Sets
• Shared Memory
• Message Queues
• Producer-Consumer Synchronization
• Project Status Briefs (45% code review)
• Assignment 4: deferred to next week, finish project coding this week!
GWU CS 259 Brad Taylor Spring 2004 55
Group I: Dan, Ricardo & Kamal / Wire Socket
Group II: Clayton, Jason / Named Pipes
Group III: Brooke, Nush, Ram / Shared Memory, Semaphores &
Futexs
Integration: Nate & Brad
Issues to discuss:
Code Review for Each Group
Error checking
Rough draft code submission for integration review
Further Guidance, Questions
Good discussion tonight; concepts & code converging!
Remember to bring code next Monday (4/5/04)
GWU CS 259 Brad Taylor Spring 2004 56