-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsynchro.tex
716 lines (643 loc) · 27.1 KB
/
synchro.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
include(`macros.m4')
\pagebreak
\pdfbookmark[0]{process synchronization and communication}{synchro}
\begin{slide}
\sltitle{Contents}
\slidecontents{6}
\end{slide}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\pdfbookmark[1]{shared data conflict}{conflict}
\begin{slide}
\sltitle{Problem: conflict while sharing data}
\begin{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\item \texttt{struct \{ int a, b; \} \emph{shared};}
]]])
\item
\begin{alltt}
for( ; ; ) \{
\emprg{/* non-atomic operation */
a = shared.a; b = shared.b;}
if (a != b) printf("NON-CONSISTENT STATE");
\emprg{/* non-atomic operation */
shared.a = val; shared.b = val;}
\}
\end{alltt}
\item if the cycle is run in 2 processes running in parallel (or threads)
that share the same structure and have different values of the
\texttt{val} variable, it will lead to conflicts due to non-atomic operations.
\end{itemize}
\end{slide}
\hlabel{SYNCHRONIZATION}
\begin{itemize}
\item Operations that can be expressed in C with single statement does not
have to be atomic. e.g. on RISC processors the command \verb.a++.
is typically translated into:
\begin{verbatim}
load reg,[a]
inc reg
store [a],reg
\end{verbatim}
due to the fact that on this architecture numbers cannot be incremented
directly in memory. For such cases there are sets of functions for atomic
arithmetic operations (e.g. \texttt{atomic\_add(3c)} on Solaris) that are
much faster than classic synchronization mechanisms.
More on page \pageref{ATOMIC_ADD}.
\item In general similar problem happens when multiple processes share a
system resource.
\item \hlabel{RACE_C} example \example{race/race.c}
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Conflict scenario}
ifdef([[[NOSPELLCHECK]]], [[[
\begin{tabular}{rl@{\hspace{2cm}}|c|c|}
]]])
\multicolumn{2}{l}{Processes \emsl{A}\texttt{(val==1)} and
\emsl{B}\texttt{(val==2)}} & \multicolumn{1}{c}{\texttt{a}} &
\multicolumn{1}{c}{\texttt{b}}\\
% \cline{3-4}
1. & initial structure state & ? & ? \\
% \cline{3-4}
2. & process \emsl{A} writes to member \texttt{a} & 1 & ? \\
% \cline{3-4}
3. & process \emsl{B} writes to member \texttt{a} & 2 & ? \\
% \cline{3-4}
4. & process \emsl{B} writes to member \texttt{b} & 2 & 2 \\
% \cline{3-4}
5. & process \emsl{A} writes to member \texttt{b} & 2 & 1 \\
% \cline{3-4}
6. & \multicolumn{1}{l}{\parbox[t]{5cm}{the structure is in inconsistent state
and one of the processes will find out.}}
\end{tabular}
\end{slide}
\begin{itemize}
\item more possibilities:
\begin{enumerate}
\item the structure is in a consistent state, e.g. \texttt{(1, 1)}
\item process \texttt{B} writes \texttt{2} into member \texttt{a}
\item process \texttt{A} reads the value of the structure \texttt{(2, 1)}
earlier than process \texttt{B} writes member \texttt{b}
\end{enumerate}
\item Note that synchronization problems may surface only after the program is
run on multiprocessor(core) machine or on more processors than what is used when
development. However, given that nowadays you have a few CPUs even on a
low-level laptop, it is easier to test such scenarios than in the past.
\end{itemize}
%%%%%
\pdfbookmark[1]{mutual exclusion}{mutexcl}
\begin{slide}
\sltitle{Solution: mutual process exclusion}
\begin{itemize}
\item it is necessary to ensure atomic operation on the structure, i.e.
while one processes modifies the structure, the other cannot manipulate it.
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\begin{tabular}{rl@{\hspace{2cm}}|c|c|}
]]])
\multicolumn{2}{l}{Processes \emsl{A}\texttt{(val==1)} and
\emsl{B}\texttt{(val==2)}} & \multicolumn{1}{c}{\texttt{a}} &
\multicolumn{1}{c}{\texttt{b}}\\
% \cline{3-4}
1. & initial structure state & ? & ? \\
% \cline{3-4}
2. & process \emsl{A} writes to member \texttt{a} & 1 & ? \\
% \cline{3-4}
\setbox0=\hbox{$\left\{\vphantom{\begin{tabular}{c}\hline1\\
\hline1\\ \hline1\\ \hline \end{tabular}}\right.$}
\ht0=0pt\dp0=0pt\box0
3. & process \emsl{B} must wait & 1 & ? \\
% \cline{3-4}
4. & process \emsl{A} writes to member \texttt{b} & 1 & 1 \\
% \cline{3-4}
\setbox0=\hbox{$\left\{\vphantom{\begin{tabular}{c}\hline1\\
\hline1\\ \hline \end{tabular}}\right.$}
\dimen0=\ht0 \advance\dimen0 by \dp0 \dimen0=0.25\dimen0
\ht0=0pt\dp0=0pt \raisebox{-\dimen0}{\box0}%
5. & process \emsl{B} writes to member \texttt{a} & 2 & 1 \\
% \cline{3-4}
6. & process \emsl{B} writes to member \texttt{b} & 2 & 2 \\
% \cline{3-4}
7. & \multicolumn{1}{l}{the structure is consistent.}\\
\end{tabular}
\end{slide}
\hlabel{CRITICALSECTION}
\begin{itemize}
\item It is necessary to ensure mutual exclusion also when reading,
so that the process that is reading cannot read inconsistent state while
another process is changing it. While writing it is necessary to exclude
all other processes. While reading it is sufficient to exclude only writers.
\item \emsl{\emph{critical section}} is piece of code that can be executed
only in one process (or thread), otherwise the operations can lead to
inconsistent state, e.g. wrongly connected linked list, mismatched database
indexes etc. It is possible to say that critical section is code which accesses
or modifies resource shared with multiple processes (or threads) and therefore
access to such code should be synchronized. A critical section should be as
short as possible to limit contention. The second definition is more generic,
it can also include situations where only one process (or thread) can change the
state but while that is not happening, more processes can read it
simultaneously.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Problem: readers and writers conflict}
\begin{itemize}
\item several processes running in parallel write protocol with operations
to common log file. Each new record is appended to the end of the file.
\item if the record writing operation is not atomic, the contents of multiple
records can be interleaved.
\item only single process can write.
\item other processes read data from the log file.
\item while reading record that is being written inconsistent data is retrieved.
\item while writing, it is not possible to read. If no process is writing,
multiple processes can read simultaneously.
\end{itemize}
\end{slide}
\begin{itemize}
\item 2 situations are permitted: one writer or multiple readers
\item On local disk the synchronization of writers can be archived via the
\texttt{O\_APPEND} flag, however this is not going to work on network file
systems such as NFS or in case it is necessary to perform multiple
\texttt{write()} operations for single record. Moreover this does not solve
the situation of readers -- it is still possible to read while writing.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Solution: file locking}
\begin{itemize}
\item writer process locks the file for writing. Other processes
(readers and writers) cannot work with the file and have to wait for the lock
to be unlocked.
\item reader process locks the file for reading. Writers have to wait
for the lock, other readers can also lock the file for reading and read data.
\item in each moment there can be at most one active lock for writing or
multiple locks for reading. Both locks cannot be locked simultaneously.
\item for efficiency each process should hold the lock for shortest time
possible and if possible do not lock the whole file -- only the section that
is being worked with. Passive waiting is preferred, active waiting is
suitable for very short time.
\end{itemize}
\end{slide}
\begin{itemize}
\item 2 ways of waiting:
\begin{description}
\item[active (busy waiting)] -- a process tests a condition in a cycle while
it is not true.
\item[passive] -- a process registers itself in the kernel as a waiting for
the condition and goes asleep. The kernel wakes it up if the condition becomes
true.
\end{description}
\item \hlabel{BUSYWAITING} active waiting is justifiable only in special
situations.
\end{itemize}
%%%%%
\pdfbookmark[1]{synchronization mechanisms}{synchromechs}
\begin{slide}
\sltitle{Synchronization mechanisms}
\begin{itemize}
\item theoretical solution -- mutual exclusion algorithms (Dekker
1965, Peterson 1981)
\item interrupt disable (1 CPU), special \emph{test-and-set} instructions
\item \emsl{lock-files}
\item tools offered by the operating system:
\begin{itemize}
\item \emsl{semaphores} (POSIX, System V IPC)
\item \emsl{file level locking} (\texttt{fcntl()}, \texttt{flock()})
\item thread synchronization: \emsl{mutexes} (surround critical sections,
only one thread can hold the mutex), \emsl{condition variables}
(the thread is blocked until another thread signals a condition change)
\emsl{read-write locks} (shared and exclusive locks, similar semantics
to file level locking)
\end{itemize}
\end{itemize}
\end{slide}
\begin{itemize}
\item Both Dekker and Peterson need to achieve the result via only shared
memory, i.e. several variables shared by processes. Busy waiting is used by
both algorithms when waiting to enter the critical section already occupied by
another process.
\item Dekker's solution is presented as a first solution to the problem of
mutual exclusion of 2 processes, without having to apply the mechanism of
strict alternation, i.e. if second process does not express the will to enter
critical section, the first can enter how many times it wants
(and ifdef([[[NOSPELLCHECK]]], [[[vice versa]]])).
Dekker's solution is not trivial, compare it with 16 year younger
Peterson solution, e.g. on Wikipedia.
\item We are not going to deal with theoretical algorithms or compare hardware
mechanisms used by the kernel. Instead we are going to focus on the use of
file level locking (which use the atomicity of some file operations)
and special synchronization primitives offered by the kernel.
\item note that ``signalling'' here has nothing to do with Unix signals. It's
confusing though.
\end{itemize}
%%%%%
\pdfbookmark[1]{lock files}{lockfiles}
\begin{slide}
\sltitle{Lock files}
\begin{itemize}
\item for each shared resource there is a previously agreed file path. Locking
is done by creating the file, unlocking by removing it. Each process waits
until it can create the yet non-existent file.
\end{itemize}
\begin{alltt}
void \funnm{lock}(char *lockfile) \{
int fd;
while((fd = open(lockfile,
O\_RDWR|O\_CREAT|\emprg{O\_EXCL}, 0600)) == -1) \{
sleep(1); {\rm/* busy waiting in loop for unlock */}
\}
close(fd);
\}
void \funnm{unlock}(char *lockfile) \{
unlink(lockfile);
\}
\end{alltt}
\end{slide}
\begin{itemize}
\item \hlabel{LOCK_UNLOCK} The key is the \texttt{O\_EXCL} flag.
\item Because the exclusive flag is not accessible in most shells, the shell
scripts usually use \texttt{mkdir} or \texttt{ln} (hard link) commands for
lock file synchronization.
\item example: \example{file-locking/lock-unlock.c} -- it is to be used together
with the \example{file-locking/run.sh} shell script.
\item In case of process crash the locks are not removed and therefore other
processes would wait forever. One idea is to write PID of the process
that created the lock to the lock file. The process that is waiting for unlock
can verify that the process with given PID number exists. If not, it can remove
the lock file and retry. User level command that can do this is e.g.
\emsl{\texttt{shlock}}(1) (on FreeBSD in \texttt{/usr/ports/sysutils/shlock}),
however could cause this situation:
\begin{itemize}
\item \emsl{watch out:} if multiple processes find out simultaneously that
the process does not exist, it can lead to error because the operation of
reading file contents and removing it is not atomic:
\begin{enumerate}
\item process A reads the contents of lock file, checks PID existence
\item process B reads the contents of lock file, checks PID existence
\item A deletes the lock file
\item A creates new lock file with its PID
\item B deletes the lock file
\item B creates new lock file with its PID
\item Now both processes think they acquired the lock.
\end{enumerate}
\end{itemize}
\item Another \emsl{problem} is that the \texttt{lock()} function above contains
active waiting.
This can be solved e.g. so that the process that acquired the lock can open
named pipe for writing. Reader processes will enter sleep by reading from the
pipe.
The \texttt{unlock()} will close the pipe and so the waiting processes will
be unblocked. See \example{pipe/countdown.c} for example with one reader.
\item Lock files are usually used for situations where multiple instances of
the same program are unwanted.
\end{itemize}
%%%%%
\pdfbookmark[1]{fcntl}{fcntl}
\begin{slide}
\sltitle{File locking: \texttt{fcntl()}}
\texttt{int \funnm{fcntl}(int \emph{fildes}, int \emph{cmd}, ...);}
\begin{itemize}
\item to set locks for file \texttt{fildes}:
\texttt{cmd}:
\begin{itemize}
\item \texttt{F\_GETLK} \dots{} takes lock description from \nth{3} argument
and replaces it with description of existing lock that collides with it.
\item \texttt{F\_SETLK} \dots{} sets or destroys lock described by the \nth{3}
argument. If the lock cannot be set immediately returns $-1$.
\item \texttt{F\_SETLKW} \dots{} like \texttt{F\_SETLK}, however puts
the process to sleep if it is not possible to set the lock
(\texttt{W} means ``wait'')
\end{itemize}
\item \nth{3} argument contains lock description and is pointer to
\texttt{struct flock}
\end{itemize}
\end{slide}
\begin{itemize}
\item Locking of files over NFS is done via the \texttt{lockd} daemon.
\item There are 2 types of locks:
\begin{description}
\item [advisory locks] -- for correct operation it is necessary that all
processes working with locked files check the locks before reading/writing.
Used more frequently.
\item [mandatory locks] -- if a file is locked, read/write operations will
automatically block the process, i.e. the lock will be applied also on
processes that do not explicitly work with the lock.
\begin{itemize}
\hlabel{MANDATORY}
\item not universally recommended, does not always work
(e.g. \texttt{lockd} implements just advisory locking)
\item for given file they are enabled by setting the SGID bit and
removing the right to execute for the group
(i.e. setting that otherwise does not make sense).
One process sets the lock (e.g. using \texttt{fcntl}). Other processes
then do not have to check the lock explicitly because each
\texttt{open/read/write} operation is checked by the kernel against
the file locks and enforces waiting till the lock is explicitly
unlocked by the owner process.\\
\item implemented e.g. on Solaris, Linux. FreeBSD does not support it.
The fcntl(2) man page on Linux (2013) does not recommend it because
the implementation contains errors that could lead to race conditions
and therefore the consistency cannot be generally achieved.
\end{itemize}
\end{description}
\item It is important to realize that when process exits, all its locks are
released.
\end{itemize}
%%%%%
\pdfbookmark[1]{flock}{flock}
\begin{slide}
\sltitle{File locking: \texttt{struct flock}}
\begin{itemize}
\item \texttt{l\_type} \dots{} lock type
\begin{itemize}
\item \texttt{F\_RDLCK} \dots{} shared lock (for reading)
\item \texttt{F\_WRLCK} \dots{} exclusive lock (for writing)
\item \texttt{F\_UNLCK} \dots{} unlock
\end{itemize}
\item \texttt{l\_whence} \dots{} same as for \texttt{lseek()}, i.e.
\texttt{SEEK\_SET},
\texttt{SEEK\_CUR}, \texttt{SEEK\_END}
\item \texttt{l\_start} \dots{} start of locked region with regards to
\texttt{l\_whence}
\item \texttt{l\_len} \dots{} length of the region in bytes, 0 means till the
end of the file
\item \texttt{l\_pid} \dots{} PID of the process holding the lock, used
only for \texttt{F\_GETLK} when returning.
\end{itemize}
\end{slide}
\begin{itemize}
\item If given part is not locked when using \texttt{F\_GETLK},
the \texttt{flock} structure is returned unchanged except for the first member
that is set to \texttt{F\_UNLCK}.
\item \hlabel{FCNTL_LOCKING} example: \example{file-locking/fcntl-locking.c}
\item \hlabel{FCNTL_FIXED_RACE_C} example on how to use \texttt{fcntl} (it is
,,fixed'' version of the previous \example{race/race.c} example from page
\pageref{RACE_C}): \example{race/fcntl-fixed-race.c}.
\item locking via \texttt{fcntl} and \texttt{lockf} has one important property
that is being described in the \texttt{fcntl} man page in FreeBSD:
\emph{This interface follows the completely stupid semantics of System V
and IEEE Std 1003.1-1988 (``POSIX.1'') that require that all locks
associated with a file for a given process are removed when any file
descriptor for that file is closed by that process. This semantic
means that applications must be aware of any files that a subroutine
library may access. For example if an application for updating the
password file locks the password file database while making the
update, and then calls \texttt{getpwnam}(3) to retrieve a record,
the lock will be lost because \texttt{getpwnam}(3) opens, reads, and
closes the password database.}
Linux contains a different kind of locking called \emph{Open file description
locks} (non-standard) that avoids this problem (the per file descriptor locks
are released only on the last close), however does not provide deadlock
detection.
\item The \texttt{lockf} function (SUSv3) is simpler variant of
\texttt{fcntl}, specifies only how to lock and how many bytes from the current
position in the file. Very often implemented as \texttt{fcntl} wrapper.
Beware: one cannot assume it is implemented around \texttt{fcntl} therefore
avoid using these together.
\item The example \example{file-locking/lockf.c} demonstrates how mandatory
locking works and the use of the \texttt{lockf} function.
\end{itemize}
%%%%%
\pdfbookmark[1]{deadlock}{deadlock}
\begin{slide}
\sltitle{Deadlock}
\begin{itemize}
\item 2 shared resources \texttt{res1} and \texttt{res2} protected by locks
\texttt{lck1} and \texttt{lck2}. Processes \texttt{p1} and
\texttt{p2} want to have exclusive access to both resources.
\end{itemize}
\begin{center}
\input{img/tex/deadlock.tex}
\end{center}
\begin{itemize}
\item \emsl{watch out for the locking order !}
\end{itemize}
\end{slide}
\begin{itemize}
\item Generally deadlock happens if process is waiting for an event that cannot
happen. Here e.g. 2 processes are waiting for each other, for the other to
release the lock however that will never happen. Next possibility is deadlock
of single process that is reading from a pipe after forgetting to close
the write end of the pipe. If there is no one else that has the pipe open,
the read will block because the all file descriptor copies of the write end are
not closed and therefore the end of file cannot happen until the reading process
closed the write end however it cannot do it because it is blocked.
in \texttt{read} syscall:
\hlabel{FIFODEADLOCK}
\begin{verbatim}
int main(void)
{
int c, fd;
mkfifo("test", 0666);
fd = open("test", O_RDWR);
read(fd, &c, sizeof(c));
/* never reached */
return (0);
}
$ ./a.out
^C
\end{verbatim}
\item \texttt{fcntl()} checks for deadlock and returns \texttt{EDEADLK}.
\item It is best to avoid deadlock by correct programming and do not rely on the
system.
\end{itemize}
%%%%%
\pdfbookmark[1]{IPC}{ipc}
\begin{slide}
\sltitle{IPC}
\begin{itemize}
\item \emsl{IPC} stands for \emph{Inter-Process Communication}
\item between processes \emsl{in single system}, e.g. does not include
network communication.
\item \emsl{semaphores} \dots{} used for process synchronization
\item \emsl{shared memory} \dots{} passing data between processes,
brings similar problems as shared files, the solution is to use semaphores.
\item \emsl{message queues} \dots{} provide both communication and
synchronization (waiting for message to arrive)
\item IPC means have \emsl{access rights} (for reading/writing) similarly to
files for owner, group and others.
\end{itemize}
\end{slide}
\begin{itemize}
\item IPC resources continue to exist even after the process that created them
is no longer around. To destroy them it is necessary to explicitly request this.
\item There are two types of standard IPC interfaces: System~V and POSIX.
In general System~V IPC has more complex albeit in some cases more capable API.
For more information about System~V IPC mechanisms, see \texttt{svipc(7)} man
page on any Linux distribution.
\item POSIX IPC API is much simpler and better designed than the System~V IPC
API. However, it is also much younger so you may see the System~V API used a
lot in existing code. For System~V semaphore API and examples, see page
\pageref{SYSVSEM}.
\item The POSIX API for IPC came in with extension 1003.1b (aka POSIX.4), see
page \pageref{POSIX4}.
\item From the shell the list of System~V IPS resources can be acquired using
the \texttt{ipcs} command. They can be deleted using the \texttt{ipcrm} command.
The state and contents of existing IPS resources is unchanged even if no process
works with them at the moment.
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Semaphores}
\begin{itemize}
\item introduced by E. Dijkstra
\item semaphore is data structure that contains:
\begin{itemize}
\item non-negative integer \texttt{i} (free capacity)
\item process queue \texttt{q}, that wait for free capacity
\end{itemize}
\item semaphore operations:
\begin{description}
\item [init(s, n)]~\\
empty \texttt{s.q; s.i = n}
\item [P(s)]~\\
\texttt{if(s.i > 0) s.i-- else\\
put calling process to sleep and add it to s.q }
\item [V(s)]~\\
\texttt{if(s.q empty) s.i++ else\\
remove one process from s.q and wake it up}
\end{description}
\end{itemize}
\end{slide}
\begin{itemize}
\item \emsl{P} is from dutch ifdef([[[NOSPELLCHECK]]],
[[[\uv{proberen te verlagen}]]]) -- try to
decrement, \emsl{V} from ifdef([[[NOSPELLCHECK]]], [[[\uv{verhogen}]]])
-- increment.
\item The \texttt{P(s)} and \texttt{V(s)} operations can be made generic:
the semaphore value is possible to change with any integer
\texttt{n} \dots{} \texttt{P(s,~n)}, \texttt{V(s,~n)}.
\item Allen B. Downey: \emph{The Little Book of Semaphores}, Second Edition,
on \url{http://greenteapress.com/semaphores/}
\item \emph{binary semaphore} has only values 0 or 1
\end{itemize}
%%%%%
\begin{slide}
\sltitle{Mutual exclusion with semaphores}
\begin{itemize}
\item one process initializes the semaphore
\begin{alltt}
sem s;
init(s, 1);
\end{alltt}
\item critical section is augmented with semaphore operations
\begin{alltt}
...
\emprg{P(s)};
critical section;
\emprg{V(s)};
...
\end{alltt}
\end{itemize}
\end{slide}
\begin{itemize}
\item semaphore initialized to \texttt{n} value allows \texttt{n} processes
to enter the critical section simultaneously. Here semaphore works like a lock.
The same process is unlocking (incrementing the value) and locking
(decrementing the value).
\item In general, the increment operation can be done by different process
than the one which performed the decrement operation (and
ifdef([[[NOSPELLCHECK]]], [[[vice versa]]])).
The mutexes work differently, see page \pageref{MUTEXES}.
\end{itemize}
%%%%%
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{sem\_open, sem\_wait, sam\_post, sem\_close}{posix-semaphores}
]]])
\hlabel{NAMED_SEMAPHORES}
\begin{slide}
\sltitle{POSIX API for semaphores}
ifdef([[[NOSPELLCHECK]]], [[[
\begin{minipage}{\slidewidth}\vspace{-1\baselineskip}\texttt{\begin{tabbing}
sem\_t \funnm{sem\_open}(\=const *char \emph{name}, int \emph{oflag},
\\\>mode\_t \emph{mode}, unsigned int \emph{value});
\end{tabbing}}
\end{minipage}
]]])
\begin{itemize}
\item creates or opens a new POSIX semaphore. \emph{mode} is same as for
\funnm{open}(). Use ``\texttt{/somename}'' for the \emph{name}.
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{sem\_wait}(sem\_t *\emph{sem});}
]]])
\begin{itemize}
\item decrement \emph{\texttt{sem}}, if currently 0, the call will block
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{sem\_post}(sem\_t *\emph{sem});}
]]])
\begin{itemize}
\item increment \emph{\texttt{sem}}. If \emph{\texttt{sem}} consequently becomes
greater than 0, another thread blocked in \funnm{sem\_wait}() will be woken up.
\end{itemize}
ifdef([[[NOSPELLCHECK]]], [[[
\texttt{int \funnm{sem\_close}(sem\_t *\emph{sem});}
]]])
\begin{itemize}
\item close \emph{\texttt{sem}}, free its resources allocated to \emsl{this}
process.
\end{itemize}
\end{slide}
\begin{itemize}
\item The POSIX semaphore APIs are part of the \emph{POSIX Realtime Extension}
and are optional. For example, macOS supports only named POSIX semaphores.
\item \emph{value} is the initial semaphore value
\item Both the \emph{mode} and \emph{value} parameters are optional. This means
that \emph{mode} has to be specified in order to set the initial value.
\item Named semaphore might appear on a file system. Usually in the form of
a node on virtual file system like \texttt{/dev/shm/} on Linux (if mounted).
\item \emph{\texttt{oflag}} can be a combination of only \texttt{O\_CREAT} and
\texttt{O\_EXCL}. If the semaphore already exists, an invocation with only
\texttt{O\_CREAT} silently succeeds, \texttt{O\_EXCL} will cause an error.
\item As soon as the semaphore is created, other processes can use it as
well, based on \emph{mode}.
\item If the semaphore is not removed via \funnm{sem\_unlink}(), it will exist
(in kernel) until the system is shut down.
\item There are also memory-based semaphores that do not use a name and are not
identified by the returned handle -- and thus use less resources. See
\funnm{sem\_init}() and \funnm{sem\_destroy}() for more information.
\item Example on POSIX semaphores (it is a fixed version of the previous example
\example{race/race.c} from page \pageref{RACE_C}):
\example{race/posix-sem-fixed-race.c}.
\item Another example: \example{semaphores/talking-heads.c}
\end{itemize}
%%%%%
\hlabel{POSIX_IPC}
ifdef([[[NOSPELLCHECK]]], [[[
\pdfbookmark[1]{shm\_open, sem\_open, mq\_open}{posixipc}
]]])
\begin{slide}
\sltitle{Other IPC facilities}
\begin{itemize}
\item POSIX and SUSv4 define other facilities for interprocess communication we
have not mentioned yet:
\begin{itemize}
\item \emsl{POSIX shared memory} accessible via \funnm{shm\_open}()
\item \emsl{POSIX queues} \dots{} \funnm{mq\_open}(),
\funnm{mq\_send}(), \funnm{mq\_receive}(), \dots{}
\item \emsl{System V APIs} for queues, shared memory, and semaphores
\end{itemize}
\item \emsl{sockets} come from the BSD world and allow communication in various
domains, e.g. \texttt{AF\_UNIX} (communication within the same system) and
\texttt{AF\_INET}/\texttt{AF\_INET6} (communication within the same system or
across network using internet protocols).
\end{itemize}
\end{slide}
\begin{itemize}
\item Sockets were accepted by other Unix systems as well and have been a part
of the UNIX specification since version 2.
\item There are other more system specific facilities. For example,
\emph{doors} is an RPC like facility on Solaris, designed to be used within the
same system only. It has some interesting features - not only it can be used for
inter-process communication however it can be used in kernel to request a
service from a userland program.
\item \texttt{shm\_open} returns file descriptor that can be then
mapped into process memory via \texttt{mmap} (see page \pageref{MMAP}).
See \example{shm/shm.c}.
On Linux, the shared segment is visible under \texttt{/dev/shm/} directory,
along with size and permissions. \texttt{shm\_unlink} destroys the shared
memory segment.
\end{itemize}
\hlabel{SYNCHRONIZATIONEND}
\endinput