UNIX Process is any program which is executed

  • Slides: 49
Download presentation
UNIX Process is any program which is executed by computer’s operation system. Kernel is

UNIX Process is any program which is executed by computer’s operation system. Kernel is Operation System, that interacts with hardware and provides services like Memory Management, CPU scheduling, Filesystem, I/O Device access, etc. System Call is direct entry point, provided by Kernel, through which active Process can obtain the service Process Representation in Memory User Context Stack Kernel Context Kernel Data Text Actual machine instructions to be executed by hardware Data Heap Program’s data, declared during program start Uninitialized Data Heap Data space, dynamically allocated by the Process Initialized Read -Write Data Initialized Read -Only Data Stack read from program file when program is executed Dynamically allocated Stack Frames, containing return address linkage and data elements for each function call Text © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Process Attributes • Process ID • Parent Process ID • Real User ID (RUID

Process Attributes • Process ID • Parent Process ID • Real User ID (RUID = UID of user, who launched the program) • Real Group ID (RGID = GID of user, who launched the program) • Saved User ID (SUID = UID of program file owner) • Saved Group ID (SGID = GID of program file owner) • Effective User ID (EUID = SUID if ‘Set user ID on execution’ bit is set = RUID else) • Effective Group ID (EGID = SGID if ‘Set group ID on execution’ bit is set = RGID else) • Process Group ID (PGID = PID of session leader process) • Terminal Group ID • Root Directory File Access Mode Word • Current Working Directory ls -l Octal Value • Initial Argument Vector • Initial Environment Vector • File Mode Creation Mask • List of open File Descriptors • Time left until scheduled alarm signal © D. Zinchin [zinchin@gmail. com] rwx rwx s s t • Signal Handling Settings Introduction to Network Programming in UNIX & LINUX Meaning

Fork System Call #include <unistd. h> pid_t fork (); © D. Zinchin [zinchin@gmail. com]

Fork System Call #include <unistd. h> pid_t fork (); © D. Zinchin [zinchin@gmail. com] fork parent child Parent: PID 1 Child: PID 2 Stack Heap Data PID 2 Text … child. PID = fork(); … 0 pointer to Text pid_t child. PID; /* typedef int pid_t */ … child. PID = fork(); if (child. PID < 0) { perror(“fork failed”); exit(1); } else if (child. PID > 0) /* This is Parent */ { /* Parent processing */ … exit(0); } else /* child. PID == 0 */ /* This is Child */ { /* …Child processing */ exit(0); } pointer to Text • The fork() system call creates the copy of process that is executing. • It is an only way in which a new process is created in UNIX. • Returns: child PID >0 - for Parent process, 0 - for Child process, -1 - in case of error (errno indicates the error). • Fork reasons: -Make a copy of itself to do another task simultaneously -Execute another program (See exec system call) Child Process has Shared Text and Copied Data. It differs from Parent Process by following attributes: • PID (new) • Parent PID (= PID of Parent Process) • Own copies of Parent file descriptors • Time left until alarm signal reset to 0 Introduction to Network Programming in UNIX & LINUX

Exit System Call Exit and Wait System Calls #include <stdlib. h> void exit (int

Exit System Call Exit and Wait System Calls #include <stdlib. h> void exit (int status); • Terminates process execution. • Exit status is passed to the Kernel and then available to the Parent Process (with wait() call) • Only low-order 1 byte used to specify the exit status (0 - success, 1 -255 – error) Wait System Call Parent wait fork Kernel Process Status: 2 low-order bytes Child exit Exit Status: 1 low-order byte #include <sys/wait. h> pid_t wait (int *p_status); • Waits for one of the children process to finish. • Returns: child PID > 0 - PID of child process terminated with exit system call or with signal, -1 - if there aren’t child processes or if wait interrupted with signal (errno=EINTR). • If p_status is not NULL, the 2 low-order bytes will be filled with process status information Zombie Process Child process terminated with exit(), but yet not wait()-ed by Parent process, becomes Zombie or Defunct. Its main resources are released by Kernel, but it still present in Process Table until Parent will read its status. If a parent process terminated without wait()-ing for its children, the parent PID of each child process is set to 1. PARENT CHILD Running, calls WAIT Running, WAIT is not called yet Terminated, WAIT was not called © D. Zinchin [zinchin@gmail. com] Running Terminated Parent blocks waiting for Child Termination Parent accepts Child’s PID and termination status immediately. Child is deleted from Process Table. ------PPID of Child is set to 1 Child becomes Zombie, Child remains in Process Table until WAIT will be called PPID of Child is set to 1, INIT process will call WAIT. Introduction to Network Programming in UNIX & LINUX 4

Waitpid System Call #include <sys/wait. h> pid_t waitpid (pid_t pid, int *p_status, int options);

Waitpid System Call #include <sys/wait. h> pid_t waitpid (pid_t pid, int *p_status, int options); • Waits for one of the children process to finish (performs functionality of wait with additional options). • The pid argument may be: >0 - PID of particular process =0 - sender’s process group =-1 - all sender’s processes <-1 - process group ID = abs(pid) • The options argument may be: WNOHANG - check status of terminated processes in non-blocking mode WUNTRACED - report also the status of stopped child processes • If p_status is not NULL, the 2 low-order bytes will be filled with Process Status information. • Returns: child PID > 0 of child process terminated with exit system call or with signal, -1 - if there aren’t child processes, -1 (errno=EINTR) - if wait interrupted with signal, -1 (errno=ECHILD) - if argument pid is invalid, -1 (errno=EINVAL) - if argument options is invalid, 0 - if WNOHANG option is specified and child process(es) still running Process Status Evaluation To evaluate process status the following MACROs are used: int * p_status Logical (0/1) Arithmetical: ( Exit Status / Signal Number ) WIFEXITED(status), WEXITSTATUS(status), Terminated due to exit() WIFSIGNALED(status), WTERMSIG(status), Terminated by signal WIFSTOPPED(status), WSTOPSIG(status) Stopped ( reported only by waitpid() ) © D. Zinchin [zinchin@gmail. com] Process Status Introduction to Network Programming in UNIX & LINUX High Order byte Low Order byte Exit Status 0 0 Signal Number WSTOPFLG

Exec System Call #include <unistd. h> int execl (const char *path, const char *arg

Exec System Call #include <unistd. h> int execl (const char *path, const char *arg 0, . . . , const char *argn, char * /*NULL*/); int execv (const char *path, char *const argv[ ]); int execlp (const char *file, const char *arg 0, . . . , const char *argn, char * /*NULL*/); int execvp (const char *file, char *const argv[ ]); int execle (const char *path, const char *arg 0, . . . , const char *argn, char * /*NULL*/, char *const envp[ ]); int execve (const char *path, char *const argv[ ], char *const envp[ ]); • Executes the program as existing process, replacing current process image with new one. • Program arguments could be passed as NULL-terminated list (arg 0, …, arg. N) or vector (argv[ ]), argument 0 would specify program name, arguments 1 -N specify parameters. • Target program file could be specified by path or as file name to be used with PATH variable. • In form with envp argument, also replaces new image environment. • It is an only way in which a program is executed in UNIX. Example: Starting two new programs from Shell • As result of exec…() system call the following process exec prog 2 attributes could be changed: - Effective User ID sh fork - Effective Group ID fork - Initial Environment Vector exec prog 1 - The signals, which were set to be caught by caller, are changed to terminate the new program How user session is started. init (because code of old signal handlers became unavailable) PID=1 • In case of error exec() returns -1, errno indicates the error. fork exec Example: execute “ls. /” init execl(“/bin/ls”, “. /”, NULL); © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX exec getty ask credentials login /bin/sh check password, load user configuration

File System Management (Input/Output) System Calls With each File, open by specific Process, the

File System Management (Input/Output) System Calls With each File, open by specific Process, the Kernel associates the unique integer value in the scope of this process. This integer key is named File Descriptor. It is used as file identifier for all subsequent read and write operations. By default, the File Descriptors 0, 1 and 2 are associated with Standard Input, Output and Error correspondently. #include <sys/types. h> #include <sys/stat. h> #include <fcntl. h> int open (const char *path, int oflag, …/*mode_t mode*/); File exists O_CREAT and O_EXCL Flags Combination: O_. . . |O_CREAT (mode used) Yes No open error open create • Deallocates File Descriptor specified by fd. This File Descriptor could be reused by subsequent open() calls. • Returns 0 on success. Returns -1 in case of error, errno value specifies the error. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX File. A stderr stdin #include <unistd. h> int close (int fd); stdout O_. . . |O_CREAT|O_EXCL (mode used) error create • Opens file specified by path-name • Argument oflag specifies one of following open modes: O_RDONLY (=0) read-only, O_WRONLY (=1) write-only, O_RDWR (=2) read & write • The following bit flag values could be added with bitwise-OR operator (see also man): O_APPEND - all writes append data to the end of file O_TRUNC - if open for write, the file is truncated to 0 bytes length Example. Open File O_CREAT - file will be created if it does not exist yet fd=open(“File. A”, O_RDWR); O_EXCL - used with O_CREAT for exclusive creation /* As result, fd equal 3 */ • Argument mode is used with O_CREAT flag and specifies the permissions of creating file as octal constant. Process file descriptors • Returns File Descriptor (>=0) in case of success. 0 1 2 3 4 … N Returns -1 in case of error, errno value specifies the error (see man).

I/O System Calls (continuation) #include <unistd. h> ssize_t read (int fd, void *buf, size_t

I/O System Calls (continuation) #include <unistd. h> ssize_t read (int fd, void *buf, size_t nbyte); • Reads no more than nbyte-s from file associated with descriptor fd into buffer buf. • Returns non-negative number of read bytes. Returns -1 in case of error, errno value specifies the error. #include <unistd. h> ssize_t write (int fd, void *buf, size_t nbyte); • Writes nbyte-s from buffer buf to the file associated with descriptor fd. • Returns non-negative number of written bytes. Returns -1 in case of error, errno value specifies the error. #include <sys/types. h> #include <unistd. h> off_t lseek (int fd, off_t offset, int whence); • Sets the file pointer associated with the open file descriptor fd as follows: - if whence == SEEK_SET, the pointer is set to offset bytes from file beginning. - if whence == SEEK_CUR, the pointer is set to its current location plus offset. - if whence == SEEK_END, the pointer is set to the size of the file plus offset. • Returns the resulting offset calculated from file beginning. Returns -1 in case of error, errno specifies the error. Buffered Input / Output The Standard C Library provides the data type FILE representing Buffered Stream Object and set of functions providing Buffered Input / Output: fopen(), fclose(), fread(), fwrite(), fseek(), fflush(). Actually, the object of type FILE is buffered wrapper of File Descriptor, associated with corresponded file. The following utility function associates the buffered stream object of type FILE with already open File Descriptor: #include <stdio. h> FILE *fdopen (int fd, const char *mode); • Associates Buffered Stream with already open File Descriptor. • Argument mode could have the same values (“r”, “w”, “a”, “r+”, “w+”, “a+”) as for fopen() function. • Returns FILE* pointer to Buffered Stream object or NULL in case of error. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Process fd=open(“File. A”…); file descriptors stdin • The new file descriptor is set to

Process fd=open(“File. A”…); file descriptors stdin • The new file descriptor is set to remain open across exec close(1); 0 stdin functions. int dup 2(int fd 1, int fd 2); • Duplicates an open file descriptor 4 …N Process 1 stdout • The file descriptor returned is the lowest one available. dup(fd)); 2 3 File A or -1 in case of failure (errno indicates the error) 3 Process • Causes the file descriptor fd 2 to refer to the same file as fd 1. 0 1 2 3 File A • Returns a new file descriptor pointing to the same file 2 File A • Duplicates an open file descriptor 1 stdout 0 stderr /* fd equal 3 */ stderr #include <unistd. h> int dup (int fd); Example. Redirection of STDOUT to File stderr Dup System Calls and Redirection stdin close(fd)); Process 0 close(1); dup(fd); © D. Zinchin [zinchin@gmail. com] dup 2(fd, 1); Introduction to Network Programming in UNIX & LINUX stdin Example. dup 2() via dup() 1 stdout • Returns non-negative value of fd 2 in case of success or -1 in case of failure (errno indicates the error) 2 3 File A closed first. stderr • If fd 2 already refers to fd 1, or if fd 1 is not valid, fd 2 will not be stdout • If fd 2 already refers to an open file, not fd 1, it is closed first.

