We are developing a simple application in Linux for Desktop computer. The scheme is very simple and as below:
A main process which deals with the external world interface (which gives some data periodically) and spawns and keeps track of child processes.
Various child processes which process this data and report back to main process from time to time.
The data coming in from external world interface is in the chunks of around 240 KBytes and 1 chunk comes in at the rate of roughly once per milliseconds. All the child processes use and work on the same data i.e. the complete chunk is sent to every child process when it arrives.
The number of child processes is not fixed, can vary from 4 to 20. The scheme adopted for inter process communication is as follows:
Shared memory capable of holding multiple data chunks is created by all the processes using common key and using shmget() and shmat() APIs. Main process writes to this shared memory and all the child processes read from this memory.
To inform the child processes that new data chunk have arrived I have used pthread_cond_broadcast() API. The conditional variable and the corresponding mutex used for this purpose reside in a small separate shared memory and are initialized to default attributes in the main process.
So whenever new data chunk arrives (roughly once per 1 ms) main process calls pthread_cond_broadcast() and the child processes which are waiting on pthread_cond_wait() read this data from the shared memory and process it. The problem I am facing is:
Depending on the processor load, sometimes the pthread signals are getting lost i.e. either delivered to only some or none of the waiting child processes. This severly affects the data processing as the data continuity is lost (and the child process is not even aware of it). Processing time of the child process is average 300 micro seconds and this application is running on a multicore processor.
To pin down the problem I even created a dummy application with 1 main process and several dummy child processes who does nothing but to wait on pthread_cond_wait(). From main process I called pthread_cond_broadcast every 1 ms and a count was incremented and printed, similarly every time a pthread signal was received in a child process another count was incremented and printed. When I ran this test program I found after some time the receiver's count began to lag the sender's count and the gap between their count went on increasing. Am I right in my understanding that this was due to some pthread signals not delivered? Are there any other fast plus secure IPC mechanisms.
I even tried the same thing using internet domain sockets using UDP datagrams in broadcast (only for the synchronization purpose while the data was still read from the shared memory). But here also I noticed as the number of child processes increased the synchronization signals were getting lost. Please give your thoughts and ideas.
Consider the test program as below:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#define SHM_KEY 3579
#define NumOfChildProc 20
int Packets_Tx = 0, Packets_Rx = 0;
void ChildProc(void)
{
/* Create the shared memory with same key as SHM_KEY
* Declare the condition and mutex and assign them the shared memory
address */
while(1)
{
pthread_mutex_lock(PTMutex);
pthread_cond_wait(PTCond, PTMutex);
pthread_mutex_unlock(PTMutex);
printf("From CP [%d]: Packets Received = %d\n",getpid(), Packets_Rx++);
}
}
int main(void)
{
int shmid, i;
pid_t l_pid;
char* SigBlock;
pthread_condattr_t condattr;
pthread_mutexattr_t mutexattr;
pthread_cond_t* PTCond;
pthread_mutex_t* PTMutex;
shmid = shmget(SHM_KEY, (sizeof(pthread_cond_t) + sizeof(pthread_mutex_t)), IPC_CREAT | 0666);
if(shmid < 0)
{
perror("shmget");
}
SigBlock = (char *)shmat(shmid, NULL, 0);
if(SigBlock == (char *) -1)
{
perror("shmat");
}
PTCond = (pthread_cond_t*) SigBlock;
PTMutex = (pthread_mutex_t*)(SigBlock + sizeof(pthread_cond_t));
pthread_condattr_init(&condattr);
pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(PTCond, &condattr);
pthread_condattr_destroy(&condattr);
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(PTMutex, &mutexattr);
pthread_mutexattr_destroy(&mutexattr);
for(i=0; i<NumOfChildProc; i++)
{
l_pid = fork();
if(l_pid == 0)
ChildProc();
}
sleep(1);
while(1)
{
/* Send pthread broadcast and increment the packets count */
printf("From Main Process : Packets Sent = %d\n", Packets_Tx++);
pthread_cond_broadcast(PTCond);
usleep(1000);
}
}
pthread_cond_broadcast() signals do not get "lost". Every thread which is waiting in a pthread_cond_wait() call at the point where the broadcast is sent will be woken - your problem is almost certainly that every thread is not waiting in the pthread_cond_wait() call at the point where pthead_cond_broadcast() is called - some threads may still be processing that last lot of data when the broadcast is sent, in which case they will "miss" the broadcast.
A pthread conditional variable should always be paired with a suitable condition (or predicate) over shared state, and a thread should only call pthread_cond_wait() after checking the state of that predicate.
For example, in your case you might have a shared variable which is the block number of the latest chunk to have arrived. In the main thread, it would increment this (while holding the mutex) before broadcasting the condition variable:
pthread_mutex_lock(&lock);
latest_block++;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&lock);
In the worker threads, each thread would keep track of the last block it has processed in a local variable, and check to see if another block has arrived before calling pthread_cond_wait():
pthread_mutex_lock(&lock);
while (latest_block <= my_last_block)
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
This will cause the worker to wait until the main thread has incremented latest_block to be greater than my_last_block (the last block that was processed by this worker).
Your example test code has the same problem - sooner or later the main thread will call pthread_cond_broadcast() when a child thread is locking or unlocking the mutex, or inside the printf() call.
A version of your example code, updated to use the fix I mentioned, does not show this problem:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <pthread.h>
#define SHM_KEY 9753
#define NumOfChildProc 20
int Packets_Tx = 0, Packets_Rx = 0;
struct {
pthread_cond_t PTCond;
pthread_mutex_t PTMutex;
int last_packet;
} *shared_data;
void ChildProc(void)
{
int my_last_packet = 0;
/* Create the shared memory with same key as SHM_KEY
* Declare the condition and mutex and assign them the shared memory
address */
while(1)
{
pthread_mutex_lock(&shared_data->PTMutex);
while (shared_data->last_packet <= my_last_packet)
pthread_cond_wait(&shared_data->PTCond, &shared_data->PTMutex);
pthread_mutex_unlock(&shared_data->PTMutex);
printf("From CP [%d]: Packets Received = %d\n",getpid(), Packets_Rx++);
my_last_packet++;
}
}
int main(void)
{
int shmid, i;
pid_t l_pid;
pthread_condattr_t condattr;
pthread_mutexattr_t mutexattr;
shmid = shmget(SHM_KEY, sizeof *shared_data, IPC_CREAT | 0666);
if(shmid < 0)
{
perror("shmget");
}
shared_data = shmat(shmid, NULL, 0);
if(shared_data == (void *) -1)
{
perror("shmat");
}
pthread_condattr_init(&condattr);
pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&shared_data->PTCond, &condattr);
pthread_condattr_destroy(&condattr);
pthread_mutexattr_init(&mutexattr);
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&shared_data->PTMutex, &mutexattr);
pthread_mutexattr_destroy(&mutexattr);
shared_data->last_packet = 0;
for(i=0; i<NumOfChildProc; i++)
{
l_pid = fork();
if(l_pid == 0)
ChildProc();
}
sleep(1);
while(1)
{
/* Send pthread broadcast and increment the packets count */
printf("From Main Process : Packets Sent = %d\n", Packets_Tx++);
pthread_mutex_lock(&shared_data->PTMutex);
shared_data->last_packet++;
pthread_cond_broadcast(&shared_data->PTCond);
pthread_mutex_unlock(&shared_data->PTMutex);
usleep(30);
}
}
Related
im trying to implement this:
Make a C multi-process program that does the following:
A process P generates two child processes P1 and P2. The two sons P1 and P2 perform an indeterminate cycle in which generate, each second, a random integer between 0 and 100. With each draw, the children communicate the numbers generated by the parent P process which provides for adding them, printing them on the screen and storing them in one file. Process P1 must handle the SIGINT interrupt signal. In particular, at the arrival of this signal P1 must display the warning message "P1 process busy!". The program is terminated by the parent P process when it verifies that the sum of the numbers, which it has received from the child processes, assumes the value 100.
Now, I need some help with the synchronization between childs and parent. Im trying to use semaphores but it looks like impossible. what can i use to synchronize them? signals? how?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#define READ 0
#define WRITE 1
void handler(int sig){
printf("process 1 is busy\n");
}
void codeprocess1(int pd[], sem_t *sem1){
int i = 0;
int numgenerated;
while( i = 0){
signal(SIGUSR1, handler);
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem1);
}
}
void codeprocess2(int pd[], sem_t *sem2){
int i = 0;
int numgenerated;
while( i = 0){
numgenerated = rand()%101;
close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(sem2);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
sem_t *sem2 = sem_open("semaph2", O_CREAT | O_EXCL, 1, 0);
sem_t *sem1 = sem_open("semaph1", O_CREAT | O_EXCL, 1, 0);
if(pipe(pipe1)<0){
exit(1);
}
if(pipe(pipe2)<0){
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1, sem1);
break;
default:
pid2= fork();
switch( pid2){
case -1:
exit(1);
case 0:
codeprocess2(pipe2, sem2);
break;
default:
while(sum!=1000){
close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
sum = sum + numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
I'm reporting here the relevant part of the man page of sem_overview(7):
POSIX semaphores come in two forms: named semaphores and unnamed sema‐
phores.
Named semaphores
A named semaphore is identified by a name of the form /somename;
that is, a null-terminated string of up to NAME_MAX-4 (i.e.,
251) characters consisting of an initial slash, followed by one
or more characters, none of which are slashes. Two processes
can operate on the same named semaphore by passing the same name
to sem_open(3).
The sem_open(3) function creates a new named semaphore or opens
an existing named semaphore. After the semaphore has been
opened, it can be operated on using sem_post(3) and sem_wait(3).
When a process has finished using the semaphore, it can use
sem_close(3) to close the semaphore. When all processes have
finished using the semaphore, it can be removed from the system
using sem_unlink(3).
Unnamed semaphores (memory-based semaphores)
An unnamed semaphore does not have a name. Instead the sema‐
phore is placed in a region of memory that is shared between
multiple threads (a thread-shared semaphore) or processes (a
process-shared semaphore). A thread-shared semaphore is placed
in an area of memory shared between the threads of a process,
for example, a global variable. A process-shared semaphore must
be placed in a shared memory region (e.g., a System V shared
memory segment created using shmget(2), or a POSIX shared memory
object built created using shm_open(3)).
Before being used, an unnamed semaphore must be initialized
using sem_init(3). It can then be operated on using sem_post(3)
and sem_wait(3). When the semaphore is no longer required, and
before the memory in which it is located is deallocated, the
semaphore should be destroyed using sem_destroy(3).
You are trying to use unnamed semaphores in standard memory. But they are meant to synchronize threads only, not processes.
I suggest to use either named semaphores (that should be easier) or unnamed semaphores backed by shared memory (get it with shmget() or shm_open(), then use it with sem_init() - the parent and the forked processes must use the same shared memory segment to have access to the inter-process semaphore).
In fact, in your code sem1 and sem2, initialized in the main process, won't be propagated to the forked processes: they have independent memory regions and addresses, and cannot be shared.
After the edit, regarding the semaphores there are many problems:
the most logically wrong: you cannot pass the pointer of one process to another process: the addresses are not shared. Every process must independently open the semaphore and use it with his own handler.
while (i=0)... ouch, try compiling with -Wall.
You wasn't checking the return code of sem_open() it was failing with errno=13 (EACCESS)
You wasn't properly setting the permission of the semaphore... it's a (sort of) file. Note that once you crete it with the wrong permissions, it stays there and it won't be possible to create it again with the same name (until you reboot the system). You can see them with: ls -l /dev/shm, and eventually just remove them with rm.
You was requesting O_EXCL, that is, exclusive access to one process, that's not what you want. See man 2 open.
the name of the semaphore must begin with /, see man sem_overview
Here is the revised code, some comments in-line:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <semaphore.h>
#include <fcntl.h>
#include <errno.h>
#define READ 0
#define WRITE 1
#define SEM1_NAME "/semaph_1a"
#define SEM2_NAME "/semaph_2a"
void handler(int sig) {
printf("process 1 is busy\n");
}
void codeprocess1(int pd[]) {
int i = 0;
int numgenerated;
// each process must open the handle to the same named semaphore.
// they cannot share a local memory address.
sem_t *my_sem = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
// the seed for the two children must be different or they will be generating the same
// sequence of random numbers.
srand(3333);
while(i == 0) {
signal(SIGUSR1, handler);
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
void codeprocess2(int pd[]){
int i = 0;
int numgenerated;
sem_t *my_sem = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (my_sem==SEM_FAILED) {
printf("semaphore creation failed, errno=%d\n", errno);
exit(1);
}
srand(1111);
while(i == 0) {
numgenerated = rand()%101;
// close(pd[READ]);
write(pd[WRITE], &numgenerated, sizeof(int));
sleep(1);
sem_wait(my_sem);
}
}
int main(){
pid_t pid1, pid2;
int sum, numread1, numread2, pipe1[2], pipe2[2];
// O_EXCL removed
// the mode flag must be set to 0777 for example, not "1".
// return value check added
sem_t *sem1 = sem_open(SEM1_NAME, O_CREAT , 0777, 0);
if (sem1==SEM_FAILED) {
printf("semaphore sem1 creation failed, errno=%d\n", errno);
exit(1);
}
sem_t *sem2 = sem_open(SEM2_NAME, O_CREAT, 0777, 0);
if (sem2==SEM_FAILED) {
printf("semaphore sem2 creation failed, errno=%d\n", errno);
exit(1);
}
if (pipe(pipe1) < 0 ) {
exit(1);
}
if (pipe(pipe2) < 0) {
exit(1);
}
pid1 = fork();
switch(pid1){
case -1:
exit(1);
case 0:
codeprocess1(pipe1);
break;
default:
pid2= fork();
switch( pid2) {
case -1:
exit(1);
case 0:
codeprocess2(pipe2);
break;
default:
// 100, not 1000
while (sum != 100) {
// all the "close()" calls are commented out
// close(pipe1[WRITE]);
read(pipe1[READ], &numread1, sizeof(int));
// close(pipe2[WRITE]);
read(pipe2[READ], &numread2, sizeof(int));
// sum must not be incremented
sum = numread1 + numread2;
printf("%d\n", sum);
sem_post(sem1);
sem_post(sem2);
}
kill(0, SIGKILL);
}
}
}
There is really a lot going on in your question.
As posted in the answer #Sigismondo, you are confusing multithreading with multiprocess programming. They have different method of communications.
To oversimplify threads share the same memory, so a thread can see for example values of global variables such as semaphores mutex and so on: if a thread modifies it, the other thread will be affected.
In multiprocessing when you fork(), a new process is generated with its own memory space. Right after the fork() variable values are almost the same (apart pid, ppid and so on) but they are in a different memory space: if you have a code block executed by only one process, modifying it will not affect the variables (the semaphores in your program) of the other process.
In your case: first of all if the children process do the same stuff (i.e. generate a random number) why do you have to different functions? Can't you do something like:
#include<stdlib.h>
int generateRand()
{
n = rand() % 100 + 1; //should be random in [1, 100]
}
HANDLING SIGNALS
Process P1 must handle the SIGINT interrupt signal. In particular, at
the arrival of this signal P1 must display the warning message "P1
process busy!". The program is terminated by the parent P process when
it verifies that the sum of the numbers, which it has received from
the child processes, assumes the value 100.
This is really unclear, in my opinion. The parent should catch the SIGINT signal. What should the children do? From what you say it seems they shouldn't catch that signal. In this case you must take a look at signal masks: basically you have to block the signal in the parent, the call the fork()s and then put back the original mask. Now you should go deeper but somehting like this (here)
sigset_t *parent_mask, *child_mask
//get the current mask
if (int res = sigprocmask (0, NULL, child_mask)<0)
printf("some error\n");
//make the mask block the signal
if (int res = sigaddset(child_mask, SIGINT)<0)
printf("some error in sigaddset \n");
// block the signal with the new mask
if (int res = sigprocmask (SIG_SETMASK, child_mask, parent_mask)<0)
printf("some error\n");
//do your forks: children will inherit the current mask and will not catch SIGINT
...
fork()
...
fork()
....
//set back the original mask so the parent catches SIGINT
if (int res = sigprocmask (SIG_SETMASK, parent_mask, NULL)<0)
printf("some error\n");
This answer of mine, although for multithreading should be a little clearer.
SIGNAL HANDLER
Why are you registering the signal handler in codeprocess1(int pd[])? I don't get it at all. And why SIGUSR1?
You should do it in the parent (before or after the fork()s shouldn't change since the signal is blocked for children: it depends if you want to have the user exit the program before starting the forks() or not: in the first case register the signal handler after the fork() otherwise put it at the beginning of main(). In both case you should do:
signal(SIGINT, handler);
Now the core to your program: to communicate your program you can use pipe() in a blocking way together with file descriptors: check here.
You need two file descriptors (one per child process and close the end (read/write) not used by the process).
Consider a single child process:
int p = fork();
int fd1[2]; //file descriptor for child1
int fd2[2]; //file descriptor for child2
if (p>0)//parent
{
close(fd1[1]);//close writing end
int n;
read(fd1[0], &n, sizeof(n));
//you might to call the other fork here and redo the same stuff
int p2 = fork();
if (p2>0)
{
close(fd2[1]);//close writing end
int n2;
read(fd2[0], &n2, sizeof(n2));
sum = n2+n1
if (sum==100 && exit = 1)
{
kill(p, SIGKILL);
kill(p2, SIGKILL);
}
}
}
else if(p==0)//child
{
close(fd1[0]);//close read end
int rand_n = generateRand();//or whaterver the name
wrote(fd1[1], &rand_n, sizeof(rand_n));
}
The exit condition is both based on the value of the sum (100) and the fact that CTRL+C has been pressed. The former is obvious in the code above. For the latter you can declare a global variable (I used exit) that if 0 CTRL+C has not been pressed, if 1 it has. This value is checked in the exit condition of the code above. Your handler will be responsible to write this variable:
//global variable here
int exit = 0;
void handler(int signo)
{
print("Parent busy doing stuff\n");
exit =1;
}
Note one thing exit is written by the parent since it is written ONLY in the handler which is called only by the parent and it is read in the part of the code executed only by the parent: the the children read its value it will be always 0 for them.
Being your question too general I tried to give some hints: there might be errors in my code since I haven't tried. You should study your own. If you will provide a minimal working example I will try to help.
I was asked a question in interview, how synchronization is done in shared memory. I told Take a struct. In that you have a flag and a data. Test the flag and change the data.
I took the following program from internet as below-. Can anyone tell if there is better way of synchronization in shared memory
#define NOT_READY -1
#define FILLED 0
#define TAKEN 1
struct Memory {
int status;
int data[4];
};
Assume that the server and client are in the current directory. The server uses ftok() to generate a key and uses it for requesting a shared memory. Before the shared memory is filled with data, status is set to NOT_READY. After the shared memory is filled, the server sets status to FILLED. Then, the server waits until status becomes TAKEN, meaning that the client has taken the data.
The following is the server program. Click here to download a copy of this server program server.c.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(int argc, char *argv[])
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
if (argc != 5) {
printf("Use: %s #1 #2 #3 #4\n", argv[0]);
exit(1);
}
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), IPC_CREAT | 0666);
if (ShmID < 0) {
printf("*** shmget error (server) ***\n");
exit(1);
}
printf("Server has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (server) ***\n");
exit(1);
}
printf("Server has attached the shared memory...\n");
ShmPTR->status = NOT_READY;
ShmPTR->data[0] = atoi(argv[1]);
ShmPTR->data[1] = atoi(argv[2]);
ShmPTR->data[2] = atoi(argv[3]);
ShmPTR->data[3] = atoi(argv[4]);
printf("Server has filled %d %d %d %d to shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = FILLED;
printf("Please start the client in another window...\n");
while (ShmPTR->status != TAKEN)
sleep(1);
printf("Server has detected the completion of its child...\n");
shmdt((void *) ShmPTR);
printf("Server has detached its shared memory...\n");
shmctl(ShmID, IPC_RMID, NULL);
printf("Server has removed its shared memory...\n");
printf("Server exits...\n");
exit(0);
}
The client part is similar to the server. It waits until status is FILLED. Then, the clients retrieves the data and sets status to TAKEN, informing the server that data have been taken. The following is the client program. Click here to download a copy of this server program client.c.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "shm-02.h"
void main(void)
{
key_t ShmKEY;
int ShmID;
struct Memory *ShmPTR;
ShmKEY = ftok(".", 'x');
ShmID = shmget(ShmKEY, sizeof(struct Memory), 0666);
if (ShmID < 0) {
printf("*** shmget error (client) ***\n");
exit(1);
}
printf(" Client has received a shared memory of four integers...\n");
ShmPTR = (struct Memory *) shmat(ShmID, NULL, 0);
if ((int) ShmPTR == -1) {
printf("*** shmat error (client) ***\n");
exit(1);
}
printf(" Client has attached the shared memory...\n");
while (ShmPTR->status != FILLED)
;
printf(" Client found the data is ready...\n");
printf(" Client found %d %d %d %d in shared memory...\n",
ShmPTR->data[0], ShmPTR->data[1],
ShmPTR->data[2], ShmPTR->data[3]);
ShmPTR->status = TAKEN;
printf(" Client has informed server data have been taken...\n");
shmdt((void *) ShmPTR);
printf(" Client has detached its shared memory...\n");
printf(" Client exits...\n");
exit(0);
}
Can anyone tell if there is better way of synchronization in shared memory?
Definitely, yes. I would say the way you waste CPU cycles in busy-wait (while (ShmPTR->status != FILLED) ;) is already a fatal mistake.
Note that POSIX shared memory has a much more sensible interface than the old SysV does. See man 7 shm_overview for details.
There are two distinct purposes for synchronization primitives:
Data synchronization
To protect data against concurrent modification, and to ensure each reader gets a consistent view of the data, there are three basic approaches:
Atomic access
Atomic access requires hardware support, and is typically only supported for native machine word sized units (32 or 64 bits).
Mutexes and condition variables
Mutexes are mutually exclusive locks. The idea is to grab the mutex before examining or modifying the value.
Condition variables are basically unordered queues for threads or processes to wait for a "condition". POSIX pthreads library includes facilities for atomically releasing a mutex and waiting on a condition variable. This makes waiting for a dataset to change trivial to implement, if each modifier signals or broadcasts on the condition variable after each modification.
Read-write locks.
An rwlock is a primitive that allows any number of concurrent "read locks", but only one "write lock" to be held on it at any time. The idea is that each reader grabs a read lock before examining the data, and each writer a write lock before modifying it. This works best when the data is more often examined than modified, and a mechanism for waiting for a change to occur is not needed.
Process synchronization
There are situations where threads and processes should wait (block) until some event has occurred. There are two most common primitives used for this:
Semaphores
A POSIX semaphore is basically an opaque nonnegative counter you initialize to whatever (zero or positive value, within the limits set by the implementation).
sem_wait() checks the counter. If it is nonzero, it decrements the counter and continues execution. If the counter is zero, it blocks until another thread/process calls sem_post() on the counter.
sem_post() increments the counter. It is one of the rare synchronization primitives you can use in a signal handler.
Barriers
A barrier is a synchronization primitive that blocks until there is a specific number of threads or processes blocking in the barrier, then releases them all at once.
Linux does not implement POSIX barriers (pthread_barrier_init(), pthread_barrier_wait(), pthread_barrier_destroy()), but you can easily achieve the same using a mutex, a counter (counting the number of additional processes needed to release all waiters), and a condition variable.
There are many better ways of implementing the said server-client pair (where shared memory contains a flag and some data).
For data integrity and change management, a mutex and one or two condition variables should be used. (If the server may change the data at any time, one condition variable (changed) suffices; if the server must wait until a client has read the data before modifying it, two are needed (changed and observed).)
Here is an example structure you could use to describe the shared memory segment:
#ifndef SHARED_H
#define SHARED_H
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct shared_data {
/* Shared memory data */
};
struct shared {
pthread_mutex_t lock;
pthread_cond_t change; /* Condition variable for clients waiting on data changes */
pthread_cond_t observe; /* Condition variable for server waiting on data observations */
unsigned long changed; /* Number of times data has been changed */
unsigned long observed; /* Number of times current data has been observed */
struct shared_data data;
};
/* Return the size of 'struct shared', rounded up to a multiple of page size. */
static inline size_t shared_size_page_aligned(void)
{
size_t page, size;
page = (size_t)sysconf(_SC_PAGESIZE);
size = sizeof (struct shared) + page - 1;
return size - (size % page);
}
#endif /* SHARED_H */
The changed and observed fields are counters, that help avoid any time-of-check-to-time-of-use race windows. It is important that before the shared memory is accessed the thread does pthread_mutex_lock(&(shared_memory->lock)), to ensure a consistent view of the data.
If a thread/process examines the data, it should do
shared_memory->observed++;
pthread_cond_broadcast(&(shared_memory->observe));
pthread_mutex_unlock(&(shared_memory->lock));
and if a thread/process modifies the data, it should do
shared_memory->modified++;
shared_memory->observed = 0;
pthread_cond_broadcast(&(shared_memory->change));
pthread_mutex_unlock(&(shared_memory->lock));
to notify any waiters and update the counters, when unlocking the mutex.
I'm doing a C application that reads and parses data from a set of sensors and, according to the readings of the senors, it turns on or off actuators.
For my application I will be using two threads, one to read and parse the data from the sensors and another one to act on the actuators. Obviously we may face the problem of one thread reading data from a certain variable while another one is trying to write on it. This is a sample code.
#include <pthread.h>
int sensor_values;
void* reads_from_sensor(){
//writes on sensor_values, while(1) loop
}
void* turns_on_or_off(){
//reads from sensor_values, while(1) loop
}
int main(){
pthread_t threads[2];
pthread_create(&threads[1],NULL,reads_from_sensor,NULL);
pthread_create(&threads[2],NULL,turns_on_or_off,NULL);
//code continues after
}
My question is how I can solve this issue, of a certain thread writing on a certain global variable while other thread is trying to read from it, at the same time. Thanks in advance.
OP wrote in the comments
The project is still in an alpha stage. I'll make sure I optimize it once it is done. #Pablo, the shared variable is sensor_values. reads_from_sensors write on it and turns_on_or_off reads from it.
...
sensor_value would be a float as it stores a value measured by a certain sensor. That value can either be voltage, temperature or humidity
In that case I'd use conditional variables using pthread_cond_wait and
pthread_cond_signal. With these functions you can synchronize threads
with each other.
The idea is that both threads get a pointer to a mutx, the condition variable
and the shared resource, whether you declared them a global or you pass them as
thread arguments, doesn't change the idea. In the code below I'm passing all
of these as thread arguments, because I don't like global variables.
The reading thread would lock the mutex and when it reads a new value of the
sensor, it writes the new value in the shared resource. Then it call
pthread_cond_signal to send a signal to the turning thread that a new value
arrived and that it can read from it.
The turning thread would also lock the mutex and execute pthread_cond_wait to
wait on the signal. The locking must be done in that way, because
pthread_cond_wait will release the lock and make the thread block until the
signal is sent:
man pthread_cond_wait
DESCRIPTION
The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. The application shall ensure that
these functions are called with mutex locked by the calling thread; otherwise, an error (for PTHREAD_MUTEX_ERRORCHECK and robust
mutexes) or undefined behavior (for other mutexes) results.
These functions atomically release mutex and cause the calling thread to block on the condition variable cond; atomically here means
atomically with respect to access by another thread to the mutex and then the condition variable. That is, if another thread is
able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or
pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
Example:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
struct thdata {
pthread_mutex_t *mutex;
pthread_cond_t *cond;
int *run;
float *sensor_value; // the shared resource
};
void *reads_from_sensors(void *tdata)
{
struct thdata *data = tdata;
int i = 0;
while(*data->run)
{
pthread_mutex_lock(data->mutex);
// read from sensor
*data->sensor_value = (rand() % 2000 - 1000) / 10.0;
// just for testing, send a singnal only every
// 3 reads
if((++i % 3) == 0)
{
printf("read: value == %f, sending signal\n", *data->sensor_value);
pthread_cond_signal(data->cond);
}
pthread_mutex_unlock(data->mutex);
sleep(1);
}
// sending signal so that other thread can
// exit
pthread_mutex_lock(data->mutex);
pthread_cond_signal(data->cond);
pthread_mutex_unlock(data->mutex);
puts("read: bye");
pthread_exit(NULL);
}
void *turns_on_or_off (void *tdata)
{
struct thdata *data = tdata;
while(*data->run)
{
pthread_mutex_lock(data->mutex);
pthread_cond_wait(data->cond, data->mutex);
printf("turns: value read: %f\n\n", *data->sensor_value);
pthread_mutex_unlock(data->mutex);
usleep(1000);
}
puts("turns: bye");
pthread_exit(NULL);
}
int main(void)
{
srand(time(NULL));
struct thdata thd[2];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// controlling vars
int run_rfs = 1;
int run_tof = 1;
float sensor_value;
thd[0].run = &run_rfs;
thd[1].run = &run_tof;
thd[0].mutex = &mutex;
thd[1].mutex = &mutex;
thd[0].cond = &cond;
thd[1].cond = &cond;
thd[0].sensor_value = &sensor_value;
thd[1].sensor_value = &sensor_value;
pthread_t th[2];
printf("Press ENTER to exit...\n");
pthread_create(th, NULL, reads_from_sensors, thd);
pthread_create(th + 1, NULL, turns_on_or_off, thd + 1);
getchar();
puts("Stopping threads...");
run_rfs = 0;
run_tof = 0;
pthread_join(th[0], NULL);
pthread_join(th[1], NULL);
return 0;
}
Output:
$ ./a
Press ENTER to exit...
read: value == -99.500000, sending signal
turns: value read: -99.500000
read: value == -25.200001, sending signal
turns: value read: -25.200001
read: value == 53.799999, sending signal
turns: value read: 53.799999
read: value == 20.400000, sending signal
turns: value read: 20.400000
Stopping threads...
read: bye
turns: value read: 20.400000
turns: bye
Note that in the example I only send the signal every 3 seconds (and do a long
sleep(1)) for testing purposes, otherwise the terminal would overflow immediately
and you would have a hard time reading the output.
See also: understanding of pthread_cond_wait() and pthread_cond_signal()
Your question is too generic. There are different multithread synchronization methods mutex, reader-writer locks, conditional variables and so on.
The easiest and most simple are mutex (mutual excluasion). They are pthread_mutex_t type variables. You first need to initialize them; you can do it in two ways:
assigning to the mutex variable the constant value PTHREAD_MUTEX_INITIALIZER
calling the funtion pthread_mutex_init
Then before reading or writing a shared variable you call the function int pthread_mutex_lock(pthread_mutex_t *mutex); and after exited the critical section you must release the critical section by calling int pthread_mutex_unlock(pthread_mutex_t *mutex);.
If the resource is busy the lock will block the execution of your code until it gets released. If you want to avoid that take a look at int pthread_mutex_trylock(pthread_mutex_t *mutex);.
If your program has much more reads than writes on the same shared variable, take a look at the Reader-Writer locks.
I am writing a program that will transfer 50 integers from one thread to another using shared memory and upon receiving the integers the receiver thread will print them.
The code compiles without any errors or warnings but when I run it I get the following output:
pthread_create() for thread 1 returns: 0
pthread_create() for thread 2 returns: 0
Segmentation fault (core dumped)
Here is my code so far:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/msg.h>
#include <string.h>
//define declarations
#define SIZE 50
//function declarations
void *send_data();
void *receive_data();
int main()
{
pthread_t thread1;
pthread_t thread2;
int ret_val_t1;
int ret_val_t2;
//create thread1
ret_val_t1 = pthread_create( &thread1, NULL, send_data, NULL);
if(ret_val_t1)
{
fprintf(stderr,"Error - pthread_create() return value: %d\n",ret_val_t1);
exit(EXIT_FAILURE);
}
//create thread2
ret_val_t2 = pthread_create( &thread2, NULL, receive_data, NULL);
if(ret_val_t2)
{
fprintf(stderr,"Error - pthread_create() return value: %d\n",ret_val_t2);
exit(EXIT_FAILURE);
}
printf("pthread_create() for thread 1 returns: %d\n",ret_val_t1);
printf("pthread_create() for thread 2 returns: %d\n",ret_val_t2);
//wait untill threads are done with their routines before continuing with main thread
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
exit(EXIT_SUCCESS);
}
void *send_data(){
int shmid;
int *array;
int i = 0;
int SizeMem;
key_t key = 12345;
SizeMem = sizeof(*array)*SIZE;
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT);
array = (int *)shmat(shmid, 0, 0);
for(i=0; i<SIZE; i++)
{
array[i] = i;
}
for(i=0; i<SIZE; i++)
{
printf("\n%d---\n", array[i]);
}
printf("\nWritting to memory succesful--\n");
shmdt((void *) array);
}
void *receive_data(){
int shmid;
int *array;
int i = 0;
key_t key = 12345;
shmid = shmget(key, SIZE*sizeof(int), IPC_EXCL);
array = shmat(shmid, 0, SHM_RDONLY);
for(i=0; i<SIZE; i++)
{
printf("\n%d---\n", array[i]);
}
printf("\nRead to memory succesful--\n");
shmdt((void *) array);
}
Use the ipcs utility and look at the output from ipcs -m
Per the shmget man page:
SYNOPSIS top
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
...
In addition to the above flags, the least significant 9 bits of
shmflg specify the permissions granted to the owner, group, and
others. These bits have the same format, and the same meaning, as
the mode argument of open(2). Presently, execute permissions are not
used by the system.
In line of code
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT);
the "the least significant 9 bits of shmflg" are set to zero.
No one has permissions to read/write your shared memory segment.
This would be better:
shmid = shmget(key, SIZE*sizeof(int), IPC_CREAT | 0600 );
That would give the owning user read/write permission.
Here in this situation, shared memory is a costly affair. And you are trying to access from the same process, there is no benefit here.
The better way to handle your situation is:
Threads of a parent process, will have access to parent process heap memory. This is simple concept to use. Create a heap object for the parent process and share across your threads.
As said in many comments, shared memory is one of the "Inter Process Communication" communication concept, like pipes or named pipes or message queue etc....
If you are using for practice, then it is OK. But as apart of PROD use, this is a costly affair.
If you do not apply locks and access restriction to your mapped/shared memory properly, and you are not behind a secured firewall, then you are welcoming attackers.
One race condition arises from your code. Your second thread can get cpu first and try to use a non yet created memory segment. As it doesn't exist, and as you don't check for errors, you can get a SIGSEGV from trying to access memory at NULL address.
You have to put synchronization devices that forbid the second thread to get to the shared memory access before it has been created and filled with data. Something like.
pthread_mutex_t lock;
pthread_cond_t shm_created_and_filled;
then in main you initialize the lock and shm_created_and_filled.
in the writing thread you will create the shared memory segment and write the numbers, finally locking and signalling the shm_created_and_filled condition variable. In the reading thread you first lock the lock variable and then wait for the shm_created_and_filled condition. There's still a little race condition in which the first thread runs and signals the shm_created_and_filled condition before the reading thread waits for it (it will lose the condition) but I have left it as an exercise for the reader.
I'm starting my studies with syncronzed threads using semaphore.
I just did a test using binary semaphore (2 threads only) and it's all good.
Imagine a lanhouse, that have 3 computers (threads) and some clients (Threads). If all computers are bussy, the client will wait in a queue with a know limit (e.g 15 clients).
I can't understand how threads will relate to each other.
As far as I know, semaphore is used to control the access of the threads to a certain critical region / memory area / global variable.
1) Create 1 semaphore to control the Clients accessing the computers (but both are threads);
2) Create 1 semaphore to control the clients in queue;
But how relate threads with threads ? How the semaphore will know which thread(s) it should work with.
I don't need a full answer for it. I just need to understand how the Threads will relate to eachother. Some help to understand the situation.
This is my code so far and it's not working ;P can't control the clients to access the 3 computers avaliable.
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ROOM_SIZE 15
sem_t queue, pc, mutex;
int room [ROOM_SIZE];
int pcsAvaliable = 3, nAvaliable = 0, roomAvaliable = ROOM_SIZE;
int computers [3]; // 0 -> Empty | 1 -> Ocuppied
void* Lan(void* arg)
{
//Enter the lanhouse
//Verify if there is a computer avaliable
sem_wait(&pc);
if(pcsAvaliable > 0)
{
sem_wait(&mutex);
pcsAvaliable--;
computers[nAvaliable] = 1;
printf("Cliente pegou pc: %d\n", nAvaliable);
nAvaliable++;
sem_post(&mutex);
//Wait for 80~90ms
printf("Client liberou pc: %d\n", nAvaliable);
computers[nAvaliable] = 0;
nAvaliable--;
sem_post(&pc);
}
else
{
printf("No computer avaliable...\n");
//Check the waiting room for avaliable slot
if(roomAvaliable > 0)
{
roomAvaliable--;
printf("Client entered the waiting room.");
}
else
printf("No avaliable space in waiting room..\n");
}
}
int main(int argc, char const *argv[])
{
int i;
if(argc > 1)
{
int numClients = atoi(argv[1]);
sem_init(&pc, 0, 3);
sem_init(&mutex, 0, 1);
pthread_t clients[numClients];
//Create Clients
for(i=0; i< numClients; i++)
{
pthread_create(&clients[i], NULL, Lan, NULL);
}
//Join Clients
for(i=0; i< numClients; i++)
{
pthread_join(clients[i], NULL);
}
}
else
printf("Please, insert a parameter.");
pthread_exit(NULL);
sem_destroy(&pc);
return 0;
}
If you're going to be technical, if you're syncing tasks between threads you should use Semaphore. Example reading input before parsing it.
Here's an answer on semaphores.
But if you're using shared resources, and need to avoid race condition/two threads accesing at the same time, you should use mutexes. Here's a question on what is a mutex.
Also look at the disambiguation by Michael Barr which is a really good.
I would read both question thoroughly and the disambiguation, and you might actually end up not using semaphore and just mutexes since from what you explained you're only controlling a shared resource.
Common semaphore function
int sem_init(sem_t *sem, int pshared, unsigned int value); //Use pshared with 0, starts the semaphore with a given value
int sem_wait(sem_t *sem);//decreases the value of a semaphore, if it's in 0 it waits until it's increased
int sem_post(sem_t *sem);//increases the semaphore by 1
int sem_getvalue(sem_t *sem, int *valp);// returns in valp the value of the semaphore the returned int is error control
int sem_destroy(sem_t *sem);//destroys a semaphore created with sim_init
Common Mutexes functions (for linux not sure what O.S. you're running on)
int pthread_mutex_init(pthread_mutex_t *p_mutex, const pthread_mutexattr_t *attr); //starts mutex pointed by p_mutex, use attr NULL for simple use
int pthread_mutex_lock(pthread_mutex_t *p_mutex); //locks the mutex
int pthread_mutex_unlock(pthread_mutex_t *p_mutex); //unlocks the mutex
int pthread_mutex_destroy(pthread_mutex_t *p_mutex);//destroys the mutex
You can treat computers as resources. The data structure for the resource can be initialized by the main thread. Then, there can be client threads trying to acquire an instance of resource (a computer). You can use a counting semaphore with a value 3 for the number of computers. To acquire a computer, a client thread does
P (computer_sem).
Similarly to release the client thread has to do,
V (computer_sem)
For more information on threads and semaphore usage, refer
POSIX Threads Synchronization in C.