0% found this document useful (0 votes)
41 views7 pages

Struct Cours6 e 4

The document discusses semaphores and their implementation in operating systems. It explains that [1] semaphores are shared integer variables that can only be accessed through wait and signal operations, [2] wait decrements the semaphore if it is positive and suspends the process if not, while signal increments the semaphore, [3] semaphores are implemented in the kernel through system calls that atomically execute wait and signal.

Uploaded by

manavbanga
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
41 views7 pages

Struct Cours6 e 4

The document discusses semaphores and their implementation in operating systems. It explains that [1] semaphores are shared integer variables that can only be accessed through wait and signal operations, [2] wait decrements the semaphore if it is positive and suspends the process if not, while signal increments the semaphore, [3] semaphores are implemented in the kernel through system calls that atomically execute wait and signal.

Uploaded by

manavbanga
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Reminder: The Semaphore concept

A semaphore is a shared integer variable. Its value is positive or 0 and it


can only be accessed through the two operations wait(s) and signal(s),
where s is an identifier representing the semaphore.

Semaphores and their


• wait(s) decrements s if s > 0 ; if not, the process executing the
implementation operation wait(s) is suspended.

• signal(s) increments s. The execution of signal(s) can have as result


(possibly delayed) that a process waiting on the semaphore s resumes
its execution. Executing a wait(s) or a signal(s) operation is done
without any possible interaction (atomically).

158 159

Implementing Semaphores

Mutual exclusion with semaphores • Semaphores are implemented in the system kernel.

Semaphores make a very simple implementation of mutual exclusion – The semaphore values are kept in a table stored in kernel memory.
possible. A semaphore is identified by a number corresponding to a position
in this table.

semaphore s = 1; – There are system calls for creating or freeing semaphores, as well
as for executing the wait and signal operations. These operations
are executed in supervisor mode and hence atomically (interrupts
Process 1 : Process 2 :
are disabled in supervisor mode).
while (True) while (True)
{ nc1: /* non critical { nc2: /* non critical • In ULg03, to execute for instance a wait operation, the arguments of
section */ ; section */ the system call, i.e. the semaphore number and a code WAIT, are
wait(s); wait(s); placed on the stack. Assuming that the semaphore number is
crit1: /* critical section */ ; crit2: /* critical section */ contained in r0, this can be done as follows.
signal(s); signal(s) PUSH(r0) | 2nd argument
} } CMOVE(WAIT,r1) | 1st argument
PUSH(r1)
SVC() | system call

160 161
The SVC handler

Before getting into the details of the handlers for semaphore operations,
we will look at the general handler needed for the SVC instruction. The
The SVC handler (continued)
handler is reached through the following stub.

h_stub: ST(r0, User, r31) | save • The program Svc_handler identifies the nature of the call and switches
ST(r1, User+4, r31) | the registers to the corresponding routine.
. . .
ST(r30, User+30*4, r31)
• One difficulty is that the arguments of SVC are on the stack of the
CMOVE(KStack, SP) | Load the system SP
process that has executed the system call, i.e. in its virtual space.
BR(Svc_handler,LP) | Call the handler
LD(r31, User, r0) | restore
LD(r31, User+4, r1) • These addresses must thus be translated before being used.
LD(r31, User+30*4, r30)
JMP(XP) | return to application

Note that the address saved in XP is that of the instruction following SVC.

162 163

The SVC handler:


data structures The SVC handler:
translating virtual addresses
The data structures used by the kernel are the following.
To have access to the stack of the process that has executed the
struct Mstate { int R0; ..., R30;} User;
instruction SVC, the system needs to translate into a physical address the
/* The saved state of the current process */
address contained in r29 (SP). Indeed, even if the page table cache is up
struct PTentry {short valid, resid ; int PhysPage;}; to date, address translation is not done by the hardware since we are in
/* An entry in the page table */ supervisor mode. This address translation can be done as follows.

struct PTentry PageMap[262144]; int Vmap(int Vaddress)


/* The page table of the current process, 2^18 entries */
{ int VpageNo, PageNo, Offset;
struct PD {struct Mstate state ; int status, semwait ;
struct PTentry PageMap[262144];} Proctbl[N]; VpageNo = Vaddress>>14; Offset = Vaddress & 16383;
/* The process table with page table, status, and the address if (PageMap[VpageNo].valid == 0 || PageMap[VpageNo].resid == 0)
of a blocking semaphore if any */ Pagerror(VpageNo);
PageNo = PageMap[VpageNo].PhysPage ;
int sem[K]; /* The table of semaphore values */ return((PageNo<<14) | Offset);
}
int Cur; /* The index of the current process */
164 165
The SVC handler:
general structure

The SVC handler extracts the code identifying the call and switches to the
The handlers Wait_h and Signal_h: first version
corresponding function.

Svc_handler() Wait_h(int semno)