Inter Process and Inter-Host Communication Inter Process Communication (IPC) is sharing of information and

Inter Process and Inter-Host Communication Inter Process Communication (IPC) is sharing of information and resources by two or more different processes. • IPC includes - Data Exchange - Synchronization host Process 1 • UNIX (Linux) provides different methods of IPC: - Pipes - FIFOs - Signals - Message Queues - Shared Memory - Semaphores Process 2 Kernel host 1 host 2 Inter-Host Communication (IHC) is IPC between two or more processes, running on different hosts in the network. • UNIX (Linux) provides the following methods for IHC: - Berkeley Socket API - System V Transport Library Interface (TLI) © D. Zinchin [zinchin@gmail. com] Process 1 Kernel Introduction to Network Programming in UNIX & LINUX Process 2 Kernel

Unnamed Pipe #include <unistd. h> int pipe (int *fd); • Creates input / output

Unnamed Pipe #include <unistd. h> int pipe (int *fd); • Creates input / output mechanism called the Pipe. The Pipe is unidirectional bite stream • Through fd[0] the read descriptor is returned. Through fd[1] the write descriptor is returned. • The Pipe is usually used for communication between Parent and Child Processes. • The pipe() call returns 0 in case of success. In case of failure -1 is returned, errno indicates the error. int fd[2]; pipe(fd); childpid=fork(); Process 1 W pipe W close(fd[1]); Process 1 Process 2 Process 1 R close(fd[0]); W R R W pipe kernel © D. Zinchin [zinchin@gmail. com] W R pipe kernel Introduction to Network Programming in UNIX & LINUX kernel R

Unnamed Pipe Usage Scenarios Bidirectional flow 2 Processes write to 1 fork Process 1

Unnamed Pipe Usage Scenarios Bidirectional flow 2 Processes write to 1 fork Process 1 W Process 2 R W R Process 1 W fork Process 2 Process 3 W R pipe 2 pipe 1 kernel • Bidirectional flow needs 2 pipes • If a process writes less than Capacity of Pipe (which is at least 4096), the write operation is guaranteed to be atomic. • In Special cases: - read & no data - write & pipe is full the behavior depends on blocking / non-blocking mode (flag O_NONBLOCK or O_NDELAY) • If one end closed while second is writing, the SIGPIPE signal is sent to writing peer. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Example of Pipe Usage fork Parent-”Client” STDIN read file name 0 STDOUT Child-”Server” print

Example of Pipe Usage fork Parent-”Client” STDIN read file name 0 STDOUT Child-”Server” print file contents or error 1 W R open file F read file contents File response pipe 2 request pipe 1 kernel Child-”Server” Parent – “Client” pipe(pipe 1); pipe(pipe 2); fork(); /* close descriptors unused by client: P 1[0] - close, P 1[1] - P 1 write P 2[0] - P 2 read, P 2[1] - close */ close(pipe 1[0]); close(pipe 2[1]); /* close descriptors unused by server: P 1[0] - P 1 read, P 1[1] - close P 2[0] - close, P 2[1] - P 2 write */ close(pipe 1[1]); close(pipe 2[0]); … do Client functionality… … do Server functionality… /* close descriptors used by client */ close(pipe 1[1]); close(pipe 2[0]); exit(0); /* close descriptors used by server */ close(pipe 1[0]); close(pipe 2[1]); exit(0); © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Pipe-related Library Functions #include <stdio. h> FILE *popen(const char *command, const char *mode); int

