Linux : Inter-process communication
Linux : Inter-process communication
- file
- signal
- socket
- unix domain socket Similar to an internet socket, but all communication occurs within the kernel.
- pipes
int pipe(int pipefd[2]);pipe() returns 2 file descriptors: pipefd[0] is open for reading and pipefd[1] is open for writing. Normally the process that calls pipe() then call fork(), creating IPC channel from the parent to the child or vice versa.
int mkfifo(const char *pathname, mode_t mode);Creating a FIFO is similar to creating a file. A pipe that is treated like a file. Processes write to and read from a named pipe, as if it were a regular file. If we write to a FIFO that no process has open for reading, the signal SIGPIPE is generated.
- Test the semaphore that controls the resource
- If the value of the semaphore is positive, the process can use the resource The process decrements the semaphore value by 1, indicating that it has used one unit of the resource.
- If the value of the semaphore is 0, the process goes to sleep until the semaphore is greater than 0
void * mmap (void *address, size_t length, int protect, int flags, int filedes, off_t offset)The mmap function creates a new mapping, connected to bytes (offset) to (offset + length - 1) in the file open on filedes. filedes is the file descriptor specifying the file that is to be mapped. address gives a preferred starting address for the mapping. NULL expresses no preference.
memory file high address --- ----- end of file --- <---- ----- len start addr --- <---- ----- heap --- offset low address --- ----- start of file
Mutex lock for Linux Thread Synchronization
When one thread starts executing the critical section (a serialized segment of the program) the other thread should wait until the first thread finishes.
The most popular way of achieving thread synchronization is by using Mutexes.
- A Mutex is a lock that we set before using a shared resource and release after using it.
- When the lock is set, no other thread can access the locked region of code.
- So we see that even if thread 2 is scheduled while thread 1 was not done accessing the shared resource and the code is locked by thread 1 using mutexes then thread 2 cannot even access that region of code.
- So this ensures synchronized access of shared resources in the code.
Working on a mutex:
- mutex creation and initialization
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)The pthread_mutex_init() function shall initialize the mutex referenced by mutex with attributes specified by attr. If attr is NULL, the default mutex attributes are used.
In cases where default mutex attributes are appropriate, the macro PTHREAD_MUTEX_INITIALIZER can be used to initialize mutexes that are statically allocated. The effect shall be equivalent to dynamic initialization by a call to pthread_mutex_init() with parameter attr specified as NULL, except that no error checks are performed.
static pthread_mutex_t foo_mutex; pthread_mutex_init(&foo_mutex, NULL); static pthread_mutex_t foo_mutex_2 = PTHREAD_MUTEX_INITIALIZER;
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> pthread_t tid[2]; int counter; pthread_mutex_t lock; void* trythis(void* arg) { pthread_mutex_lock(&lock); unsigned long i = 0; counter += 1; printf("\n Job %d has started\n", counter); for (i = 0; i < (0xFFFFFFFF); i++) ; printf("\n Job %d has finished\n", counter); pthread_mutex_unlock(&lock); return NULL; } int main(void) { int i = 0; int error; if (pthread_mutex_init(&lock, NULL) != 0) { printf("\n mutex init has failed\n"); return 1; } while (i < 2) { error = pthread_create(&(tid[i]), NULL, &trythis, NULL); if (error != 0) printf("\nThread can't be created :[%s]", strerror(error)); i++; } pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); pthread_mutex_destroy(&lock); return 0; }
Conditional wait and signal in multi-threading
When you want to sleep a thread, condition variable can be used.
- there is a function pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) to wait or sleep. The pthread_cond_wait() release a lock specified by mutex and wait on condition cond variable.
- there is a function pthread_cond_signal(pthread_cond_t *cond) to wake up sleeping or waiting thread.
#include <pthread.h> #include <stdio.h> #include <unistd.h> // Declaration of thread condition variable pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; // declaring mutex pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; int done = 1; // Thread function void* foo() { // acquire a lock pthread_mutex_lock(&lock); if (done == 1) { // let's wait on conition variable cond1 done = 2; printf("Waiting on condition variable cond1\n"); pthread_cond_wait(&cond1, &lock); } else { // Let's signal condition variable cond1 printf("Signaling condition variable cond1\n"); pthread_cond_signal(&cond1); } // release lock pthread_mutex_unlock(&lock); printf("Returning thread\n"); return NULL; } // Driver code int main() { pthread_t tid1, tid2; // Create thread 1 pthread_create(&tid1, NULL, foo, NULL); // sleep for 1 sec so that thread 1 // would get a chance to run first sleep(1); // Create thread 2 pthread_create(&tid2, NULL, foo, NULL); // wait for the completion of thread 2 pthread_join(tid2, NULL); return 0; }
Multithreaded Programming Guide
Chapter 4 Programming with Synchronization Objects
Synchronization objects are variables in memory that you access just like data.
Threads in different processes can communicate with each other through synchronization objects placed in threads-controlled shared memory, even though the threads in different processes are generally invisible to each other.
The available types of synchronization objects are:
- Mutex Locks
- Condition Variables
- Semaphores
"Using Condition Variables"
- Initialize a Condition Variable
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER;The pthread_cond_init() function shall initialize the condition variable referenced by cond with attributes referenced by attr. If attr is NULL, the default condition variable attributes shall be used. the macro PTHREAD_COND_INITIALIZER can be used to initialize condition variables that are statically allocated. The effect shall be equivalent to dynamic initialization by a call to pthread_cond_init() with parameter attr specified as NULL.
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. They shall be called with mutex locked by the calling thread. These functions atomically release mutex and block the calling thread until the condition variable cond is signaled. The blocked thread can be awakened by a pthread_cond_signal(), a pthread_cond_broadcast(), or when interrupted by delivery of a signal.
When the call returns, the mutex is locked again.
int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);The pthread_cond_broadcast() function shall unblock all threads currently blocked on the specified condition variable cond. The pthread_cond_signal() function shall unblock at least one of the threads that are blocked on the specified condition variable cond. The thread(s) that are unblocked shall contend for the mutex.
int pthread_cond_destroy(pthread_cond_t *cond);The pthread_cond_destroy() function shall destroy the given condition variable specified by cond; the object becomes, in effect, uninitialized.
pthread_mutex_lock(&condition_lock); while ( condition_predicate is not TRUR) pthread_cond_wait(&condition_variable, &condition_lock); ... pthread_mutex_unlock(&condition_lock);
- a finite-size buffer
typedef struct { char buf[BSIZE]; int occupied; int nextin; int nextout; pthread_mutex_t mutex; pthread_cond_t more; pthread_cond_t less; } buffer_t; buffer_t buffer;
void producer(buffer_t *b, char item) { pthread_mutex_lock(&b->mutex); while (b->occupied >= BSIZE) pthread_cond_wait(&b->less, &b->mutex); // wait for more space assert(b->occupied < BSIZE); b->buf[b->nextin++] = item; b->nextin %= BSIZE; b->occupied++; /* now: either b->occupied < BSIZE and b->nextin is the index of the next empty slot in the buffer, or b->occupied == BSIZE and b->nextin is the index of the next (occupied) slot that will be emptied by a consumer (such as b->nextin == b->nextout) */ pthread_cond_signal(&b->more); pthread_mutex_unlock(&b->mutex); } char consumer(buffer_t *b) { char item; pthread_mutex_lock(&b->mutex); while(b->occupied <= 0) pthread_cond_wait(&b->more, &b->mutex); // wait for more data assert(b->occupied > 0); item = b->buf[b->nextout++]; b->nextout %= BSIZE; b->occupied--; /* now: either b->occupied > 0 and b->nextout is the index of the next occupied slot in the buffer, or b->occupied == 0 and b->nextout is the index of the next (empty) slot that will be filled by a producer (such as b->nextout == b->nextin) */ pthread_cond_signal(&b->less); pthread_mutex_unlock(&b->mutex); return(item); }
留言