{ int code; { if (sem[semno] <= 0) {
code = *Vmap(User.R29-4) User.R30 = User.R30 - 4; /* SVC will be executed again */
switch (code) Proctbl[Cur].status = WAIT; Proctbl[Cur].semwait = semno;
{ .... scheduler();
} else
case WAIT : Wait_h(*Vmap(User.R29-8)); break; sem[semno] = sem[semno] - 1;
case SIGNAL : Signal_h(*Vmap(User.R29-8)); break; }
....
}
When the value of the semaphore is not positive, the process is
}
suspended; it will reexecute the SVC when it is reactivated.
The addresses of the arguments of the system call are not computed
exactly as in the case of a procedure call since, in the case of a system
call, nothing is added to the stack of the calling process after the call.
166 167

Signal_h(int semno)
{ for (i = 0; i<N; i++) {
The handlers Wait_h and Signal_h: second version
if (Proctbl[i].status == WAIT && Proctbl[i].semwait == semno)
Proctbl[i].status = RUN; Wait_h(int semno)
} { if (sem[semno] <= 0) {
sem[semno] = sem[semno] + 1; Proctbl[Cur].status = WAIT; Proctbl[Cur].semwait = semno;
} scheduler();
} else
sem[semno] = sem[semno] - 1;
The processes that are waiting on the semaphore are reactivated and the
}
semaphore is incremented.

The system call is not reexecuted when a process leaves its wait state.
All the waiting processes will reexecute the system call wait; the one that
Indeed, the handler for signal will take care of reactivating a waiting
will find a nonzero value for the semaphore can be anyone of these.
process if there is one, without the semaphore being incremented and
decremented.
For mutual exclusion, this implementation will not guarantee that each
process can access its critical section.

168 169
Signal_h(int semno) Implementing semaphores on a multiprocessor
{ int i,wokeup = 0;
for (i = 0; i<N; i++) {
if (Proctbl[i].status == WAIT && Proctbl[i].semwait == semno) { • On a multiprocessor machine, execution in supervisor mode does not
Proctbl[i].status = RUN; guarantee mutual exclusion since it can occur simultaneously on more
wokeup = 1; break; than one processor.
}
} • Another mechanism for implementing mutual exclusion is thus needed.
if (wokeup == 0) sem[semno] = sem[semno] + 1;
}
• Atomic memory reads and writes are not sufficient for a practical
This implementation guarantees that, if only two processes use the solution.
semaphore, none will wait indefinitely.

• One thus introduces a special instruction that can atomically read


A fair implementation for more than two processes will use a wait queue
AND modify memory.
per semaphore for handling the waiting processes.

170 171

The microcode of TCLR


The instruction “Test and Clear”
TCLR(Ra, Literal, Rc) (supervisor mode)
This instruction copies a memory word to a register and sets it to 0.
Opcode = 000100 IRQ = * PC31 = 1
TCLR(Ra,literal,Rc) : PC ← PC + 4
EA ← Reg[Ra] + SEXT(literal) Phase Fl. Latch UC/D ALU LD DR PC+ SVR
flags F,Cin ,M SEL SEL
Reg[Rc] ← Mem[EA]
00000 * 1 0 000000 0011 0001 0 0 SMAR <- Ra
Mem[EA] ← 0 00001 * 1 0 000000 0001 0100 0 0 A <- SRAM
00010 * 1 0 000000 0010 0010 0 0 B <- Lit
It’s opcode is chosen to be 0x04 00011 * 1 0 000000 0011 0000 0 0 SMAR <- Rc
00100 * 1 0 100110 0100 0011 0 0 DMAR <- A+B
00101 * 1 0 000000 0101 0101 0 0 SRAM <- DRAM
00110 * 1 0 001101 0110 0011 0 0 DRAM <- 0
.macro TCLR(Ra,Lit,Rc) ENC_LIT(0b000100,Ra,Rc,Lit)
00111 * 1 0 000000 0100 0110 1 0 DMAR <- PC; PC+
01000 * 1 0 000000 0000 0101 0 0 INSTREG <- DRAM

172 173
The ”buffer” or ”producer-consumer” problem
Mutual exclusion with TCLR
A Producer process sends a stream of information to a Consumer process.
It is quite simple to implement mutual exclusion with TCLR.
This stream of information goes through a buffer modeled as a table in
shared memory accessed with the two functions (append and take).
wait: TCLR(r31,lock,r0)
BEQ(r0,wait) /* shared memory */
int in = 0, out = 0;
... section critique ... int buf[N];

CMOVE(1,r0) append(int v) int take()


ST(r0,lock,r31) { buf[in]= v; { int v;
in = (in+1) % N; v = buf[out];
This implementation uses an active wait and does not guarantee fairness } out = (out+1) % N;
among the waiting processes. return v;
}
It is not used to implement mutual exclusion in general, but it is perfectly
As such these operations allow writing in a full buffer or reading from an
adequate for implementing semaphores on a multiprocessor machine.
empty one. they need to be synchronized.