Pipe-related Library Functions #include <stdio. h> FILE *popen(const char *command, const char *mode); int pclose(FILE *stream); • Function popen() creates a pipe between the calling program and the command to be executed. • The command argument consists of a shell command line, that is invoked as follows: execl("/usr/xpg 4/bin/sh", "-c", command, NULL); • The mode argument is either “r” for reading or “w” for writing. • Depending on mode, calling program could read from STDIN or write to STDOUT of the command. • Function pclose() closes a pipe, waits for the associated process and returns its termination status. Example: Using popen() to run “ls *. c” #include <stdlib. h> int system(const char *string) • Function system() invokes string as shell command, waits until the shell completion and return its status. #include <stdio. h> #include <stdlib. h> int main() { char *cmd = "/usr/bin/ls *. c"; char buf[BUFSIZ]; FILE *ptr; if ((ptr = popen(cmd, "r")) != NULL) { while (fgets(buf, BUFSIZ, ptr) != NULL) printf("%s", buf); pclose(ptr); } • This function could be used only if single-threaded process. • In multi-threaded process the following code could replace functionality of system(): pclose(popen(string, ”w”)); } © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

FIFO or Named Pipe #include <sys/stat. h> int mknod(const char *path, mode_t mode, dev_t

FIFO or Named Pipe #include <sys/stat. h> int mknod(const char *path, mode_t mode, dev_t dev); • The system call mknod() creates a filesystem node (file, device special file or named pipe) • The FIFO (First In, First Out) is unidirectional bite stream with persistent name. • FIFO (Named Pipe) is created by the command mknod or by system call mknod() with mode=(S_IFIFO | permissions) • Upon successful completion, mknod() returns 0. Otherwise, it returns -1, and errno is set to indicate the error. • FIFO is opened for writing / reading as regular file by its name ( open(), close(), fopen(), fclose()). • Process, opening FIFO for writing, waits (is blocking) until the second process opens FIFO for reading. • To communicate through FIFO the processes don’t have to be in Parent-Child relation. • In Special cases ( read & no data, write & pipe is full ) the behavior depends on blocking / non-blocking mode (flag O_NONBLOCK or O_NDELAY) Process 1 Process 2 W R FIFO <Fifo. Name> kernel #include <unistd. h> int unlink(const char *path); Process 1 • The unlink() function removes a link to a file. • When the file's link count becomes 0 and no process has the file open, the file is deleted mknod(Fifo. Name, S_IFIFO | perms, 0); open(Fifo. Name, O_WRONLY); Process 2 open(Fifo. Name, O_RDONLY); © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Blocking Mode and Special Conditions of I/O operations on Pipes and FIFOs Condition Normal

Blocking Mode and Special Conditions of I/O operations on Pipes and FIFOs Condition Normal (Blocking) Mode Non-Blocking Mode O_NONBLOCK • open FIFO for read • writer doesn’t exist Waits until writer opens FIFO for write Returns immediately, no error • open FIFO for write • reader doesn’t exist Waits until reader opens FIFO for read Returns immediately with error, errno=ENXIO • read from Pipe / FIFO • no data Waits until: • writer writes data (return amount of data) • or writer is closed (return 0) Returns immediately with error, errno= EAGAIN • write to Pipe / FIFIO • the Pipe / FIFO is full Waits until space is available, write the data Returns immediately with error, errno= EAGAIN • read from Pipe / FIFO • writer close its side descriptor • write to Pipe / FIFO • reader closed its side descriptor Returns immediately with value 0 Returns with error, errno=EPIPE Signal SIGPIPE is sent, its default action to terminate the process #include <unistd. h> #include <fcntl. h> int fcntl(int fildes, int cmd, /*arg*/. . . ); © D. Zinchin [zinchin@gmail. com] Flag O_NONBLOCK or O_NDELAY could be specified • during open()-ing of FIFO descriptor • with function fcntl() on already open Pipe or FIFO descriptor On some UNIX systems flag O_NDELAY specifies different behavior, than O_NONBLOCK On some UNIX systems if flag O_NDELAY is specified instead of O_NONBLOCK, the value 0 is returned. • Function fcntl() provides miscellaneous operations on file descriptors • To set non-blocking I/O mode, the following form is used: int flags = fcntl (fd, F_GETFL, 0); /* get current flags */ fcntl (fd, F_SETFL, flags | O_NONBLOCK); /* add non-blocking flag */ Introduction to Network Programming in UNIX & LINUX

Example: FIFO-based Client-Server Model Client R Server w W R W Server FIFO uses

Example: FIFO-based Client-Server Model Client R Server w W R W Server FIFO uses well-known SERVER_FIFO Name Client FIFO uses temporary Client FIFO Name kernel Client: Server: - Creates Client FIFO with unique temporary name - Creates Server FIFO with well-known name - Opens Server FIFO for write - Opens Server FIFO for read (blocking) - Opens Server FIFO for write to avoid read unblocking - Waits (blocking read) for incoming client request - Writes Request, containing Client FIFO name - Reads Client FIFO Name from Client Request - Closes Server FIFO. - Opens Client FIFO to write - Opens Client FIFO for read - Writes the Reply - Reads the Reply - Closes Client FIFO. - Closes and deletes Client FIFO. - (repeat for each client) - (On termination) Closes and deletes Server FIFO © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Iterative and Concurrent Server Iterative Server Client Concurrent Server Start-up Bind to well-known address

Iterative and Concurrent Server Iterative Server Client Concurrent Server Start-up Bind to well-known address Bind to ephemeral address Bind to well-known address Wait for Clients Connect to Server by well-known address Wait for Clients Read Client Request Send Request Sub-Server Read Client Request Connect to Client Address Wait for Reply Connect to Client Address Fork Sub-Server Send Reply to Client Read Reply Send Reply to Client Disconnect From Client yes Continue? Disconnect from Server Disconnect From Client Disconnect from ephemeral address no Disconnect from well-known address © D. Zinchin [zinchin@gmail. com] Continue? no yes release defuncts Disconnect from well-known address Introduction to Network Programming in UNIX & LINUX

Signal is software interrupt used as notification to process about some event. Process accepts

Signal is software interrupt used as notification to process about some event. Process accepts a signals: • sent by Kernel • sent by another process UNIX utility kill /usr/bin/kill -s signal_name pid. . . [-signal_name] pid. . . [-signal_number] pid. . . -l • Sends a signal to the process(es) specified by each pid operand. • The pid argument may be: >0 - PID of particular process =0 - sender’s process group -1 - all sender’s processes <-1 - process group ID = abs(pid) • With -l parameter writes all supported signal names Example: Send SIGINT to process with PID=12345 # kill -s INT 12345 # kill -2 12345 © D. Zinchin [zinchin@gmail. com] Name Value Default Event ============================== SIGHUP 1 Exit Hangup on controlling terminal SIGINT 2 Exit Interrupt (Ctrl-C) SIGQUIT 3 Core Quit – interactive termination SIGILL 4 Core Illegal Instruction SIGTRAP 5 Core Trace or Breakpoint Trap SIGABRT 6 Core Abort – abnormal termination SIGEMT 7 Core Emulation Trap SIGFPE 8 Core Arithmetic Exception SIGKILL 9 Exit Killed (can’t be changed) SIGBUS 10 Core Bus Error (undefined memory access) SIGSEGV 11 Core Segmentation Fault (invalid memory reference) SIGSYS 12 Core Bad System Call SIGPIPE 13 Exit Broken Pipe SIGALRM 14 Exit Alarm Clock (timeout signal) SIGTERM 15 Exit Terminated SIGUSR 1 16 Exit User Signal 1 SIGUSR 2 17 Exit User Signal 2 SIGCHLD 18 Ignore Child Status Changed SIGPWR 19 Ignore Power Fail or Restart SIGWINCH 20 Ignore Window Size Change SIGURG 21 Ignore Urgent Socket Condition SIGPOLL 22 Exit Pollable Event SIGSTOP 23 Stopped (can’t be changed) SIGTSTP 24 Stopped (user) (Ctrl-Z) SIGCONT 25 Ignore Continued (command fg) SIGTTIN 26 Stopped (tty input) SIGTTOU 27 Stopped (tty output) SIGVTALRM 28 Exit Virtual Timer Expired SIGPROF 29 Exit Profiling Timer Expired SIGXCPU 30 Core CPU time limit exceeded SIGXFSZ 31 Core File size limit exceeded Introduction to Network Programming in UNIX & LINUX

