Title: Critical Sections and Semaphores
1Critical Sections and Semaphores
- A critical section is code that contains access
to shared resources that can accessed by multiple
processes. - Critical sections can be managed with semaphores
- This chapter describes POSIX.1b semaphores and
System V semaphores
2Protecting CS
- Mutual Exclusion Only one process is in CS at a
time - Progress If no process is in CS, a process that
wishes to can get in - Bounded Wait No process can be postponed
indefinitely (starved)
3Non-Atomic Operations
- Implementations of c
- and c
- r1 c r2 c
- r1 r1 1 r2 r2 1
- c r1 c r2
- c 6
- Process A Process B
- c c
- At the end of processes A and B we expect c to be
incremented and decremented so the final value is
6. - However, if process A is interrupted after
completing the instruction r1 c and process B
executes to completion, c 5 at the end of B and
it is set to 7 after A finishes
4test-and-set/swap
- Test-and-Set and Swap are routines implemented in
hardware to coordinate lower level critical
sections such as the implementing of a semaphore
counter - Review section 8.1 if you are unfamiliar with
these operations
5Busy-Wait Semaphores
- wait while (s lt 0) noop
- (s)
- signal (s)
- s 1
- Process A Process B
- wait(s) wait(s)
- c c
- signal(s) signal(s)
- Busy-wait implementations waste CPU cycles
- One process can starve the others
6Waiting List Semaphores
- wait if (sp-gtvalue gt 0)
- sp-gtvalue
- else
- ltblock the current process and add it to
waiting list sp-gtlistgt - signal if (sp-gtlist ! NULL)
- ltremove process at head of semaphore queue
and place it in - ready queuegt
- else
- sp-gtvalue
7Motivation for and Syncronization
- A 1 B 1
- Process 1 Process 2 Process 3
- wait(A) wait(A) wait(B)
- ltuse resource Agt wait(B) ltuse resource Bgt
- signal(A) ltuse resources A and Bgt signal(B)
- signal(B)
- signal(A)
8and Syncronization
A 1 B 1 Process 1 Process 2 Process
3 wait(A) wait(A, B) wait(B) ltuse
resource Agt ltuse resources A and Bgt ltuse
resource Bgt signal(A) signal(A, B)
signal(B) wait(A, B) denotes
simultaneous wait on A and B. The semaphores A
and B are decremented only if both of them can
decremented without blocking
9and Syncronization Implementation
- wait if ((ap-gtvalue gt 0) (bp-gtvalue gt 0))
- (ap-gtvalue)--
- (bp-gtvalue)--
-
- else
- if (ap-gtvalue lt 0)
- ltadd current process to ap-gtlistgt
- else
- ltadd current process to bp-gtlistgt
- ltblock process and reset pc to beginning of
waitgt - signal ap-gtvalue
- ltmove all processes on ap-gtlist to ready
queuegt - bp-gtvalue
- ltmove all processes on bp-gtlist to ready queuegt
10Why Wake All Processes on Signal?
- Process 1 Process 2 Process 3
- a1 wait(A,B) a2 wait(B,C) a3
signal(B,C) - b1 ltcritical sectiongt b2 ltcritical sectiongt
- c1 signal(A,B) c2 signal(B,C)
- Assume a1 is executed followed by a2, the
semaphore queues are - A process 1
- B process 1, process 2
- C process 2
- Then, if a3 is executed and the signal only wakes
up the first process in each queue, the semaphore
queues are - A process 1
- B process 2
- C
- Now process 1 holds B while blocking on A.
Process 2 cannot proceed until process 1 gets A.
This defeats the purpose of - and synchronization
11POSIX.1b Semaphores
- POSIX.1b standard was adopted in 1993
- Since they are new, POSIX semaphores may not be
available in all operating systems even those
that claim to be POSIX.1 compliant - An implementation supports POSIX semaphores if
_POSIX_SEMAPHORES is defined in unistd.h It is
defined there on the ect-unix machines
12POSIX.1b Semaphore Variables
- Semaphore variable is of type sem_t
- Atomic operations for initializing, incrementing
and decrementing value - Unnamed semaphores Can be used by a single
process or by children of a process that created
it - Named semaphores Can be used by all processes
- Unnamed semaphores are similar in operation to
pipes, and named semaphores are similar to named
pipes
13POSIX.1b Semaphore Declaration
- include ltsemaphore.hgt
- sem_t sem
- sem is a semaphore variable
- POSIX.1b does not specify underlying type of
sem_t - One possibility is for it to act like a file
descriptor that points to a local table and the
table entries point to entries in a system file
table
14Semaphore Operations
- SYNOPSIS
- include ltsemaphore.hgt
- int sem_init (sem_t sem, int pshared, unsigned
int value) - int sem_destroy (sem_t sem)
- int sem_wait (sem_t sem)
- int sem_try (sem_t sem)
- int sem_post (sem_t sem)
- int sem_getvalue (sem_t sem, int sval)
- POSIX.1b
- All semaphore functions return 1 and set errno
on error - It is uncertain what semaphore functions return
on success, but usually 0 - _POSIX_SEMAPHORES may be defined but system may
NOT support POSIX.1b semaphores - POSIX.1b semaphores are counting semaphores
15sem_init
- Initializes semaphore to value parameter
- If the value of pshared is non-zero, the
semaphore can be used between processes (the
process that initializes it and by children of
that process) - If the value of pshared is zero, the semaphore
can only be used by threads of a single process - Think of sem as referring to a semaphore rather
than being the semaphore itself
16sem_destroy
- Destroys a previously initialized semaphore
- If sem_destroy attempts to destroy a semaphore
that is being used by another process, it may
return 1 and set errno to EBUSY Unfortunately,
the specifications do not require that the system
detect this
17sem_wait and sem_trywait
- sem_wait is a standard semaphore wait operation
- If the semaphore value is 0, sem_wait blocks
until it can successfully decrement value or when
interrupted such as by SIGINT - sem_trywait is similar to sem_wait except instead
of blocking on 0, it returns 1 and sets errno to
EAGAIN
18sem_post
- sem_post increments the semaphore value and is
the classical semaphore signal operation - sem_post must be async_signal_safe and may be
invoked from a signal handler
19sem_getvalue
- Allows the user to examine the value of a named
or unnamed semaphore - Sets the integer referenced by sval to the value
of the semaphore - If there are processes waiting for the semaphore,
POSIX.1b allows setting sval to either 0 or a
negative number whose absolute value is equal to
the number of waiting processes ambiguity! - Returns 0 on success and 1 and sets errno on
error
20Unnamed Semaphore Example
- include ltsemaphore.hgt
-
- void main()
-
-
- if (sem_init(my_lock, 1, 1)
- perror(could not initialize my_lock
semaphore) -
- for (i 1 i lt n i)
- if (childpid fork()) break
-
- if (sem_wait (my_lock) 1)
- perror (semaphore invalid) exit (1)
- Critical Section
- if (sem_post (my_lock) 1)
- perror (semaphore done) exit(1)
-
-
21Named Semaphores
- Named semaphores can synchronize processes that
do not have common ancestors - Have a name, user ID, group ID and permissions
just like files do - POSIX.1b does not require name to appear in file
system nor does it specify consequences of having
two processes refer to same name - If name begins with a slash (/), two processes
(or threads) open the same semaphore
22sem_open
- SYNOPSIS
- include ltsemaphore.hgt
- sem_t sem_open(const char name, int oflag)
- sem_t sem_open(const char namd, int oflag,
mode_t mode, - unsigned int value)
- POSIX.1b
- sem_open establishes a connection between a named
semaphore and a sem_t value - sem_open returns a pointer identifying the
semaphore
23sem_open oflag parameter
- oflag determines whether sem_open access a
previously defined semaphore or creates a new one - If oflag is 0 the semaphore is previously defined
with the same name If no such name is found
sem_open returns 1 and sets errno to ENOENT - oflag of O_CREAT or O_CREATO_EXCL means the
semaphore is not previously defined and requires
the second form of sem_open that includes
permissions and semaphore value - oflag of O_CREATO_EXCL opens a semaphore if one
of that name does not exist or returns 1 if one
DOES exist and sets errno to EEXIST
24sem_close
- SYNOPSIS
- include lt semaphore.hgt
- int sem_close (sem_t sem)
- int sem_unlink(const char name)
- POSIX.1b
- A process calls sem_close to deallocate system
resources allocated to the user of the semaphore - sem_close does not necessarily remove the
semaphore, but makes it inaccessible to the
process - _exit and exec system calls also deallocate
process semaphores
25sem_unlink
- sem_unlink removes a named semaphore from the
system - If there is still a reference to the semaphore,
destruction is delayed until the other references
are closed by sem_close, _exit or exec - Calls to sem_open with the same name after
sem_unlink with refer to a different semaphore
26Named Semaphore Example (top)
include ltsemaphore.hgt define S_MODE S_IRUSR
S_IWUSR void main() if ((my_lock sem_open
(my.dat, O_CREATO_EXCL, S_MODE, 1) 1)
errno ENOENT) perror(semaphore open
failed) exit(1) for (i 1 i lt n
i) if (childpid fork()) break
27Named Semaphore Example (bottom)
if (sem_wait (my_lock) 1)
perror (semaphore invalid) exit (1)
Critical Section if (sem_post (my_lock)
1) perror (semaphore done) exit(1)
if (sem_close (my_lock) 1)
perror(semaphore close failed) exit(1)
28System V Semaphores
- System V semaphores are part of the general
System V interprocess communication facility - A System V semaphore is created by executing a
semget system call - The semget creats a semaphore data structure in
the kernel and returns an integer handle to the
semaphore - Processes cannot access semaphore data structures
directly only through system calls - Semaphore ids or handles are analogous to file
descriptors
29Posix vs System V Semaphores
- System V semaphores, shared memory and message
queues are not part of POSIX.1 they are
included in the Spec 1170 specification - System V data structures are created and kept in
the kernel and are referenced through integer
handles - In POSIX.1, a program declares a variable of type
sem_t and passes a pointer to that variable
30Semaphore Sets
- A semaphore set is an array of semaphore elements
- A process can perform operations on the entire
set in a single system call - The internal representation of semaphore sets and
semaphore elements is not directly accessible - Each semaphore includes at least the following
- A nonnegative integer representing semaphore
value - Process ID of the last process to manipulate the
semaphore element - Number of processes waiting for the semaphore
element to increase - Number of processes waiting for the semaphore
element value to equal 0
31Semaphore Sets (Cont)
- Semaphore operations allow a process to block
until a semaphore element value is 0 or until it
becomes positive - Each element has two queues associated with it
a queue of processes waiting for the semaphore
element value to increase and a queue of
processes waiting for the value to equal 0
32Semaphore Creation
- SYNOPSIS
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
- int semget (key_t key, int nsems, int semflg)
- Spec 1170
33IPC_PRIVATE
include ltstdio.hgtinclude ltsys/stat.hgtinclude
ltsys/types.hgtinclude ltsys/ipc.hgtinclude
ltsys/sem.hgtdefine PERMS S_IRUSRS_IWUSRdefine
SET_SIZE 3int semidvoid main(void) int
seimid if ((semid semget(IPC_PRIVATE,
SET_SIZE, PERMS)) lt 0) perror("Could not
create new private semaphore") else
printf("Semaphore created with ID d\n",semid)
34Random Key
include ltstdio.hgtinclude ltsys/stat.hgtinclude
ltsys/ipc.hgtinclude ltsys/sem.hgtinclude
ltstring.hgtinclude lterrno.hgtdefine PERMS
S_IRUSRS_IWUSRS_IRGRPS_IWGRPS_IROTHS_IWOTHd
efine SET_SIZE 1define KEY ((key_t)99887)void
main(void) int semid if ((semid
semget(KEY, SET_SIZE, PERMS IPC_CREAT)) lt 0)
fprintf(stderr, "Error creating semaphore with
key d s\n", (int)KEY,
strerror(errno)) else printf("Semaphore
with ID d created for key d\n",semid,(int)KEY)
35Generate Key from ftok
int semid key_t mykey if (argc ! 3)
fprintf(stderr, "Usage s filename
id\n", argv0) exit(1) if
((mykey ftok(argv1, atoi(argv2)))
(key_t) -1) fprintf(stderr, "Could not
derive key from filename s s\n",
argv1, strerror(errno)) exit(1)
else if ((semid semget(mykey, SET_SIZE, PERMS
IPC_CREAT)) lt 0) fprintf(stderr, "Error
creating semaphore with key d s\n",
(int)mykey, strerror(errno)) exit(1)
36semop
- SYNOPSIS
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
- int semop(int semid, struct sembuf sops, int
nsops) - Spec 1170
- A process can increment, decrement or test
individual semaphore elements with the semop
system call - semid is handle returned by semget
- sops points to an array of element operations
- nsops specifies the number of element operations
in the sops array - semop returns 1 and sets errno on error
37struct sembuf
- struct sembuf has the following three fields
- short sem_num The number of the semaphore
element - short sem_op The particular operation to be
performed on the semaphore element - shore sem_flg The flags to specify options for
the opration
38sem_op
- If sem_op gt 0, semop adds the value to the
corresponding semaphore element and awakens
processes waiting for semaphore element to
increase - If sem_op 0, and semaphore element value is not
0, semop blocks the calling process (waiting for
0) and increments the count of processes waiting
for a zero value of that element - If sem_op lt 0, semop adds the value to the
corresponding semaphore element value provided
the result would not be negative. If the
operation would make the element value negative,
semop blocks the process on the event that the
semaphore element value increases. If the
resulting value is 0, semop wakes the processes
waiting for 0
39set_sembuf_struct
/ Example 8.21 /include ltsys/types.hgtinclude
ltsys/ipc.hgtinclude ltsys/sem.hgtvoid
set_sembuf_struct(struct sembuf s, int num,
int op, int flg) s-gtsem_num (short)
num s-gtsem_op op s-gtsem_flg flg
return Do not set sembuf elements in a
declaration such as struct sembuf myopbuf 1,
1, 0
40Semaphore Example 1
- struct sembuf GET_TAPES2
- struct sembuf RELEASE_TAPES2
- set_sembuf_struct((GET_TAPES0), 0,1,0)
- set_sembuf_struct((GET_TAPES1), 1,1,0)
- set_sembuf_struct((RELEASE_TAPES0), 0,1,0)
- set_sembuf_struct((RELEASE_TAPES0), 1,1,0)
- Process 1 semop(S, GET_TAPES,1)
- ltuse tape Agt
- semop(S, RELEASE_TAPES,1)
- Process 2 semop(S,GET_TAPES,2)
- ltuse tapes A and Bgt
- semop(S,RELEASE_TAPES,2)
- Process 3 semop(S, GET_TAPES 1,1)
- ltuse tape Bgt
- semop(S, RELEASE_TAPES 1,1)
41struct sembuf Example 1
- GET_TAPES0 RELEASE_TAPES0
- num 0 num 0
- op 1 op 1
- flg 0 flg 0
- GET_TAPES1 RELEASE_TAPES1
- num 1 num 1
- op 1 op 1
- flg 0 flg 0
42Semaphore Ex 2 (1)
int semid int semop_ret struct
sembuf semwait1 struct sembuf
semsignal1 if ( (argc ! 2) ((n
atoi (argv1)) lt 0) ) fprintf (stderr,
"Usage s number_of_processes\n", argv0)
exit(1) / Create a
semaphore containing a single element / if
((semid semget(IPC_PRIVATE, SET_SIZE, PERMS))
-1) fprintf(stderr, "ld Could not
access semaphore s\n", (long)getpid(),
strerror(errno)) exit(1)
43Semaphore Ex 2 (2)
/ Initialize the semaphore element to 1 /
set_sembuf_struct(semwait, 0, -1, 0)
set_sembuf_struct(semsignal, 0, 1, 0) if
(semop(semid, semsignal, 1) -1)
fprintf(stderr, "ld semaphore increment
failed - s\n", (long)getpid(),
strerror(errno)) if (remove_semaphore(semid
) -1) fprintf(stderr, "ld, could
not delete semaphore - s\n",
(long)getpid(), strerror(errno)) exit(1)
44Semaphore Ex 2 (3)
for (i 1 i lt n i) if (childpid
fork()) break Each child sends pid
and ppid info to buffer while(( (semop_ret
semop(semid, semwait, 1)) -1)
(errno EINTR)) if (semop_ret
-1) fprintf(stderr, "ld semaphore
decrement failed - s\n",
(long)getpid(), strerror(errno)) else
Each child prints out its pid and ppid buffer
while(((semop_ret semop(semid, semsignal, 1))
-1) (errno EINTR))
if (semop_ret -1)
fprintf(stderr, "ld semaphore increment
failed - s\n", (long)getpid(),
strerror(errno))
45Semaphore Ex 2 (4)
while((wait(status) -1) (errno
EINTR)) if (i 1) / the original
process removes the semaphore / if
(remove_semaphore(semid) -1)
fprintf(stderr, "ld, could not delete
semaphore - s\n", (long)getpid(),
strerror(errno)) exit(0)
46Semaphore Example 3
- include ltstdio.hgt
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
- include lt string.hgt
- include lt errno.hgt
- define SET_SIZE 2
- struct sembuf myopSET_SIZE
- set_sembuf_struct((myop0), 0, 0, 0)
- set_sembuf_struct((myop1), 0, 1, 0)
- if (semop(semid, myop, 2) -1)
- perror(Semaphore operation failed)
47struct sembuf Example 2
- myop0
- num 0
- op 0
- flg 0
- myop1
- num 0
- op 1
- flg 0
48Create and Set Semaphore (top)
include ltsys/stat.hgtinclude ltsys/ipc.hgtinclud
e ltsys/sem.hgtinclude ltstdio.hgtinclude
lterrno.hgtdefine PERMS S_IRUSRS_IWUSRdefine
SET_SIZE 2int create_and_initialize(key_t mykey,
int value) int semid struct sembuf getlock,
setlock, setinit semid semget(mykey,
SET_SIZE,
PERMSIPC_CREATIPC_EXCL) if ((semid lt 0)
(errno ! EEXIST)) perror("Semaphore
create failed") return -1 else if
(semid gt 0) / initialize the semaphore
and open the lock / set_sembuf_struct(set
init, 1, value, 0) semop(semid, setinit,
1) set_sembuf_struct(setlock, 0, 1, 0)
semop(semid, setlock, 1)
49Create and Set Semaphore (bottom)
else / semaphore already exists --
wait for initialization / if ((semid
semget(mykey, SET_SIZE, PERMS)) lt 0)
perror("Could not access existing semaphore")
return -1
set_sembuf_struct(getlock, 0, -1, 0)
semop(semid, getlock, 1)
set_sembuf_struct(setlock, 0, 1, 0)
semop(semid, setlock, 1) return
semid
50semctl
- include ltsys/types.hgt
- include ltsys/ipc.hgt
- include ltsys/sem.hgt
- int semctl(int semid, int semnum, int cmd, /
union semun arg / - semctl querries or sets the values of individual
semaphore elements - semid identifies the semaphore set
- semnum indicates the semaphore element within the
set - cmd refers to individual elements and specifies
which command is to be executed - arg is used differently for different cmd values
51Important Commands
- GETVAL Return the value of a specific semaphore
element - GETPID Return process ID of last process to
manipulate - element
- GETNCNT Return number of processes waiting for
element to - increment
- GETZCNT Return number of processes waiting for
element to - become 0
- SETVAL Set value of a specific semaphore
element to arg.val - IPC_RMID Remove the semaphore identified by
semid - IPC_SET Set the permissions of the semaphore
- semctl returns 1 on error and and sets errno
On success the return value is 0 for all commands
except GETVAL, GETPID, GETNCNT, and GETZCNT
return the value of the command
52semnum Paramater
- union semnum
- int val
- struct semid_ds buf
- ushort array
- May not be included directly in programs, since
some systems do not define it in semaphore header
files
53initialize _sem_element
int initialize_sem_element(int semid, int
semnum, int semvalue) union semun arg
arg.val semvalue return semctl(semid,
semnum, SETVAL, arg)void main(void) int
semid int retval if ((semid
semget(IPC_PRIVATE, 2, 0600)) lt 0)
perror("Could not create new private
semaphore") retval initialize_sem_element(se
mid,1,3) printf("initialize_sem_element
returned d\n",retval) initialize_sem_element
returns 0 on success and 1 and sets errno on
error
54remove_semaphore
/ Example 8.26 /include ltsys/types.hgtinclude
ltsys/ipc.hgtinclude ltsys/sem.hgt int
remove_semaphore(int semid) return
semctl(semid, 0, IPC_RMID)
55Command Prompt Semaphore Ops
- List all semaphores ipcs s
- Remove semaphore id 12345 ipcrm 12345
56semop_restart
/ Example 8.29 /include ltsys/types.hgtinclude
ltsys/ipc.hgtinclude ltsys/sem.hgtinclude
lterrno.hgtint semop_restart(int semid, struct
sembuf sops, int nsops) int retval
while ( ((retval semop(semid, sops, nsops))
-1) (errno EINTR) )
return retval
57sem_wait_restart
/ Example 8.30 /include ltsemaphore.hgtinclude
lterrno.hgtint sem_wait_restart(sem_t sem)
int retval while ( ((retval sem_wait(sem))
-1) (errno EINTR) )
return retval