UNIX_IPC

download UNIX_IPC

of 122

Transcript of UNIX_IPC

  • 8/3/2019 UNIX_IPC

    1/122

    Interprocess

    Communication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    An Introduction toConcurrent Programming

  • 8/3/2019 UNIX_IPC

    2/122

    2

    Contents

    Concurrent Programming Concepts Process Creation and Management Signals Pipes, Named Pipes and Unix Sockets IO Multiplexing Message Queues Shared Memory Semaphores POSIX Threads

    Creation and Management Synchronization

  • 8/3/2019 UNIX_IPC

    3/122

    3

    Standards

    There are many UNIX flavors around... Linux, HP-UX, Solaris, AIX, BSD, IRIX, etc. Different capabilities, different APIs for advanced functionalities

    Vendors tried to find a common ground: POSIX = Portable Operating System Interface

    Standards? UNIX System V (Release 4) BSD 4.3 (1988) POSIX.1 (1994) POSIX Spec. 1170 (2001) IEEE Std. 1003.1-2001 (or POSIX)

  • 8/3/2019 UNIX_IPC

    4/122

    4

    POSIX Extensions

    There is the 2001 base standard and theres its extensions Implementations that comply with the base standard define

    _POSIX_VERSION to 200112L in

  • 8/3/2019 UNIX_IPC

    5/122

    5

    POSIX Extensions (2)

    And many,many more...

  • 8/3/2019 UNIX_IPC

    6/122

    6

    Man pages typically indicate compliance

  • 8/3/2019 UNIX_IPC

    7/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    Process Creation andManagement

  • 8/3/2019 UNIX_IPC

    8/122

    8

    Process Model

    Process creation in Unix isbased on spawning childprocesses which inherit all thecharacteristics of their fathers

    Variables, program counter,open files, etc.

    Spawning a process is doneusing the fork() system call

    After forking, each process willbe executing having differentvariables and different state.

    The Program Counter will bepointing to the next instruction

    Changing a variable in the childprogram doesnt affect itsfather (and vice-versa)

    a = f();fork();b = g();

    State

    a = f();

    fork();b = g();

    State

    a = f();

    fork();b = g();

    State

    ChildProcess

    Original Process

    OriginalProcess

  • 8/3/2019 UNIX_IPC

    9/122

    9

    Process Management

    Each process has an unique identifier (PID). Each process has afather, which is also identified (PPID).

    pid_t getpid(void);Returns the PID of the current process.

    pid_t getppid(void);Returns the PID of the parent process.

    pid_t fork(void);Creates a new process which inherits all its fathers state. It returns 0on the original process and the childs PID in the spawned process.

    pid_t wait(int* status);Waits until a child process exits. The status of the child is set instatus. (status is the return value of the process)

    pid_t waitpid(pid_t who, int* status, int options);Same as wait() but allows to wait for a particular child. In options, byusing WNOHANG in options, allows for checking if a child has alreadyexited without blocking. 0 in whomeans wait for any child.

  • 8/3/2019 UNIX_IPC

    10/122

    10

    Using processes for doing different things

    The key idea is to create asymmetry between processes#include#include#include

    #include

    int main(){pid_t id;

    id = fork();if (id == 0){

    printf("[%d] I'm the son!\n", getpid());

    printf("[%d] My parent is: %d\n", getpid(), getppid());}

    else{

    printf("[%d] I'm the father!\n", getpid());wait(NULL);

    }return 0;

    }

  • 8/3/2019 UNIX_IPC

    11/122

    11

    And the result is...

    But why did we do wait(NULL)?

    ...printf("[%d] I'm the father!\n", getpid());

    wait(NULL);...

  • 8/3/2019 UNIX_IPC

    12/122

    12

    Process Termination in UNIX

    A process is only truly eliminated by the operating systemwhen its father calls wait()/waitpid() on it. This allows the parent check things like the exit code of its sons

    Zombie Process: One that has died and its parent hasnot acknowledged its death (by calling wait()) Be careful with this if your are designing servers. They are eating

    up resources!!

    Orphan Process: One whose original parent has died. Inthat case, its parent becomes init (process 1).

  • 8/3/2019 UNIX_IPC

    13/122

    13

    Lets generate some Zombies

    #include#include

    #include#include#include

    void worker() {printf("[%d] Hi, I'm a worker process! Going to die...\n",

    getpid());}

    int main(){

    for (int i=0; i

  • 8/3/2019 UNIX_IPC

    14/122

    14

    Zombies (2)

  • 8/3/2019 UNIX_IPC

    15/122

    15

    Lets see adoption by init

    #include (...)

    void worker(){ sleep(10);printf("[%d] Let's see who my dady is: %d\n", getpid(),

    getppid());}

    int main(){for (int i=0; i

  • 8/3/2019 UNIX_IPC

    16/122

    16

    And the result is...

  • 8/3/2019 UNIX_IPC

    17/122

    17

    How to structure code

    (...)

    if ((id = fork()) == 0)

    {// Huge amount of code// ...

    }else

    { // Huge amount of code// ...

    }

    (...)

    Fairly common...Dont do it!

  • 8/3/2019 UNIX_IPC

    18/122

    18

    How to structure code

    void client(int id){// Client code

    // ...}

    if ((id = fork()) == 0){

    client(id);exit(0);

    }else if (id == -1){

    error();}

    // Original process code// ...

    Server

    Client Client Client

    Note: You still have to consider how to take care of zombies

  • 8/3/2019 UNIX_IPC

    19/122

    19

    How a process becomes another executable

    Somehow the OS must be able to execute code startingfrom an executable file e.g. how does the shell (bash) becomes ls?

    int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ..., char *const envp[]);

    int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);

    exec family of functionsAllow to substitute the current process executable image by

    another one. The substitution is complete!

    The functions that have a p make use of the environment PATH;The functions that have a v make use of a pointer to an array onparameters; The functions that have an l have the parameterspassed separated by commas

    Make sure that the first parameter is the name of the program!

  • 8/3/2019 UNIX_IPC

    20/122

    20

    Example

    Simple program that lists the files in the current directory

    Note: A successful exec() never returns The code, the stack, the heap, its all replaced by the new

    executable

    Original Code ls code

    ls code

    exec()

  • 8/3/2019 UNIX_IPC

    21/122

    21

    The corresponding code...

    #include#include

    #include#include

    int main(){if (execlp("ls", "ls", "-a", NULL) == -1)

    perror("Error executing ls: ");elseprintf("This cannot happen!\n");

    return 0;

    }

    char* ls_param[] = { "ls", "-a", NULL };

    if (execvp(ls_param[0], ls_param) == -1)

    perror("Error executing ls: ");

    Using an arraycan be moreflexible...

  • 8/3/2019 UNIX_IPC

    22/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    Asynchronous Events:Signals

  • 8/3/2019 UNIX_IPC

    23/122

    23

    Signals

    A signal represents an asynchronous event which anapplication must (should? can?) process The programmer can register a routine to handle such events

    Examples: The user hits Ctrl+C SIGINT The system requests the application to terminate SIGTERM The program tried to write to a closed channel SIGPIPE

    Process

    normal flowof execution

    int sigint_handler() {// process signal}

    SIGINT

  • 8/3/2019 UNIX_IPC

    24/122

    24

    Signals (2)

    Signals can be in one of four states:

    Blocked:Upon arrival, they are stored in a queue until the process unblocksthem. Then, they are delivered.

    Ignored:Upon arrival, they are discarded. It is as if they had never existed.

    Being Handled:They are redirected to a signal handler which is called.

    None of the above:Non-handled, non-blocked or ignored signals. Upon arrival, theycause program termination.

    Some signals cannot be ignored or handled (e.g. SIGKILL) When a process starts, signals are on their defaultbehavior.

    Some are ignored, most are in the non-handled, non-blocked norignored state. If a signal occurs, the process will die.

  • 8/3/2019 UNIX_IPC

    25/122

    25

    Basic Signal Routines

    typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);Redirects a certain signal (signum) to a handler routine.

    int kill(pid_t pid, int sig);Sends a signal to a certain process identified by a PID. (Note: if pid is0, sends to all processes in the current process group.)

    int pause();Blocks the process until a signal is received.

    prototype of the

    handler routine

    int sigaction();int sigprocmask();int sigpending();int sigsuspend();

    Note: These are the recommended

    POSIX routines. We are not going tocover them here. The problem with

    signal() is that in certain cases itsbehavior is undefined acrosssystems.

  • 8/3/2019 UNIX_IPC

    26/122

    26

    Handling a signal

    void sigint(int signum) {char option[2];

    signal(SIGINT, sigint);

    printf("\n ^C pressed. Do you want to abort? ");scanf("%1s", option);if (option[0] == 'y') {printf("Ok, bye bye!\n");exit(0);

    }}int main(){// Redirects SIGINT to sigint()

    signal(SIGINT, sigint);

    // Do some work!

    while (1) {printf("Doing some work...\n");

    sleep(1);}return 0;

    }

  • 8/3/2019 UNIX_IPC

    27/122

    27

    Handling a signal (2)

  • 8/3/2019 UNIX_IPC

    28/122

    28

    Special constants in signal()

    signal(SIGINT, SIG_IGN)

    Ignores SIGINT

    signal(SIGINT, SIG_DFL)

    Restores SIGINT to itsdefault handling

  • 8/3/2019 UNIX_IPC

    29/122

    29

    The problem with signals

    They make programming extremely hard Its completely asynchronous: you never know when you are going to get

    a signal

    This means that you have to protect all calls!

    read(....);

    After calling a standard function, it may return -1 indicating an error errno==EINTRmeans that a certain routine was interrupted and has to

    be tried again.

    Other routines return other things. It you are using signals, you have to protect them against all that!

  • 8/3/2019 UNIX_IPC

    30/122

    30

    The problem with signals (2)

    For instance, simply to try to read a struct person fromdisk...

    struct person p;...int n, total = 0;while (total < sizeof(p)){

    n = read(fd, (char*)p + total, sizeof(p)-total);if (n == -1){if (errno == EAGAIN)

    continue;else{

    // True error!}

    }total+= n;

    }And you have to do something like thisfor all calls being done in your program!

  • 8/3/2019 UNIX_IPC

    31/122

    31

    Sending a signal

    Its just a question of calling kill() with the PID of thetarget process...

    void master(pid_t pid_son){printf("Master sleeping for a while...\n");sleep(3);printf("Master says: Hello son!\n");kill(pid_son, SIGUSR1);

    }

    int main() {pid_t son;

    // Creates a worker processif ((son=fork()) == 0) {worker();exit(0);

    }

    // The mastermaster(son);wait(NULL);

    return 0;}

  • 8/3/2019 UNIX_IPC

    32/122

    32

    The code of the child process...

    void dady_call(int signum)

    {printf("Dady has just called in!\n");

    }

    void worker(){// Redirect "user signal 1" to a handler routine

    signal(SIGUSR1, dady_call);

    // Do some workprintf("Child process, life is good...\n");for (int i=0; i

  • 8/3/2019 UNIX_IPC

    33/122

    33

    And the result is...

  • 8/3/2019 UNIX_IPC

    34/122

    34

    Danger!!!

    What do you think it will happen if you receive a signal inside a signalhandler??

    In most systems, upon entering a signal handling routine, all signals ofthat type become blocked (i.e. they are queued). [Well, for normalsignals, a finite set of them are queued (typically 1); for real timesignals, all are...]

    The other signals are still processed asynchronously if they arrive. This behavior is not consistent across systems. In fact, in some systems,

    that signal type resets to its default behavior. This means that if,meanwhile, the program receives a signal of the same type it may die! On

    that type of system, the first thing that you must do is to once again setthe signal handler.

    Well... doesnt really solve the problem, it just makes it less likely. The new POSIX routines address this use them. Also, most system

    nowadays dont reset the signal handler.

    void dady_call(int signum){signal(SIGUSR1, dady_call);

    printf("Dady has just called in!\n");}

  • 8/3/2019 UNIX_IPC

    35/122

    35

    Beware

    Signal numbers vary across operating systems andarchitectures. Dont rely on them, use symbolic constants

    Linux, i386

    man 7 signal

  • 8/3/2019 UNIX_IPC

    36/122

    36

    Some Signals

  • 8/3/2019 UNIX_IPC

    37/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    Pipes, Named Pipes andUNIX Sockets

  • 8/3/2019 UNIX_IPC

    38/122

    38

    Stream mode of communication

    Pipes, Named Pipes and Stream Unix SOCKETS allowprocesses to communicate using streams of dataA pipe is a connection between two processes. You can send

    things through the pipe, you can try to receive things from thepipe.

    A pipe acts like a synchronous finite buffer. If a process tries to write to a pipe that is full, it blocks If a process tries to read from a pipe that is empty, it blocks

    write() read()

    pipe

  • 8/3/2019 UNIX_IPC

    39/122

    39

    Pipes

    Provides for communication amount processes that arehierarchically related (i.e. father-child) Pipes must be created prior to creating child processes

    Whenever a pipe is created, using pipe(), two filedescriptors are opened: one for reading (fd[0]), one forwriting (fd[1])

    Unused file descriptors should be closed! Pipes are unidirectional (well... normally)

    fd[1] fd[0]int fd[2];

    pipe(fd);

    readingwriting

  • 8/3/2019 UNIX_IPC

    40/122

    40

    Example

    typedef struct {int a;int b;

    } numbers;

    // File descriptors for the pipe channelint channel[2];(...)

    int main() {// Create a pipepipe(channel);

    // Create the processesif (fork() == 0) {worker();exit(0);

    }master();wait(NULL);

    return 0;}

  • 8/3/2019 UNIX_IPC

    41/122

    41

    Example (cont.)

    void worker() {numbers n;

    close(channel[1]);

    while (1) {read(channel[0], &n, sizeof(numbers));printf("[WORKER] Received (%d,%d) from master to add. Result=%d\n",

    n.a, n.b, n.a+n.b);}

    }

    void master(){numbers n;

    close(channel[0]);

    while (1) {n.a = rand() % 100;

    n.b = rand() % 100;

    printf("[MASTER] Sending (%d,%d) for WORKER to add\n", n.a, n.b);write(channel[1], &n, sizeof(numbers));sleep(2);

    }}

  • 8/3/2019 UNIX_IPC

    42/122

    42

    The result is...

  • 8/3/2019 UNIX_IPC

    43/122

    43

    Be careful!

    A pipe is a finite buffer. If you try to write too much too quickly intoit, the process will block until some space clears up.

    Atomicity is something to be dealt with If you try to write less that PIPE_BUF bytes into a pipe, you are

    guarantied that it will be written atomically

    It you try to write more, you have no guaranties! If several processes arewriting at the same time, the writes can be interleaved

    Also, when a process tries to read from a pipe, you are not guarantiedthat it will be able to read everything Meaning...

    You must synchronize your writes when youre writing a lot of data! You must ensure that you read complete messages!

    struct person p;

    int n, total = 0;

    while (total < sizeof(p)) {n = read(fd[0], (char*)p + total, sizeof(p)-total);total+= n;

    }

  • 8/3/2019 UNIX_IPC

    44/122

    44

    Controlling File Descriptors

    Each process has a file descriptor table. By default, entries 0, 1 and 2are: stdin, stdout, stderr.

    Each time a file is open, an entry is added to this table. Each time afile is closed, the corresponding entry becomes available.

    The process descriptor table, in fact, contains only references to theOS global file descriptor table.

    01

    2

    3

    4

    5

    6

    stdinstdout

    stderr

    f2

    f3File Descriptor Table after:open(f1)open(f2)

    open(f3)close(f1)

  • 8/3/2019 UNIX_IPC

    45/122

    45

    Controlling File Descriptors (2)

    Two routines are useful for controlling file descriptors: int dup(int fd)Duplicates file descriptor fd on the first available position of the

    file descriptor table.

    int dup2(int fd, int newfd)Duplicates file descriptor fd on the newfd position, closing it ifnecessary.

    Note that after a file descriptor is duplicated, the originaland the duplicate can be used interchangeably. Theyshare the file pointers, the buffers, locks, etc. Careful: Closing one file descriptor doesnt close all other that

    have been duplicated!

  • 8/3/2019 UNIX_IPC

    46/122

    46

    Implementing a pipe between two processes

    Implementing a pipe betweentwo processes is quite easy.Its only necessary to associatethe standard output of oneprocess with the standard inputof another.

    Simple example: ls | sort. Note: closing one file

    descriptor doesnt close allother that have beenduplicated!

  • 8/3/2019 UNIX_IPC

    47/122

    47

    Resulting in...

  • 8/3/2019 UNIX_IPC

    48/122

    48

    NAMED PIPES

  • 8/3/2019 UNIX_IPC

    49/122

    49

    Named Pipes (also known as FIFOs)

    Similar to pipes but allow communication betweenunrelated processes. Each pipe has a name (string). The pipe is written persistently in the file system. For creating a named pipe, use the mkfifo command or call

    mkfifo(const char* filename, mode_t mode);

    Typically, like pipes, they are half-duplex Means that they must be open read-only or write-only They are opened like files, but they are not filesYou cannot fseek() a named pipe; write() always appends to the

    pipe, read() always returns data from the beginning of the pipe.

    After data is read from the named pipe, its no longer there. Itsnot a file, its an object in the unix kernel!

  • 8/3/2019 UNIX_IPC

    50/122

    50

    Unrelated client/server program (np_server.c)

    #define PIPE_NAME "np_client_server"(...)

    int main(){// Creates the named pipe if it doesn't exist yetif ((mkfifo(PIPE_NAME, O_CREAT|O_EXCL|0600)

  • 8/3/2019 UNIX_IPC

    51/122

    51

    Unrelated client/server program (np_client.c)

    #define PIPE_NAME "np_client_server"(...)

    int main(){// Opens the pipe for writingint fd;if ((fd = open(PIPE_NAME, O_WRONLY)) < 0) {perror("Cannot open pipe for writing: ");exit(0);

    }

    // Do some workwhile (1) {numbers n;n.a = rand() % 100;n.b = rand() % 100;printf("[CLIENT] Sending (%d,%d) for adding\n", n.a, n.b);write(fd, &n, sizeof(numbers));sleep(2);

    }

    return 0;}

  • 8/3/2019 UNIX_IPC

    52/122

    52

    Executing them...

  • 8/3/2019 UNIX_IPC

    53/122

    53

    Some interesting issues...

    If you get a SIGPIPE signal, this means that you are tryingto read/write from a closed pipe

    A named pipe is a connection between two processes. Aprocess blocks until the other party open the pipe...

    Being it for reading or writing. Its possible to bypass this behavior (open it non-blocking

    O_NONBLOCK), but be very, very careful: if not properlyprogrammed, it can lead to busy waiting. If a named pipe is opennon-blocking, EOF is indicated when read() returns 0.

    When designing a client/server multiple client application, thismeans that either the pipe is re-opened after each client

    disconnects, or the pipe is open read-write. If opened read-write, the server will not block until the other

    party connects (since, he itself is also another party!)

  • 8/3/2019 UNIX_IPC

    54/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    I/O Multiplexing

  • 8/3/2019 UNIX_IPC

    55/122

    55

    Interesting Problem

    A printer daemon is connected to a physical printer There are 3 named-pipes which allow automatic formatted

    printing

    PrinterDaemon

    /printer/a4_double_sided

    /printer/a4_single_sided

    /printer/a3_single_sided

    Pipes are blocking, so thisdoesnt work!!!

  • 8/3/2019 UNIX_IPC

    56/122

    56

    I/O Multiplexing

    I/O Multiplexing: The ability to examine several filedescriptors at the same time select() and pselect()

    int select(int n,fd_set* readfd,fd_set* writefd,fd_set* exceptfd,struct timeval* timeout)

    Blocks until activity is detected or a timeout occurs.

    Greatest fd plus one

    For reading activity

    For writing activity

    For out-of-band activity

    The fd_set variables are input/output. Upon return, they indicateif there was activity in a certain descriptor or not.

  • 8/3/2019 UNIX_IPC

    57/122

    57

    select()

    Careful: n is the number of the highest file-descriptoradded of one. Its not the number of file descriptors

    fd_setA bit set representingfile descriptors

    FD_ZERO(fd_set* set)Cleans up the file descriptor set

    FD_SET(int fd, fd_set* set)Sets a bit in the file descriptor set

    FD_CLEAR(int fd, fd_set* set)Clears a bit in the file descriptor set

    FD_ISSET(int fd, fd_set* set)Tests if a file descriptor is set

  • 8/3/2019 UNIX_IPC

    58/122

    58

    Example (printerd.c)

    (...)

    #define BUF_SIZE 4096#define NUM_PRINTERS 3

    constchar* PRINTER_NAME[] = {"printer1", "printer2", "printer3"

    };

    // The printer file descriptorsint printer[NUM_PRINTERS];

    void create_printers() {for (int i=0; i= 0);

    }}

    int main(int argc, char* argv[]) {create_printers();accept_requests();

    }

  • 8/3/2019 UNIX_IPC

    59/122

    59

    Example (printerd.c) (2)

    void accept_requests() {while (1) {fd_set read_set;

    FD_ZERO(&read_set);for (int i=0; i 0 ) {for (int i=0; i 0) {buf[n] = '\0';printf("%s", buf);

    }

    } while (n > 0);

    close(printer[i]);printer[i] = open(PRINTER_NAME[i], O_RDONLY|O_NONBLOCK);

    }} } }

    }

  • 8/3/2019 UNIX_IPC

    60/122

    60

    Resulting in...

  • 8/3/2019 UNIX_IPC

    61/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    Message Queues

  • 8/3/2019 UNIX_IPC

    62/122

    62

    Types of communication

    Streams represent a flow of bytes. There are no fixeddata boundaries. The sender requests the transmission of N bytes The data starts flowing, the receiver starts getting it The receiver may get several chucks of less then N bytes

    Messages represent a complete fixed structure of data Its like sending a letter. Either you get if fully or you dont. You

    dont get half a letter.

    169

  • 8/3/2019 UNIX_IPC

    63/122

    63

    Message Queues

    Another IPC mechanism

    Based on messages, not no data streams

    Completely asynchronousA process can start executing, write some messages to a message

    queue and die; another process can latter on come alive anreceive them.

    Sharp contrast with all the mechanisms that weve seen so far,which require both the sender and the receiver to be present atthe same time

    Message queues are maintained by the operating system. Theyare not destroyed if a process dies!

    msgsnd() msgrcv()

  • 8/3/2019 UNIX_IPC

    64/122

    64

    Message Queues System V

    int msgget(key_t key, int flags)Obtains an identifier to an existing message queue or creates a new

    one. key can be IPC_PRIVATE (which creates a new unique identifier), or an

    existing identifier. ftok() can be used to generate a number based on afilename.

    flags, normal mode flags. When ORed with IPC_CREAT creates a newone.

    int msgctl(int mqid, int cmd, struct msqid_ds* buff)Provides a variety of control operations on the message queue. mqid is the value returned by msgget() cmd is the command (most usually: IPC_RMID to remove it) buff a structure used in some control operations

  • 8/3/2019 UNIX_IPC

    65/122

    65

    Message Queues System V (2)

    int msgsnd(int mqid, const void* message, size_t length, int flags)Sends a message to a certain key.

    mqid is the value returned by msgget() message its a pointer to the message to send length represents the length of the payload of the message (not total) flags: 0 or IPC_NOWAIT (non-blocking)

    int msgrcv(int mqid, void* message, size_t length, int type, int flags)Retrieves a message from a message queue. mqid is the value returned by msgget() message its a pointer to the message to receive length represents the maximum payload we are willing to receive type represent the type of message to receive (0 FIFO) flags: 0 or IPC_NOWAIT

  • 8/3/2019 UNIX_IPC

    66/122

    66

    Messages a Message Payload

    In System V a message can be anything. But, it mustalways have a long integer in the beginning This long is called a message type identifier

    typedef struct

    { long mtype;int first;int second;

    } numbers_message;

    Message type (must be >0)!

    Payload

  • 8/3/2019 UNIX_IPC

    67/122

    67

    mq_pong.c (1)

    typedefstruct {long mtype;

    int first, second;} numbers_msg;

    // Message queue idint id;

    void cleanup(int signum) {

    msgctl(id, IPC_RMID, NULL);exit(0);}

    void main(int argc, char* argv[]) {assert( (id = msgget(IPC_PRIVATE, IPC_CREAT|0700)) != 0 );signal(SIGINT, cleanup);

    if (fork() == 0)ping();

    elsepong();

    }

  • 8/3/2019 UNIX_IPC

    68/122

    68

    mq_pong.c (2)

    void ping(){

    numbers_msg msg;msg.first = rand() % 100;msg.second = rand() % 100;

    while (1) {msg.mtype = 1;

    printf("[A] Sending (%d,%d)\n", msg.first, msg.second);msgsnd(id, &msg, sizeof(msg)-sizeof(long), 0);

    msgrcv(id, &msg, sizeof(msg)-sizeof(long), 2, 0);

    printf("[A] Received (%d,%d)\n", msg.first, msg.second);

    ++msg.first;++msg.second;

    sleep(3);}

    }

  • 8/3/2019 UNIX_IPC

    69/122

    69

    mq_pong.c (3)

    void pong(){

    numbers_msg msg;

    while (1) {msgrcv(id, &msg, sizeof(msg)-sizeof(long), 1, 0);printf("[B] Received (%d,%d)\n", msg.first, msg.second);

    msg.mtype = 2;

    ++msg.first;++msg.second;

    printf("[B] Sending (%d,%d)\n", msg.first, msg.second);

    msgsnd(id, &msg, sizeof(msg)-sizeof(long), 0);}

    }

    R l i i

  • 8/3/2019 UNIX_IPC

    70/122

    70

    Resulting in...

    IPC R

  • 8/3/2019 UNIX_IPC

    71/122

    71

    IPC Resources

    Remember, IPC resources are not automatically cleaned This can lead so serious resource leaks

    ipcs allows you to see the current System V IPCs in useipcrm allows you to manually delete resources

    POSIX M Q

  • 8/3/2019 UNIX_IPC

    72/122

    72

    POSIX Message Queues

    Very similar to System V except: Message types represent prioritiesA read from a POSIX message queue always returns the oldest

    message of the largest type (priority)

    POSIX message queues allow a signal to be raised when amessage is put on an empty queue or the initiation of a thread

    Messages queues are represented by names on the file system(like named pipes)

    mq_open() mq_close() mq_unlink() mq_send() mq_receive()

  • 8/3/2019 UNIX_IPC

    73/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    Shared Memory andSemaphores

    Wh h d ?

  • 8/3/2019 UNIX_IPC

    74/122

    74

    Why shared memory?

    Up until now... System calls are slow! Copying thought the kernel is slow!

    P1 P2

    buffer

    write() read()

    UserSpace

    KernelSpace

    Wh h d ?

  • 8/3/2019 UNIX_IPC

    75/122

    75

    Why shared memory?

    Shared Memory (Almost) No kernel involvement! Fast! Very Fast!

    P1 P2

    shared

    bufferUserSpace

    Kernel

    Space

    Dangerous, very dangerous!

    H d it k

  • 8/3/2019 UNIX_IPC

    76/122

    76

    How does it work

    Each process has an address space Each address space corresponds to a page table. There are as

    many page tables as there are processes

    Shared memory corresponds to putting the same realmemory pages in the page tables of two differentprocesses

    0 0

    4Gb 4Gb

    Address Space

    Process A

    Address Space

    Process B

    1000 1000

    Physical Memory

    0

    256Mb5000

    Page Translation

    (slightly simplified)

    Shared Memor S stem V

  • 8/3/2019 UNIX_IPC

    77/122

    77

    Shared Memory System V

    int shmget(key_t key, int size, int flags)Obtains an identifier to an existing shared memory or creates a new

    one. key can be IPC_PRIVATE (which creates a new unique identifier), or an

    existing identifier. ftok() can be used to generate a number based on afilename.

    size its the shared memory size in bytes flags, normal mode flags. When ORed with IPC_CREAT creates a new

    one.

    int shmctl(int shmid, int cmd, struct shmid_ds* buff)Provides a variety of control operations on the shared memory. shmid is the value returned by shmget() cmd is the command (most usually: IPC_RMID to remove it) buff a structure used in some control operations

    Shared Memory System V (2)

  • 8/3/2019 UNIX_IPC

    78/122

    78

    Shared Memory System V (2)

    int shmat(int shmid, const void* where, int flags)Maps a certain shared memory region into the current process

    address space. shmid represents the shared memory identifier shmid returned by

    shmget()

    where represents an unused address space location where to map theshared memory (normally, use NULL)

    flags represent different ways of doing the mapping (typically 0)

    int shmdt(const void* where)Unmaps a certain shared memory region from the current addressspace. where represents an unused address space location where to map the

    shared memory (normally, use NULL)

    How does attaching work

  • 8/3/2019 UNIX_IPC

    79/122

    79

    How does attaching work

    Address Space A Address Space B

    Real Memory

    int id = shmget(1234, 2, IPC_CREAT|0777)

    char* p = shmat(id, NULL, 0);

    p[0] = 19;

    int id = shmget(1234, 2, 0777)

    char* p = shmat(id, NULL, 0);

    p[1] = 59;

    p

    p(id.. 1234)

    19

    19

    19

    59

    5959

    Whats wrong with this routine?

  • 8/3/2019 UNIX_IPC

    80/122

    80

    Whats wrong with this routine?

    P1 P2

    print_work(a, 12); print_work(b, 65);

    Synchronization Semaphores

  • 8/3/2019 UNIX_IPC

    81/122

    81

    Synchronization Semaphores

    A semaphore is a synchronization object Controlled access to a counter (a value) Two operations are supported: wait() and post()

    wait() If the semaphore is positive, decrement it and continue If not, block the calling process (thread)

    post() Increment the semaphore value If there was any process (thread) blocked due to the semaphore, unblock one of

    them.

    5

    P1

    value

    P6 P3 NULLblockedprocesslist

    A semaphore

    Corrected version

  • 8/3/2019 UNIX_IPC

    82/122

    82

    Corrected version

    Mutual Exclusion:Only one process can be in here!

    You always have to synchronize, even if you areonly reading or writing one byte!

    Synchronization Semaphores

  • 8/3/2019 UNIX_IPC

    83/122

    83

    Synchronization Semaphores

    System V Semaphores Works with semaphore arrays semget(), semctl(), semop()A little bit hard to use by themselves Use a library to encapsulate them!

    POSIX Semaphores Quite easy to use sem_init(), sem_close(), sem_post(), sem_wait()Also work with threads! But... in Linux, they only work with threads (kernel 2.4)

    Semaphores are very useful for other thingsbesides mutual exclusion: they can be used to count things!

    semaphore h

  • 8/3/2019 UNIX_IPC

    84/122

    84

    semaphore.h

    My Semaphore Library

    Example Producer/Consumer

  • 8/3/2019 UNIX_IPC

    85/122

    85

    Example Producer/Consumer

    A producer puts elements on a finite buffer. If the bufferis full, it blocks until theres space.

    The consumer retrieves elements. If the buffer is empty, itblocks until something comes along.

    We will need three semaphores

    One to count the empty slots One to count the full slots One to provide for mutual exclusion to the shared buffer

    Producer Consumer

    Example Producer/Consumer

  • 8/3/2019 UNIX_IPC

    86/122

    86

    Example Producer/Consumer

    Producer Consumer

    empty full

    read_pos write_pos

    put_element(e) {sem_wait(empty);sem_wait(mutex);

    buf[write_pos] = e;write_pos = (write_pos+1) % N;sem_post(mutex);sem_post(full);

    }

    mutex

    get_element() {sem_wait(full);sem_wait(mutex);

    e = buf[read_pos];read_pos = (read_pos+1) % N;sem_post(mutex);sem_post(empty);return e;

    }

    The result of executing it!

  • 8/3/2019 UNIX_IPC

    87/122

    87

    The result of executing it!

    And the main part of its code

  • 8/3/2019 UNIX_IPC

    88/122

    88

    And the main part of its code...

    void producer() {for (int i=TOTAL_VALUES; i>0; i--) {printf("[PRODUCER] Writing %d\n", i);put_element(i);}}

    void consumer() {for (int i=0; i

  • 8/3/2019 UNIX_IPC

    89/122

    89

    put_element() and get_element()

    void put_element(int e) {sem_wait(sem, EMPTY);sem_wait(sem, MUTEX);buf[write_pos] = e;write_pos = (write_pos+1) % N;sem_post(sem, MUTEX);sem_post(sem, FULL);}

    int get_element() {sem_wait(sem, FULL);sem_wait(sem, MUTEX);int e = buf[read_pos];read_pos = (read_pos+1) % N;sem_post(sem, MUTEX);sem_post(sem, EMPTY);return e;}

    init() and terminate()

  • 8/3/2019 UNIX_IPC

    90/122

    90

    init() and terminate()

    int sem, shmid;int write_pos, read_pos;

    int* buf;

    void init() {sem = sem_get(3, 0);sem_setvalue(sem, EMPTY, N); // N is the number of slotssem_setvalue(sem, FULL, 0);

    sem_setvalue(sem, MUTEX, 1);

    write_pos = read_pos = 0;

    shmid = shmget(IPC_PRIVATE, N*sizeof(int), IPC_CREAT|0700);

    buf = (int*) shmat(shmid, NULL, 0);}

    void terminate() {

    sem_close(sem);shmctl(shmid, IPC_RMID, NULL);

    }

    Remember

  • 8/3/2019 UNIX_IPC

    91/122

    91

    Remember

    Always cleanup...

    Implementation of semlib (semlib.c)

  • 8/3/2019 UNIX_IPC

    92/122

    92

    Implementation of semlib (semlib.c)

    Implementation of semlib (semlib.c) (2)

  • 8/3/2019 UNIX_IPC

    93/122

    93

    Implementation of semlib (semlib.c) (2)

    Implementation of semlib (semlib.c) (3)

  • 8/3/2019 UNIX_IPC

    94/122

    94

    Implementation of semlib (semlib.c) (3)

  • 8/3/2019 UNIX_IPC

    95/122

    InterprocessCommunication in Unix

    Department of Informatics Engineering

    University of Coimbra

    Copyright: Paulo Marques, DEI

    POSIX Threads

    Threads

  • 8/3/2019 UNIX_IPC

    96/122

    96

    Threads

    Flows of execution inside of a program They share all the address space of a process Each thread has its own stack and local variables

    (even so, they can access others threads variables)Process

    Thread 1 Thread 2Thread 3

    Why use threads?

  • 8/3/2019 UNIX_IPC

    97/122

    97

    y

    They are very light weight compared to processes Light context switches Fast to create and terminate Fast to synchronize

    Much easier to program than shared memory! Everything is already shared Be careful to synchronize accesses!

    POSIX Threads Thread Management

  • 8/3/2019 UNIX_IPC

    98/122

    98

    g

    Simple thread creation example (simple thread.c)

  • 8/3/2019 UNIX_IPC

    99/122

    99

    p p ( p _ )

    Running the example

  • 8/3/2019 UNIX_IPC

    100/122

    100

    g p

    gcc lpthread D_REENTRANT Wall fich.c o fich

    Compiling with threads

  • 8/3/2019 UNIX_IPC

    101/122

    101

    p g

    Linux

    -D_REENTRANT is quite important in LinuxThreads (Kernel2.4)

    It instructs the compiler to use special re-entrant routine functions If you dont... it ONLY appears to work, until you get in trouble!

    gcc lpthread D_REENTRANT Wall fich.c o fich

    Beware: Many routines are not re-entrant, they cannot be

    directly used with threads since they use common storage

    in an unsynchronized way (e.g. stktok())!

    In some cases, there are re-entrant versions (e.g. strtok_r()).Check the manual! Dont trust common sense.

    Example of a non-reentrant routine

  • 8/3/2019 UNIX_IPC

    102/122

    102

    p

    What happens if this is called from two

    different threads at the same time??

    Things to beware of

  • 8/3/2019 UNIX_IPC

    103/122

    103

    g

    Doesnt work, i is on the stack and constantly changing

    Doesnt work, (1) after main() dies, its variables disappear

    race condition with the starting threads; (2) main() dies everything dies!

    If you need to terminate the main() thread...

  • 8/3/2019 UNIX_IPC

    104/122

    104

    y ()

    This is OK!

    Note: the other threads continue to execute.

    Synchronization

  • 8/3/2019 UNIX_IPC

    105/122

    105

    Mutexes Provide mutual exclusion zones between threads In fact, these are just fast binary semaphores

    POSIX Semaphores Used to signal events across threads Used to count objects in an synchronized way

    Condition VariablesAllow a thread to block or to notify others on any condition Semaphores are a kind of condition variable:

    the implicit condition is the semaphore being greater than 0

    Mutexes

  • 8/3/2019 UNIX_IPC

    106/122

    106

    POSIX Semaphores

  • 8/3/2019 UNIX_IPC

    107/122

    107

    Producer/Consumer Revisited

  • 8/3/2019 UNIX_IPC

    108/122

    108

    Producer Consumer

    Producer

    Producer

    prod_cons_threads.c

  • 8/3/2019 UNIX_IPC

    109/122

    109

    prod_cons_threads.c (2)

  • 8/3/2019 UNIX_IPC

    110/122

    110

    Which results in...

  • 8/3/2019 UNIX_IPC

    111/122

    111

    Synchronization Condition Variables

  • 8/3/2019 UNIX_IPC

    112/122

    112

    Condition variables allow the programmer to suspend ornotify a thread on any condition!

    Synchronization Condition Variables (2)

  • 8/3/2019 UNIX_IPC

    113/122

    113

    Important rule:A condition variable always has an associated mutex.Always check the condition variable in mutual exclusion. The

    mutex must be locked.

    How does it work?Makes thread Acheck its condition

    again

    Synchronization Condition Variables (3)

  • 8/3/2019 UNIX_IPC

    114/122

    114

    The thread tests a condition inmutual exclusion. If the conditionis false, pthread_cond_wait()atomically releases de mutex

    ANDwaits until someonesignals that the conditionshould be tested again.

    When the condition issignaledANDthe mutex isavailable, pthread_cond_wait()atomically reacquires themutexANDreleases thethread.

    pthread_cond_signal() indicatesthat exactly one blockedthread should test thecondition again. Note that thisis note a semaphore. If there isno thread blocked, the signal islost.

    If all threads should re-check thecondition, usepthread_cond_broadcast(). Sincea mutex is involved, each onewill test it one at a time, inmutual exclusion.

    Example

  • 8/3/2019 UNIX_IPC

    115/122

    115

    Suppose a buffer that can hold a maximum of N elements.When it is full, it should immediately be emptied. While

    the buffer is being emptied, no thread can put things intoit.

    Producer

    CleanerProducer

    Producer

    bounded_buffer.c

  • 8/3/2019 UNIX_IPC

    116/122

    116

    bounded_buffer.c (2)

  • 8/3/2019 UNIX_IPC

    117/122

    117

    With 3 producers and one cleaner...

  • 8/3/2019 UNIX_IPC

    118/122

    118

    Condition Variables Rules (!)

  • 8/3/2019 UNIX_IPC

    119/122

    119

    The condition must always be tested with a while loop, never an if!Being unlocked out of a condition variable only means that thecondition must be re-checked, not that it is true

    The condition must always be checked and signaled inside a lockedmutex.

    While condition() may betrue while Thread B is executing,something may happen betweenthe time that the condition is signaledand Thread A is unblock (e.g. another

    thread may change the condition)

    Synchronization Some basic rules...

  • 8/3/2019 UNIX_IPC

    120/122

    120

    Never Interlock waits! Locks should always be taken in the same order Always release locks in the reverse order they have been

    taken

    sem_wait(A)sem_wait(B)

    // Critical Section

    sem_post(B)sem_post(A)

    sem_wait(B)sem_wait(A)

    // Critical Section

    sem_post(A)sem_post(B)

    Deadlock!

    One way to assure that you always take locks in the sameorder is to create a lock hierarchy. I.e. associate a numberto each lock using a table and always lock in increasingorder using that table as reference (index).

    Synchronization Some basic rules... (2)

  • 8/3/2019 UNIX_IPC

    121/122

    121

    Sometimes it is not possibleto know each order to takewhen locking (or usingsemaphores) Example: you are using two

    resources owned by theoperating system. They arecontrolled by locks. Youcannot be sure anotherapplication is not using

    exactly the same resourcesand locking in reverseorder.

    In that case, usepthread_mutex_trylock() orsem_trywait() and back offif you are unsuccessful. Allow the system to make

    progress and not deadlock

    To Learn More

  • 8/3/2019 UNIX_IPC

    122/122

    Advanced Programming in the UNIXEnvironment, 2nd Edition

    by W. Richard Stevens, Stephen A. RagoAddison-Wesley, June 2005

    UNIX Network Programming, Volume 2:Interprocess Communications, 2nd Ed. by W. Richard Stevens

    Prentice Hall, August 1998

    Unix Systems Programming:Communication, Concurrency and Threads,2nd Edition