Sending and Handling the Signal #include <sys/types. h> #include <signal. h> int raise(int sig);

Sending and Handling the Signal #include <sys/types. h> #include <signal. h> int raise(int sig); • Sends the signal sig to executing program. • Is equivalent to kill(getpid(), sig) int kill(pid_t pid, int sig); • System call kill() sends a signal to a process or a group of processes. • The sender and receiver must have the same effective user ID or sender must be superuser. • Sending sig = 0 used to check process existence. • The pid argument could have the following values: >0 - PID of particular process =0 - sender’s process group -1 - all sender’s processes <-1 - process group ID = abs(pid) • The kill() call returns 0 in case of success. In case of failure -1 is returned, errno indicates the error. #include <signal. h> #include <unistd. h> void (*signal (int sig, void (*sig_handler)(int) ) ) (int); int pause(void); this is equivalent of typedef void( *sig_handler) (int sig); sig_handler signal(int sig, sig_handler); • System call pause() suspends the calling process until it receives a signal. • If process is not terminated by signal, returns value -1 and sets errno=EINTR • System call signal() modifies signal disposition. • The sig specifies any signal, excepting SIGKILL and SIGSTOP. • The sig_handler argument specifies the signal's disposition, which may be: - SIG_DFL - to provide default signal handling - SIG_IGN - to ignore the signal - user-defined handler name - to provide specific signal handling with specific handler • The signal() call returns the previous handler (disposition) of the signal. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Example 1. Suspend / Resume application output with SIGINT signal (sent with Ctrl-C). Signal

Example 1. Suspend / Resume application output with SIGINT signal (sent with Ctrl-C). Signal Handling Examples #include <signal. h> int flag=0; /*--- Signal SIGINT handler--- */ void sigreact (int sig) { /* OS-depended re-subscription*/ /* signal(SIGINT, sigreact); */ flag = ! flag; } … /*--- Prints application data ---*/ void print. Info(…) { … /* subscribe to signal */ signal (SIGINT, sigreact); while(…/*has more data */) { while(flag) { /* wait for signal */ pause ( ); } printf(…/*print the data*/); } /* unsubscribe from signal */ signal (SIGINT, SIG_DFL); … } © D. Zinchin [zinchin@gmail. com] #include <unistd. h> unsigned int alarm (unsigned int sec); • Causes the system to generate a SIGALRM signal for the process after the number of sec real-time seconds. • If sec = 0, a pending alarm request, if any, is cancelled. • Alarm requests are not stacked. Each alarm() call reschedules the alarm time. • The fork() call clears pending alarms in the child process. Example 2. Using alarm() System Call to provide operation timeout. #include <stdio. h> #include <unistd. h> #include <signal. h> char user. Name[MAXBUFF]; /* --- Alarm signal handler. --- */ void catch_alarm(int sig_num) { perror("Operation timed out. "); exit(0); } … /* --- Reads user name --- */ int main(int argc, char* argv[]) { /* subscribe for ALRM signals */ signal(SIGALRM, catch_alarm); /* prompt the user for input */ printf("Username: "); fflush(stdout); /* start a 30 seconds alarm */ alarm(30); /* wait for user input */ fgets(user. Name, MAXBUFF, stdin); /* remove the timer*/ alarm(0); printf("User name: '%s'n", user. Name); return 0; } Introduction to Network Programming in UNIX & LINUX

Race Condition or Race Hazard This is a defect of Process functionality, when behavior

Race Condition or Race Hazard This is a defect of Process functionality, when behavior of the process is unexpectedly and critically depends on the sequence or timing of occurring events. Most common scenario: two events (or signals) are racing each other to affect the Process. Race Condition Example Two “racing” events are: • Flag testing by procedure • Flag setting by signal handler If signal arrives here, flag will be changed to 0, pause() call will not be performed, process execution will continue If signal arrives here, flag will be changed to 0, pause() call will be interrupted, and process execution will continue void sigreact (int sig) { flag = ! flag; } void some. Procedure() {… flag = 1; /* subscribe to signal */ signal (SIGINT, sigreact); … … /* wait for signal */ while(flag) { pause ( ); } /* continue execution */ … If signal arrives after check of flag will be changed to 0, but pause() call will be performed, process will be “stuck” Critical Region This is the region of code, where occuring of event (or signal) causes Race Condition. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX This is Critical Region

Avoiding of Race Condition There are two ways to avoid Race Condition : 1)

Avoiding of Race Condition There are two ways to avoid Race Condition : 1) To perform the Polling - repeatable check of the resource state with limited time period In previous example this means to replace system call pause( ) with call to function sleep(int seconds). Advantage: This is the simplest way to avoid process forever “sticking”. Disadvantage: In certain condition, process will be able to recognize state update only after delay. In previous example, if signal will occur after flag check, but before sleep() call, the process will be able to recognize flag update only after expiration of next sleep timeout 2) To provide Blocking of event (signal) delivery in the Critical Region. In previous example this means to prohibit delivery of specific signal SIGINT between flag check and system call pause( ). Advantage: The event (signal) delivery delay will be minimized. Event (signal) will be delivered as soon as Critical Region will be passed. Disadvantage: Requires more complicated blocking mechanism to be provided by Kernel Note: In most the cases the Race Condition occurs between test and set operations. To avoid “racing” of test and set, the Kernel provides number of tools (system calls), performing test-and-set as one impartible operation. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Signal Blocking is temporary prevention of signal delivery. Each process has Process Signal Mask,

Signal Blocking is temporary prevention of signal delivery. Each process has Process Signal Mask, specifying the set of signals, which currently blocked by the process. Blocked signals (unlike ignored signals) do not get lost. If generated signal is specified as blocked, the process holds the occurrence of this signal in Pending Signal Mask until signal is unblocked. After unblocking, the pending signal is delivered (the specified action is performed). Signal Delivery Diagram Start the Process (init Signal Mask) process flow signal blocking Block the Signal (add to Signal Mask) Unblock the Signal (remove from Signal Mask) Process Flow no signal unblocking Event occurre d Is Signal pending ? yes Remove occurrence from Pending Signal Mask Signal Generation Add occurrence to Pending Signal Mask yes Is Signal blocked ? no Discard the Signal © D. Zinchin [zinchin@gmail. com] yes Is Signal ignored ? no Introduction to Network Programming in UNIX & LINUX Deliver the Signal (take proper action)

POSIX Signal Set Utilities POSIX Signal Set To provide system calls on set of