174 175

The buffer problem:


The buffer problem:
Synchronizing the consumer
Synchronizing the consumer and the producer

To prevent the consumer from taking an element from an empty buffer, a


A second semaphore, initialized to the number of empty slots in the
semaphore that counts the number of elements in the buffer is used.
buffer, is used to synchronize the producer.
/* shared memory */
/* shared memory */
int in, out = 0;
int in, out = 0;
int buf[N];
int buf[N];
semaphore n = 0;
semaphore n = 0, e = N;

append(int v) int take()


append(int v) int take()
{ buf[in]= v; { int v;
{ wait(e); { int v;
in = (in+1) % N; wait(n);
buf[in]= v; wait(n);
signal(n); v = buf[out];
in = (in+1) % N; v = buf[out];
} out = (out+1) % N;
signal(n); out = (out+1) % N;
return v;
} signal(e);
}
return v;
It is still necessary to limit the producer when the buffer is full. }

176 177
The buffer problem:
Mutual exclusion of the operations

If there are several producers and consumers, it is necessary to ensure that


Binary semaphores
the buffer manipulation operations are performed in mutual exclusion.
This can be done with an additional semaphore.
• A binary semaphore is a semaphore whose value can only be 0 or 1.
/* shared memory */
int in, out = 0; int buf[N];
• Thus type of semaphore makes it possible to synchronize processes,
semaphore n = 0, e = N, s = 1;
but not to count.
append(int v) int take()
{ wait(e); { int v;
• A general semaphore may nevertheless be simulated by a binary
wait(s); wait(n);
semaphore and a counter.
buf[in] = v; wait(s);
in = (in+1) % N; v = buf[out];
signal(s); out = (out+1) % N; • Some problems can nevertheless be caused by the split between the
signal(n); signal(s); semaphore and the counter.
} signal(e);
return v;
}
178 179

The buffer with binary semaphores


The buffer with binary semaphores:
/* shared memory */ Solution for one producer and one consumer
int in = 0, out = 0, count = 0;
int buf[N]; /* shared memory */
semaphore n = 0, e = 0, s = 1; int in = 0, out = 0, count = 0;
int buf[N]; int ewait = 0 ,nwait = 0;
append(int v) int take() semaphore n = 0, e = 0, s = 1;
{ wait(s); { int v;
if (count == N) { wait(s); append(int v) int take()
signal(s);wait(e);wait(s);} if (count == O) { { wait(s); { int v;
buf[in] = v; in = (in+1)%N; signal(s);wait(n);wait(s);} if (count == N) { wait(s);
count = count+1; v = buf[out];out = (out+1)%N; ewait = 1; signal(s); if (count == O) {
if (count == 1) signal(n); count = count-1; wait(e);wait(s);ewait = 0;} nwait = 1; signal(s);
signal(s); if (count == N-1) signal(e); buf[in] = v; in = (in+1)%N; wait(n);wait(s);nwait = 0;}
} signal(s); return v; count = count+1; v = buf[out];out = (out+1)%N;
} if (nwait > 0) signal(n); count = count-1;
signal(s); if (ewait > 0) signal(e);
This solution is not correct since it is possible to execute append; take;
} signal(s); return v;
take. The problem is that the presence of an element is signaled twice: by
}
setting count to 1 and by the operation signal(n).
180 181
The buffer with binary semaphores:
Solution for several producers and consumers
• To avoid signaling twice, signal(n) or signal(e) are only executed if a
/* shared memory */
process is waiting.
int in = 0, out = 0, count = 0;
int buf[N]; int ewait = 0 ,nwait = 0;
• One can imagine extending this solution to several producers and semaphore n = 0, e = 0, s = 1;
consumers by counting the number of waiting processes.
append(int v) int take()
{ wait(s); { int v;
• However, one must for example avoid a situation in which a second if (count == N) { wait(s);
consumer overtakes a consumer that has just been allowed to proceed ewait++;signal(s);wait(e); if (count == O) {
(has been released) and consumes the element intended for this ewait--;} nwait++; signal(s);wait(n);
consumer. buf[in] = v;in = (in+1)%N; nwait--;}
count = count+1; v = buf[out];out = (out+1)%N;
if (nwait > 0) signal(n); count = count-1;
• This can be done by transferring mutual exclusion to the process that
else signal(s); if (ewait > 0) signal(e);
has been released.
} else signal(s);
return v;
}
182 183

Conclusions on semaphores

• Semaphores are an excellent mechanism for handling processes that


have to be suspended (put in a wait state).

• When suspending processes is combined with manipulating a counter,


semaphores are perfectly well adapted.

• Attempting to use binary semaphores shows that difficulties can occur


when waiting implemented with semaphores is combined with
manipulating another data structure.

• It would be useful to have a systematic approach for handling this


type of problem.

184

You might also like