Distributed and Parallel Processing George Wells
JCSP: Producer/Consumer Problem channel Producer Consumer One2OneChannel
Integer Producer/Consumer Integer object Producer Consumer One2OneChannelInt channel.write(item) One2OneChannelInt int item = channel.read() Channel_of_Integer
Producer Overview class Producer implements CSProcess { private One2OneChannelInt channel; public Producer (final One2OneChannelInt out) { channel = out; } // constructor public void run() { int item = 100; channel.write(item); } // run } // class Producer
Consumer Overview class Consumer implements CSProcess { private One2OneChannelInt channel; public Consumer (final One2OneChannelInt in) { channel = in; } // constructor public void run() { int item = channel.read(); System.out.println(item); } // run } // class Consumer
JCSP Classes Needed import jcsp.lang.*;
To Execute public final class PCMain { public static void main(String[] args) { new PCMain(); } // main public PCMain() { // create channel object final One2OneChannelInt channel = new One2OneChannelInt(); // create and run parallel construct with list of procs new Parallel ( new CSProcess[] { new Producer(channel), new Consumer(channel) } ).run (); } // constructor } // class PCMain
Practical Assignment 2 On the course web site follow the practical assignment link – Example program to get the feel of the JCSP library
Formal Analysis and JCSP Formal Analysis of Concurrent Java Systems, Peter Welch and Jeremy Martin, Communicating Process Architectures 2000 – Formal model of Java concurrency mechanisms – Formal proof of equivalence between JCSP and CSP communication also CTJ communication – Formal verification of JCSP ALT mechanism incorrect and correct!
Multiprocessing in UNIX UNIX OSes: – Threads (pthreads) – Processes – Interprocess communication Processes – Use the fork/join model
Multiprocessing in UNIX The fork() system call – Creates two copies of calling process int pid; if ((pid = fork()) == 0) { /* Execute child process code */... } else { /* Execute parent process code */... }
Exercise Write a “Hello World” multiprocessing program for Linux – Use fork() – Child and parent write different messages
Interprocess Communication (IPC) in UNIX Unix operating systems have a rich and powerful set of mechanisms – Message queues Asynchronous – Semaphore sets – Shared memory Known as System V IPC
System V IPC Complex APIs All forms require a numeric “key” – Unique to application – Shared by all cooperating processes The ftok system call generates a key from: – a filename – a single character “project” ID Use the key to “get” a numeric identifier
Example: Message Queue int msgKey = ftok(“/home/csgw/somefile.txt”, 'a'); int msgqID = msgget(msgKey, IPC_CREAT | 0660);
Sending a Message Messages have – a type ( long ) – a message (arbitrary block of data) struct msgbuf { long mtype; /* type */ char mtext[1]; /* message text */ }; struct my_msgbuf { long mtype; /* Type */ long request_id; /* Request ID */ struct client info; /* Data */ };
To Send struct msgbuf { long mtype; /* type */ char mtext[1000]; /* message text */ } myMsg; int result, length; /* The length is essentially the size of the structure minus sizeof(mtype) */ length = sizeof(struct msgbuf) – sizeof(long); myMsg.mtype = 1L; if((result = msgsnd( msgqID, &myMsg, length, 0)) == -1) /* ERROR... */ Or IPC_NOWAIT
To Receive struct msgbuf rdBuf; int length, len; length = sizeof(struct msgbuf) – sizeof(long); len = msgrcv(msgqID, &rdBuf, length, 1L, 0); if (len == -1) /* ERROR... */ Or IPC_NOWAIT Type parameter – =0: first message on queue – >0: first message matching type (exactly) – <0: first message with type ≤ |type|
Message Queue Control The msgctl() system call: – Query internal data structures – Set permissions – Delete message queue msgctl( msgqID, IPC_RMID, 0);
Exercise Modify the “Hello World” program – Use fork to create two processes – Parent sends “Hello World” message to child – Child displays message on screen
Semaphores System V IPC Semaphores – Actually sets of semaphores – Multiple, atomic operations int sid, numSems;... sid = semget(myKey, numSems, IPC_CREAT | 0660);
Working with Semaphores The semop() system call int semop (int sid, struct sembuf *sops, unsigned nops); struct sembuf { ushort sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ }; The sem_op field – >0: add to the semaphore value – <0: subtract from the semaphore value Wait, if necessary – =0: Wait for semaphore to reach zero
Example: Simple Binary Semaphore struct sembuf semLock = { 0, -1, 0 }; struct sembuf semUnlock = { 0, 1, IPC_NOWAIT }; if (semop(sid, &semLock, 1) == -1) /* ERROR */ /* Do critical region stuff... */ if (semop(sid, &semUnlock, 1) == -1) /* ERROR */
Semaphore Control The semctl() system call: – Query internal data structures – Set permissions – Delete semaphore set – Get the value(s) of one/all semaphores in a set – Get the number of processes waiting – Get the number of processes waiting for 0 – Get the process ID of the last process to access the semaphore set – Set one/all semaphore value(s)
Shared Memory Use key to “get” shared memory segment – specify desired size int shmID; shmID = shmget(key, segSize, IPC_CREAT | 0640);
Shared Memory Step Two: “attach” the segment char * ptr = shmat(shmID, 0, 0); Desired address (best left as 0) Flags: address rounding, read-only.
When finished... Use shmdt() to detach shared memory segment – Doesn't necessarily delete segment shmdt(ptr);