POSIX Signal Set Utilities POSIX Signal Set To provide system calls on set of signals, POSIX standard provides data type sigset_t, representing the Signal Set. This type contains the set of bit flags. Each bit flag corresponds to specific signal. For manipulation with POSIX Signal Set the following service functions are used : #include <signal. h> int sigemptyset(sigset_t *set); - removes all signals from the Signal Set int sigfillset(sigset_t *set); - fills the Signal Set with all the signals int sigaddset(sigset_t *set, int signum); - adds the specified signal to the Signal Set int sigdelset(sigset_t *set, int signum); - deletes the specified signal from the Signal Set int sigismember(const sigset_t *set, int signum); - checks if a specified signal is in the Signal Set • All these functions return 0 on success, -1 on error. • The sigismember() function returns 1(=true) / 0(=false) / -1 (error). © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

POSIX System Calls for Signal Blocking #include <signal. h> int sigprocmask (int how, const

POSIX System Calls for Signal Blocking #include <signal. h> int sigprocmask (int how, const sigset_t *set, sigset_t *oldset); • System call sigprocmask() is used to change / examine the Signal Mask (set of blocked signals) of the calling process • Parameter how may have the following values -SIG_BLOCK - the Signal Set, specified by set, is added to process Signal Mask -SIG_UNBLOCK - the Signal Set, specified by set, is removed from process Signal Mask -SIG_SETMASK - the Signal Set, specified by set, replaces the process Signal Mask • If oldset is not NULL, the old Signal Mask value is returned • If set is NULL, the process Signal Mask is not modified. • If any pending signals became unblocked during the call, at least one of those signals will be delivered before the call to sigprocmask() returns. • Attempts to block signals SIGKILL, SIGTOP are silently ignored. • Return values: 0 -success, -1 failure (errno will specify the error). #include <signal. h> int sigsuspend (const sigset_t *set); • System call sigsuspend() performs the following operations: - temporary replaces Signal Mask; - suspends the calling process until it receives a signal; - restores the Signal Mask , if process is not terminated by signal. • If process is not terminated, returns -1 and sets errno = EINTR #include <signal. h> int sigpending (sigset_t *set); • System call sigpending() function retrieves those signals that have been sent to the process but are being blocked from delivery by the calling process's signal mask. • The result stored in set argument. • Returns 0 on success, -1 on failure. Commonly the sigprocmask() call is used to block Signal delivery in Critical Region. The sigsuspend() call is used to wait for the signal out of Critical Region. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Race Condition Resolution Example If signal arrives here, flag will be changed to 0,

Race Condition Resolution Example If signal arrives here, flag will be changed to 0, then sigsuspend () will not be called, process execution will continue void sigreact (int sig) { flag = ! flag; } void some. Procedure() {… sigset_t block. Mask, unblock. Mask; /* prepare masks */ sigemptyset ( &block. Mask); sigaddset ( &block. Mask, SIGINT); sigprocmask ( SIG_SETMASK, NULL, &unblock. Mask); sigdelset ( &unblock. Mask, SIGINT); flag = 1; /* subscribe to signal */ signal (SIGINT, sigreact); … /* block the signal */ sigprocmask (SIG_BLOCK, &block. Mask, NULL); … /* wait for signal */ while(flag) { sigsuspend ( &unblock. Mask); } /* continue execution */ Here signal is blocked. If it will arrive, Pending Signal Mask Will be updated, but signal will not be delivered. … © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX Critical Region Is covered. Here the signal (pending or generated) is safely handled

POSIX Advanced Signal Handling #include <signal. h> int sigaction(int signum, const struct sigaction *act,

POSIX Advanced Signal Handling #include <signal. h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); where: struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; }; • The sigaction() system call provides POSIX advanced (relatively to signal() ) interface, used to change the action taken by a process on receipt of a specific signal. • Parameter signum specifies the signal number. Parameters act and oldact specify the pointers to the structures, describing old and new action to be taken on specified signal delivery. • The structure sigaction specifies the following fields: sa_handler - the signal handling function sa_mask - a mask of signals which should be blocked during execution of the signal handler. (t he signal which triggered the handler will also be blocked, unless the SA_NODEFER flag is used) sa_flags - specifies a set of flags used to modify the delivery of the signal (flags are combined with logical OR) Example: Signal handling with sigaction() Some of sa_flags values: void handler(int sig) SA_RESETHAND - Restore the signal action to {…} the default state once the signal … handler has been called. struct sigaction new. Action; SA_RESTART - Provide restart of “slow” system new. Action. sa_handler=handler; /* set the handler*/ calls, interrupted by signal. sigfillset(&new. Action. sa_mask); /* block other SA_NODEFER - Do not prevent the signal from signals during being received from within the handling */ its own signal handler. new. Action. sa_flags = 0; /* no flags */ • Returns 0 – success, if (sigaction(SIGUSR 1, &new. Action, NULL) < 0) -1 – failure (errno specifies the error). {…} © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

System V IPC provides three methods: -Shared Memory Segments -Message queues -Semaphores All these

System V IPC provides three methods: -Shared Memory Segments -Message queues -Semaphores All these IPC methods have similar access interface: persistent integer KEY name and integer system ID. Method Name Space (name given by user) Identification (ID given by Kernel) Unnamed Pipe No Name File Descriptor FIFO Pathname File Descriptor System V IPC KEY , 32 -bit integer ID, integer How to build the KEY for System V IPC object ipcs #include <sys/ipc. h> typedef int key_t; key_t ftok(const char *path, int prm_char); Report IPC facilities status • The ftok() function returns a key based on path and additional prm_char (only 8 bits used) • Returns the same values for the different path names of the same existing file and same prm_char • Returns -1 if path does not exist or is not accessible #include <sys/ipc. h> #define IPC_PRIVATE (key_t)0 /* private key */ • May be used by process as ephemeral KEY Predefined (well-known) value Ephemeral (IPC_PRIVATE) value Path ftok Prm_char © D. Zinchin [zinchin@gmail. com] UNIX IPC-related Utilities KEY ipcrm [-m id] [-q id] [-s id] [-M key] [-Q key] [-S key] Removes IPC object by ID or by KEY. #ipcs IPC status from <running system> as of Thu Feb 1 12: 11: 56 IST 2007 T ID KEY MODE OWNER GROUP Message Queues: q 0 0 xd 9019340 --ra-r--r-root q 1 0 xd 9719343 --ra-r--r-root q 2 0 xd 9089388 --ra-r--r-root Shared Memory: m 0 0 x 1 e 34 --rw-rw-rwroot other m 257 0 x 79 ebc 2 ac --rw-r----oracle dba m 258 0 x 17220 c --rw-r----oracle dba m 259 0 x 4 d 0190 d 2 --rw-rw-rwtemip_us m 260 0 x 300192 ed --rw-rw-rwtemip_us m 261 0 x 300192 d 0 --rw-rw-rwtemip_us Semaphores: s 0 0 xf 9019340 --ra-r--r-root s 65539 0 xf 9019349 --ra-r--r-root s 65540 0 xf 901934 a --ra-r--r-root Introduction to Network Programming in UNIX & LINUX

System V Shared Memory is the fastest form of IPC available. Once the Shared

System V Shared Memory is the fastest form of IPC available. Once the Shared Memory region is mapped into the address space of the processes, no kernel involvement occurs in passing data between the processes. Data copied 4 times Client memory W write() Data copied 2 times W R read() Output File write() pipe Server Shared Memory Client R R W read() Input File write() read() Input File Output File kernel Server kernel Note: Access to Shared Memory from different processes must be synchronized © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Access and Control Shared Memory flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/shm.

Access and Control Shared Memory flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/shm. h> 0400 Read by owner 0200 Write by owner int shmget(key_t key, size_t size, int flags); 0040 Read by group Access • Creates new Shared Memory segment with specified size if: - key = IPC_PRIVATE or - key does not refer to existing segment and IPC_CREAT flag specified • If segment was created or already exist, returns segment ID. • Returns -1 in case of error, errno specifies the error. 0020 Write by group Permissions 0004 Read by others 0002 Write by others IPC_CREAT Create if does not exist IPC_EXCL In combination with IPC_CREAT – return an error, if segment already exists Flags Combination: #include <sys/types. h> #include <sys/ipc. h> #include <sys/shm. h> Yes No perms open error perms | IPC_CREAT open create perms | IPC_CREAT | IPC_EXCL error create struct shmid_ds {…/* see man */…}; int shmctl(int shmid, int cmd, struct shmid_ds *buf); • Performs control operations on Shared Memory. • The cmd may be: IPC_STAT, IPC_SET, IPC_RMID, IPC_LOCK, IPC_UNLOCK • To remove Shared Memory segment, used in following form: shmctl(shmid, IPC_RMID, NULL); • Returns 0 in success, -1 in case of error (errno specifies the error) © D. Zinchin [zinchin@gmail. com] Segment exists Introduction to Network Programming in UNIX & LINUX

Attach / Detach Shared Memory #include <sys/types. h> #include <sys/shm. h> void *shmat(int shmid,

Attach / Detach Shared Memory #include <sys/types. h> #include <sys/shm. h> void *shmat(int shmid, const void *addr, int flags); • Attaches Shared Memory segment, specified by ID, to the Data Segment of the calling process: addr = NULL – attached to address selected by system addr != NULL – attached to specified address (see flags) • Specific flags value: SHM_RDONLY – read only access • Returns: the segment starting address on success, -1 in case of failure (errno specifies the error) #include <sys/types. h> #include <sys/shm. h> int shmdt(const void *shmaddr); • Detaches Shared Memory segment from the calling process. • Returns 0 (success) / -1 (failure) © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Shared Memory Example /* -- server main -- */ include “common. h” main() {

Shared Memory Example /* -- server main -- */ include “common. h” main() { int shmid; key_t key; char *shm; /* Create the segment. */ shmid = shmget(THE_KEY, THE_SIZE, PERMS | IPC_CREAT | IPC_EXCL); if (shmid < 0) { … /*handle error*/ } /*-- common. h --*/ #include <sys/types. h> #include <sys/ipc. h> #include <sys/shm. h> #define THE_KEY 5678 #define THE_SIZE 100 #define PERMS 0666 /*-- client main -- */ #include “common. h” main() { int shmid; key_t key; char *shm, buf [THE_SIZE]; /* Get the segment ID */ shmid = shmget(THE_KEY, THE_SIZE, PERMS); if (shmid < 0) { …/*handle error*/ } /* Attach the segment to client data space. */ shm = shmat(shmid, NULL, 0); if (shm == (char *) -1) { …/*handle error*/ } /* Attach the segment to server data space. */ shm = shmat(shmid, NULL, 0); if (shm == (char *) -1) { … /*handle error */ } /* Put some data to be read by another process. For example: */ strcpy(shm, “Any Data”); /* Notify the client and wait until read finish */. . . } /* Wait notification from server to begin read */ … /* Read the data from server. For example: */ strcpy(buf, shm); /* Notify the server about read finish */ … /* Detach the segment */ if (shmdt(shm) == -1){ …/*handle error*/ } /* Remove the segment */ if (shmctl(shmid, IPC_RMID, NULL) == -1 ){ …/*hanlde error*/ } } © D. Zinchin [zinchin@gmail. com] /* Detach the segment */ if (shmdt(shm) == -1){ …/*handle error*/ } Introduction to Network Programming in UNIX & LINUX

Example: Shared Memory - based Client-Server Model with Signal Synchronization Client Server PID Client

Example: Shared Memory - based Client-Server Model with Signal Synchronization Client Server PID Client PID 4 bytes Data Length 4 bytes Data Buffer Server N bytes Shared Memory Client: Server: Gets Shared Memory ID by well-known KET_T name - Creates Shared Memory with well-known KEY_T name - Attaches Shared Memory - Reads Server PID from “Server PID” field” - Writes own PID to “Server PID” field - Subscribes for signal notification from Server - Subscribes for signal notification from Client - Checks if “Client PID” is 0 and writes there own PID - Writes 0 to “Client PID” field - Writes null-terminated File Name to “Data Buffer” - Waits for signal from new Client field and its length to “Data Length” field - Sends notification signal to Server by PID - Reads from “Client PID”, “Data Length”, “Data Buffer” - Waits for signal from Server - Opens requested File for read - Writes data portion to “Data Buffer”, fills “Data Length” - Reads reply portion from “Data Buffer” - Sends notification signal to Client by PID - Sends notification signal to Server by PID - Waits for signal from Client - (repeats until empty portion was read) - (repeats until empty portion is written) - Detaches Shared Memory - (repeats for each client) - Detaches Shared Memory - Removes Shared Memory © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

System V Message Queue is persistent queue of messages stored in Kernel. As any

System V Message Queue is persistent queue of messages stored in Kernel. As any other System V IPC objects, Message Queue is identified by KEY_T name and ID. Processes read and write messages to arbitrary queues. Sender process can write the message and exit. Receiver process can read the message later. Every message in queue has the following attributes: • Type – long integer type value • Length – the length of data portion of the message ( >=0 ) • Data – any data ( if Length > 0 ) The Message Queue could be considered as linked list of messages. For each Message Queue the Kernel maintains the structure msqid_ds, holding the current state of this Queue. kernel struct msqid_ds msqid msg_perm structure link NULL type = 100 type = 200 type = 300 length = 1 length = 2 length = 3 msg_first data msg_last data . . . data msg_ctime © D. Zinchin [zinchin@gmail. com] - time of last change Introduction to Network Programming in UNIX & LINUX

Message Queue Access and Control flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg.

Message Queue Access and Control flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h> 0400 Read by owner 0200 Write by owner int msgget(key_t key, int flags); 0040 Read by group Access • Creates new Message Queue with specified size if: - key = IPC_PRIVATE or - key does not refer to existing segment and IPC_CREAT flag specified • If queue was created or already exist, returns queue ID. • Returns -1 in case of error, errno specifies the error. 0020 Write by group Permissions 0004 Read by others 0002 Write by others IPC_CREAT Create if does not exist IPC_EXCL In combination with IPC_CREAT – return an error, if queue already exists Flags Combination: #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h> Yes No perms open error perms | IPC_CREAT open create perms | IPC_CREAT | IPC_EXCL error create struct msqid_ds {…/* see man */…}; int msgctl(int msqid, int cmd, struct msqid_ds *buf); • Performs control operations on Message Queue. • The cmd may be: IPC_STAT, IPC_SET, IPC_RMID • To remove Message Queue, used in following form: msgctl(msqid, IPC_RMID, NULL); • Returns 0 in success, -1 in case of error (errno specifies the error) © D. Zinchin [zinchin@gmail. com] Queue exists Introduction to Network Programming in UNIX & LINUX

Send Message to Message Queue #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h>

Send Message to Message Queue #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h> struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data of any type and size, may be empty */ }; int msgsnd(int msqid, struct msgbuf *ptr, int length, int flag); • Sends message to Queue with specified ID. • Parameter msqid specifies ID of existing Message Queue • Parameter ptr must point to any structure with positive long first field. • Parameter length specifies real size of message data in pointed structure (excluding 1 st field) • Parameter flag could be 0 or IPC_NOWAIT. • Without IPC_NOWAIT flag, the system call blocks, when Message Queue has no room for the new message (occurs if too many messages in Queue or system-wide) • Returns 0 on success, -1 on error (errno specifies the error) © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Receive Message from Message Queue #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h>

Receive Message from Message Queue #include <sys/types. h> #include <sys/ipc. h> #include <sys/msg. h> int msgrcv(int msqid, struct msgbuf *ptr, int length, long msgtype, int flags); • Receives message from Queue with specified ID. • Parameter msqid specifies ID of existing Message Queue • Parameter ptr points to buffer for message storage. • Parameter length specifies maximal length of message data to be received. • Parameter msgtype: 0 - receive message of any type >0 - receive message of specific msgtype <0 - receive message with lowest type that <=abs(msgtype) • Parameter flag could have bits: MSG_NOERROR - no error if message data exceeds specified length (data is truncated) IPC_NOWAIT - non-blocking mode • If IPC_NOWAIT flag is not specified, the system call blocks if Message of specified msgtype does not exist System call unblocks in following cases: - message appeared in Queue - Queue removed - signal interrupt • Returns number of bytes stored into the buffer ptr, -1 on error (errno specifies the error) © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Message Queue usage Scenarios in Client – Server Model 1) Server and Client use

Message Queue usage Scenarios in Client – Server Model 1) Server and Client use separate Message Queues 2) Server and Client use the same Message Queue with different Message types 3) Multiplexing of messages for Server and number of Clients, using different 4) Message types for different Clients in the same Queue Example: Message Multiplexing for 1 Server and 3 Clients client 1 pid = 123 type = 1 client 2 pid = 456 type = 123 client 3 pid = 789 type = 1 type = 789 type = 456 message queue type = 123 or 456 or 789 server 4) Prioritizing of Messages by Message type (use msgrcv() with msgtype <0) © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

What is Semaphores are synchronization construct. Designed by E. W. Dijkstra in the late

What is Semaphores are synchronization construct. Designed by E. W. Dijkstra in the late 1960 s. Original model : Railroad with single track section, guarded by semaphore. Before entering the track train must wait for permitted (unlocked) state of semaphore. The semaphore changes its state when train enters the track (locked) and when the train leaves the track (unlocked). In programming: Semaphore used to provide resource access synchronization between different processes. Simplified Model Legend: semaphore=0 – resource locked, semaphore=1 – resource unlocked. Problems: 1) Critical region between Testing of “semaphore” value and its Setting (decrement) leads to Race Condition. /* wait for resource to be unlocked*/ while(semaphore<1) ; /* lock resource */ semaphore--; … /* unlock resource */ semaphore++; 2) Waiting process continues to use CPU System V Semaphore is not a single value, but set of nonnegative integer counters. Kernel provides group of operations on semaphore, executed in “atomic” mode. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Semaphore Access flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> 0400 Read

Semaphore Access flags: #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> 0400 Read by owner 0200 Write by owner int semget(key_t key, int n. Sems, int flags); 0040 Read by group Access • Creates new Semaphore set with n. Sems semaphores if: - key = IPC_PRIVATE or - key does not refer to existing Semaphore set and IPC_CREAT flag specified • If Semaphore set was created or already exist, returns its ID. • Returns -1 in case of error, errno specifies the error. 0020 Write by group Permissions 0004 Read by others 0002 Write by others IPC_CREAT Create if does not exist IPC_EXCL In combination with IPC_CREAT – return an error, if set already exists Flags Combination: #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> Yes No perms open error perms | IPC_CREAT open create perms | IPC_CREAT | IPC_EXCL error create int semctl(int semid, int sem. Num, int cmd, …); • Performs control operations on specified element in Semaphore set. • To remove Semaphore set, used in following form: semctl(msqid, 0, IPC_RMID); • Returns 0 in success, -1 in case of error (errno specifies the error) • (Other possible operations will be described later) © D. Zinchin [zinchin@gmail. com] Queue exists Introduction to Network Programming in UNIX & LINUX

Semaphore Operations #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> struct sembuf {

Semaphore Operations #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> struct sembuf { ushort_t sem_num; /* semaphore number, beginning from 0 */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ }; int semop(int semid, struct sembuf * p. Ops, size_t n. Ops); • Performs set of operations on Semaphore set with specified ID. • Parameter p. Ops is array of structures, one structure per operation. • Parameter n. Ops is array length. • Operation type is specified by sem_op field and has the following meaning: sem_op > 0 : Semaphore[sem_num] += sem_op; sem_op = 0 : wait while (Semaphore[sem_num] != 0); sem_op < 0 : wait while (Semaphore[sem_num] < abs(sem_op)); Semaphore[sem_num] += sem_op; • Field sem_flg could contain the following bit flags: IPC_NOWAIT – operation to be performed in non-blocking mode SEM_UNDO – individual operation in the array to be rolled back when the process exits • All set of operations, specified by array, is performed by Kernel as one impartible “atomic” operation. The execution rule is “all or nothing”: if one of operation fails, all other operations in array are not performed. • The system call semop() blocks (unless the IPC_NOWAIT flag is set), and remains blocked until: - the semaphore operations can all finish, so the call succeeds, - the process receives a signal, or - the semaphore set is removed. • Returns 0 on success, -1 on error (errno specifies the error). © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Example 1: Binary Semaphore as Signal Emulation Device The Scenario: • We need stateful

Example 1: Binary Semaphore as Signal Emulation Device The Scenario: • We need stateful device with operations: non-blocking “send signal”, and impartible blocking “wait for signal”. • To avoid race condition (signal loss), the initial state of the device would be “waiting for signal”. The Solution: • “Waiting” state will correspond to 0 value of semaphore, “signal sending” will correspond to ++ operation. /*--- COMMON ---*/ #define THE_SEM_KEY 1357 #define SEM_CLIENT 0 #define SEM_SERVER 1 … int sem. Sig. Send(int sem. ID, int sem. N) /* Semaphore[sem. N]+=1 */ { struct sembuf p. Ops[1]; p. Ops[0]. sem_num = sem. N; p. Ops[0]. sem_op = 1; p. Ops[0]. sem_flg = 0; return semop(sem. ID, p. Ops, 1); } Note: int sem. Sig. Wait(int sem. ID, int sem. N) /* wait while (Semaphore[sem. N] < 1); */ { /* Semaphore[sem. N] --; // reset to 0 */ struct sembuf p. Ops[1]; p. Ops[0]. sem_num = sem. N; p. Ops[0]. sem_op = -1; p. Ops[0]. sem_flg = 0; return semop(sem. ID, p. Ops, 1); } #include “time. h” struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /*nanoseconds*/ }; /*-- SERVER ---*/ … int sem. ID; sem. ID=semget(THE_SEM_KEY, 2, 0666|IPC_CREAT); /* initially Semaphore[i] = 0 */ sem. Sig. Wait(sem. ID, SEM_SERVER); … sem. Sig. Send(sem. ID, SEM_CLIENT); © D. Zinchin [zinchin@gmail. com] “Eternal” waiting for never arrived “signal” could be avoided by replacing semop() system call with: int semtimedop( int semid, struct sembuf * p. Ops, size_t n. Ops, struct timespec *timeout); where: /*--- CLIENT ---*/ … int sem. ID; sem. ID=semget(THE_SEM_KEY, 0, 0666); … sem. Sig. Send(sem. ID, SEM_SERVER); sem. Sig. Wait(sem. ID, SEM_CLIENT); Introduction to Network Programming in UNIX & LINUX

Example 2: Binary Semaphore as Resource Locking Device The Scenario: • We need device

Example 2: Binary Semaphore as Resource Locking Device The Scenario: • We need device with 2 states (locked / unlocked) and operations “lock” and “unlock”. • Operation “lock” would be impartible, working in blocking mode, if device already “lock”- ed by another process. • Initial state of device to be “unlocked” to avoid deadlocking forever in case of initialization failure. The Solution: “Unlocked” state will correspond to 0 value of semaphore, “locked” state will correspond to value 1. int sem. Lock(int sem. ID, int sem. N) /* wait while (Semaphore[sem. N] !=0 ); */ { /* Semaphore[sem. N]++; // i. e. = 1 */ struct sembuf p. Ops[2]; p. Ops[0]. sem_num = sem. N; p. Ops[0]. sem_op = 0; p. Ops[0]. sem_flg = 0; /* IPC_NOWAIT to be used for non-blocking mode*/ p. Ops[1]. sem_num = sem. N; p. Ops[1]. sem_op = 1; p. Ops[1]. sem_flg = SEM_UNDO; /* roll-back the operation on process exit */ return semop(sem. ID, p. Ops, 2); } int sem. Unlock(int sem. ID, int sem. N) /* Semaphore[sem. N] --; // i. e. = 0 */ { /* would not require blocking */ struct sembuf p. Ops[1]; p. Ops[0]. sem_num = sem. N; p. Ops[0]. sem_op = -1; p. Ops[0]. sem_flg = IPC_NOWAIT|SEM_UNDO; /* no block + roll-back on exit */ return semop(sem. ID, p. Ops, 1); } © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX Note: Locking “forever” by unexpectedly terminated process is avoided by usage of SEM_UNDO flag

Control Operations on Semaphore Set #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h>

Control Operations on Semaphore Set #include <sys/types. h> #include <sys/ipc. h> #include <sys/sem. h> kernel struct semid_ds semid union semun { int val; /* for SETVAL */ struct semid_ds *buf; /* for IPC_STAT, IPC_SET */ unsigned short *array; /* for GETALL, SETALL */ }; int semctl (int semid, int sem. Num, int cmd, … /*union semun arg*/); sem_perm structure sem_base sem_nsems time of last semop - sem_otime of last change - sem_ctime struct semval - semaphore value sempid - pid of last operation semncnt - # awaiting value > x semzcnt - # awaiting value = 0 • Performs control operations on Semaphore set • Parameter cmd could have the following values: IPC_STAT - copy Semaphore set info to arg. buf IPC_SET - update Semaphore set permissions and ownership from arg. buf GETALL, SETALL - get / set semaphore semval values using arg. array (on set adjustment roll-back values are cleared in all processes, blocked processes are unblocked) SETVAL - set semval value of semaphore number sem. Num to be equal val. (adjustment roll-back value is cleared in all processes, blocked processes are unblocked) GETVAL, GETPID, GETNCNT, GETZCNT - return corresponded values for semaphore number sem. Num IPC_RMID - remove Semaphore set (unblocking any blocked processes) • Returns actual value for getter methods, 0 on success for others, -1 on error (errno specifies the error) Example: Unlock method implementation int sem. Unlock(int sem. ID, int sem. N) { union semun {int val; struct semid_ds *buff; ushort *array; } arg; arg. val=0; return semctl(sem. ID, sem. N, SETVAL, arg); } © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

Example: Shared Memory - based Client-Server Model with Semaphore Synchronization Client Data Length Server

Example: Shared Memory - based Client-Server Model with Semaphore Synchronization Client Data Length Server Data Buffer Shared Memory Work with Server: sem. Sig. Send(); Work with Client: sem. Sig. Send(); kernel Start: sem. Lock(); Stop: sem. Unlock(); Work with Client: sem. Sig. Wait(); Work with Server: sem. Sig. Wait(); SEM_CLI_LOCK SEM_CLI_SIG SEM_SER_SIG Start: sem. Try. Lock(); Stop: sem. Unlock(); SEM_SER_LOCK Semaphore Set © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX 46

Safe Usage of Semaphore The Problem: After unexpected termination (for example, with SIGKILL) of

Safe Usage of Semaphore The Problem: After unexpected termination (for example, with SIGKILL) of process, responsible for Semaphore set deletion, this unused set could remain until system reboot. The Proposed Solution: One of possible solutions (See [1]) is to provide user-defined locking utilities, maintaining the Semaphore set of 3 semaphores instead of single semaphore, as follows: • The First semaphore is used for lock/unlock operations (as in previous examples) • The Second semaphore is initialized to sufficiently big value and then used as counter of processes, currently using the Semaphore set. For this purpose user-defiled methods open/close provide decrementing and incrementing of this counter correspondently. • Knowing the number of processes, which use the Semaphore set, this set may be deleted by the last process, closing it. • The Third semaphore is used to avoid race condition during Semaphore set (re)creation / closing. Note: The problem with unexpectedly terminated last process, responsible to delete Semaphore set, still exists. But, with implementation described above, it could occur with significantly lower probability. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX

POSIX Semaphores POSIX provides it’s own standard of Semaphore as Inter-Process or Inter-Thread synchronization

POSIX Semaphores POSIX provides it’s own standard of Semaphore as Inter-Process or Inter-Thread synchronization device. POSIX Semaphore is single non-negative integer counter with only 2 possible operations: • Post – Semaphore+=1 (analog of System 5 sem_op=1) • Wait – wait while (Semaphore == 0); Semaphore -=1; (analog of System 5 sem_op=-1) POSIX defines 2 forms of Semaphores: Named and Unnamed. Named Semaphore – identified by pathname, visible by multiple processes #include <semaphore. h> sem_t *sem_open(const char *name, int oflag, …/*mode_t permissions, unsigned int value*/); int sem_close(sem_t *sem); • Creates new or opens already existing Named Semaphore, identified by Full Path name , beginning from “/”. • Parameter oflag could be combined from binary flags O_CREAT, O_EXCL (see call open() ). • For newly-created Semaphore octal permissions and initial value are required. • Returns pointer to Semaphore on success, SEM_FAILED constant on failure (errno specifies error). • Closes the Named Semaphore. • Returns 0 on success, -1 on failure. int sem_unlink(const char *name); • Removes Named Semaphore. The name is removed immediatelly. Semaphore object is destroyed when sem_close()-d by all processes. • Returns 0 on success, -1 on failure. Unnamed Semaphore – memory-based semaphore allocated in memory shared by multiple processes or threads. int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_destroy(sem_t *sem); • Initializes new Unnamed Semaphore in address sem with initial value • Destroys the Unnamed Semaphore for Inter-Process (pshared =1, sem points to shared memory) or pointed by sem. for Inter-Thread (pshared =0) synchronization. • Returns 0 on success, -1 on failure. Semaphore Operations: Post and Wait int sem_post(sem_t *sem); int sem_wait(sem_t *sem); • Increments (unlocks) the Semaphore sem. • Returns 0 on success, -1 on failure. © D. Zinchin [zinchin@gmail. com] • Waits until Value of Semaphore sem becomes positive, then decrements (locks) the semaphore. • Returns 0 on success, -1 on failure. Introduction to Network Programming in UNIX & LINUX

BSD Shared Memory #include <unistd. h> #include <sys/mman. h> void *mmap(void *start, size_t length,

BSD Shared Memory #include <unistd. h> #include <sys/mman. h> void *mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset); • Maps a file of specified length, pointed by open descriptor fd, into the current process memory, pointed by start (may be NULL), beginning from specified offset. The memory divided into pages (4 K). Each page loaded from file once, when was accessed at first time. • Parameter prot specifies the permissions (protection mode - what could be done with segment), and could have the following values: PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE • Parameter flags describes the memory sharing modes and visibility: MAP_FIXED, MAP_SHARED, MAP_PRIVATE, MAP_ANONYMOUS When map is shared, it could be attached by multiple processes in the same time. • Returns pointer to mapped memory or -1 in case of problem (errno specifies the error) int msync(const void *start, size_t length, int flags); • Synchronizes memory with source file. When shared segment is modified, the changes do not appear in the file until msync() is called. Example of mmap() usage: int fd; int mprotect(const void *addr, size_t len, int prot); char* p. Buff; fd = open("/dev/zero", O_RDWR)); • Modifies the protection mode of the shared memory p. Buff = mmap(0, len, PROT_READ|PROT_WRITE, int munmap(void *start, size_t length); MAP_SHARED, fd, 0); (void) close(fd); • Unmaps the attached segment. © D. Zinchin [zinchin@gmail. com] Introduction to Network Programming in UNIX & LINUX