Synchronization between childs and parent processes c - c

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.

Related

Keeping parent process alive while child process will be terminated by SIGINT signal

As I dig deep into SIGNAL in c, I was wondering is it possible to keep parent process alive upon receiving SIGINT signal but I got a bit confused researching online as they isn't much discussion about it.
Is it possible to use signal handler to keep parent process alive by ignoring the SIGINT signal for parent process.
If yes, how should I implement it?
I would say, that there is nothing to discuss.
Have a look at the man page of signal(7). In the section Standard signals, the default action for SIGINT is program termination. That means, if you do not handle the specified signal, the kernel takes the default action, therefore, if you want to keep the process alive, you have to catch the signal.
To answer your question, read the provided man page.
A process can change the disposition of a signal using sigaction(2) or signal(2).
#Erdal Küçük has already answered your question but here is a sample piece of code so you can understand it better.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handler(int _) {
(void)_;
printf("\nEnter a number: ");
ffush(stdout);
}
int main(void) {
pid_t pid;
pid = fork();
int n = 0;
if (pid < 0) {
perror("Can't fork");
} else if (pid == 0) {
// Child process
kill(getpid(), SIGKILL); // Killing the child process as we don't need it
} else {
// Parent process
struct sigaction sg;
sg.sa_flags = SA_RESTART;
sg.sa_handler = handler;
sigaction(SIGINT, &sg, NULL);
printf("Enter a number: ");
scanf("%d", &n);
}
printf("Value of n = %d", n);
return 0;
}

Share POSIX semaphore among multiple processes

I need to create two child processes each of which calls execvp ater being forked, and the executables share POSIX semaphores between them.
Do I need to create a shared memory or just implement named semaphores?
I got two answers from the following links:
Do forked child processes use the same semaphore?
How to share semaphores between processes using shared memory
But I am confused about how to proceed in the implementation.
Do I need to create a shared memory or just implement named
semaphores?
Either approach will work. Pick one and go with it - though I personally prefer named semaphores because you don't have to deal with memory allocation and with setting up the shared memory segments. The interface for creating and using named semaphores is way more friendly, in my opinion.
With named semaphores, in your example scenario, here's what happens:
You create and initialize the semaphore in the parent process with sem_open(3). Give it a well-known name that the child processes will know; this name is used to find the semaphore in the system.
Close the semaphore in the parent, since it won't be using it.
Fork and execute
Unlink the semaphore with sem_unlink(3). This must be done exactly once; it doesn't really matter where (any process that has a reference to the semaphore object can do it). It is ok to unlink a semaphore if other processes still have it open: the semaphore is destroyed only when all other processes have closed it, but keep in mind that the name is removed immediately, so new processes won't be able to find and open the semaphore.
The child processes call sem_open(3) with the well-known name to find and obtain a reference to the semaphore. Once a process is done with the semaphore, you need to close it with sem_close(3).
Below is an example of what I just described. A parent process creates a named semaphore, and forks + executes 2 child processes, each of which finds and opens the semaphore, using it to synchronize between each other.
It assumes that the parent forks and executes the ./sem_chld binary. Keep in mind that a name for a semaphore must begin with a forward slash, followed by one or more characters that are not a slash (see man sem_overview). In this example, the semaphore's name is /semaphore_example.
Here's the code for the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SEM_NAME "/semaphore_example"
#define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define INITIAL_VALUE 1
#define CHILD_PROGRAM "./sem_chld"
int main(void) {
/* We initialize the semaphore counter to 1 (INITIAL_VALUE) */
sem_t *semaphore = sem_open(SEM_NAME, O_CREAT | O_EXCL, SEM_PERMS, INITIAL_VALUE);
if (semaphore == SEM_FAILED) {
perror("sem_open(3) error");
exit(EXIT_FAILURE);
}
/* Close the semaphore as we won't be using it in the parent process */
if (sem_close(semaphore) < 0) {
perror("sem_close(3) failed");
/* We ignore possible sem_unlink(3) errors here */
sem_unlink(SEM_NAME);
exit(EXIT_FAILURE);
}
pid_t pids[2];
size_t i;
for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++) {
if ((pids[i] = fork()) < 0) {
perror("fork(2) failed");
exit(EXIT_FAILURE);
}
if (pids[i] == 0) {
if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) {
perror("execl(2) failed");
exit(EXIT_FAILURE);
}
}
}
for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++)
if (waitpid(pids[i], NULL, 0) < 0)
perror("waitpid(2) failed");
if (sem_unlink(SEM_NAME) < 0)
perror("sem_unlink(3) failed");
return 0;
}
Note that sem_unlink(3) is called after both children terminate; although this is not required, if it was called before there would be a race condition between the parent process unlinking the semaphore and both child processes starting up and opening the semaphore. In general, though, you can unlink as soon as you know that all required processes have opened the semaphore and no new processes will need to find it.
Here's the code for sem_chld, it's just a small toy program to show the usage of a shared semaphore:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define SEM_NAME "/semaphore_example"
#define ITERS 10
int main(void) {
sem_t *semaphore = sem_open(SEM_NAME, O_RDWR);
if (semaphore == SEM_FAILED) {
perror("sem_open(3) failed");
exit(EXIT_FAILURE);
}
int i;
for (i = 0; i < ITERS; i++) {
if (sem_wait(semaphore) < 0) {
perror("sem_wait(3) failed on child");
continue;
}
printf("PID %ld acquired semaphore\n", (long) getpid());
if (sem_post(semaphore) < 0) {
perror("sem_post(3) error on child");
}
sleep(1);
}
if (sem_close(semaphore) < 0)
perror("sem_close(3) failed");
return 0;
}
You can eliminate the need to keep the semaphore name synchronized between the two source files by defining it in a common header file and including it in the code for each program.
Note that error handling is not ideal in this example (it's merely illustrative), there is a lot of room for improvement. It's just there to make sure you don't forget to do proper error handling when you decide to change this example to suit your needs.
Shared memory approach will also work here, only thing here is parent process has to initialise the shared memory.
There seems one bug in this code instead of two child, parent function will fork 3 child process here. There should be break statement inside
if (pids[i] == 0) {
if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) {
perror("execl(2) failed");
exit(EXIT_FAILURE);
}
break; //this is required
}

Pthreads signals getting lost when used as IPC inLinux

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);
}
}

posix semaphore trouble

I supposed that my program should work like this:
1) initializing unnamed semaphore with value = 0
the second value for sem_init(..) is 1 so as it said in MAN the semaphore is shared between processes
2) creating child, child waits until semaphore value becomes 1
parent process increases the value of semaphore so the child should exit now
but it doesn't exit really, so that is the problem
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
pid_t child;
void child_proc(sem_t* sem) {
sem_wait(sem);
printf("OK\n");
}
void parent_proc(sem_t* sem) {
sem_post(sem);
sleep(2);
int status;
waitpid(child, &status, 0);
}
int main(int argc, char* argv[]) {
sem_t sem;
sem_init(&sem, 1, 0);
child = fork();
if (0 == child) {
child_proc(&sem);
return 0;
}
parent_proc(&sem);
return 0;
}
The problem is that both processes have a local (not shared) copy of the semaphore structure and changes in one process won't reflect to the other process.
As the man page also says, if you want to share semaphores across processes, not only do you need to pass a non-zero value to the second argument of sem_init, but the sem_t structure also needs to exist in an area of shared memory. In your example program, it exists on the stack, which is not shared.
You can have shared memory by using a common file mapping (with mmap) or with shm_open, notably.

What happens when a process enters a semaphore (critical section) and sleeps?

As per my understanding, when a process enters a critical section, no other process can simultaneously enter. But i see, by a program, that it is not.
I create Process A, and child Process B.
Child enters critical section, and sleeps, meanwhile i am surprised to see that parent too enters critical section, while child sleeps. How is it possible? 2 processes simultaneously at critical section?
enter code here
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
sem_t sem;
int shared=0;
int pid;
void func()
{
sem_trywait(&sem);
if(pid==0)printf("Child entered\n");
else if(pid>0)printf("Parent entered\n");
sleep(2);
shared++;
sem_post(&sem);
if(pid==0)printf("Child exited\n");
else if(pid>0)printf("Parent exited\n");
}
int main()
{
pid=fork();
sem_init(&sem,1,0);
if(pid==0){
printf("In child\n");
func();
}
else {
func();
}
}
Output:
[root#dhcppc0 semaphore]# gcc semaphore1.c -lrt
[root#dhcppc0 semaphore]# ./a.out
In child
Child entered
Parent entered
<pause 2 secs>
Child exited
Parent exited
For a semaphore to work across processes, it needs to reside in shared memory and to be initialized with pshared==1 - You're not putting the semaphore in shared memory. Look up e.g. shm_open or mmap.
You should also initialize the semaphore before you fork() - initializing a sempahore twice doesn't work. Also use sem_wait rather than sem_trywait as you seem to want to block on the semaphore. If you want sem_trywait at least check if the try part succeeded.
EDIT: Corrected source.
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
sem_t * sem; /* MODIFIED: We want a semaphore in shared memory, using a pointer instead */
int shared=0;
int pid;
void func()
{
sem_wait(sem); /* MODIFIED &sem to sem */
if(pid==0)printf("Child entered\n");
else if(pid>0)printf("Parent entered\n");
sleep(2);
shared++;
sem_post(sem); /* MODIFIED &sem to sem */
if(pid==0)printf("Child exited\n");
else if(pid>0)printf("Parent exited\n");
}
int main()
{
/* MODIFIED: Put semaphore in shared memory */
sem = mmap(0, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
/* MODIFIED: Initial count of 1, so that a sem_wait will succeed */
sem_init(sem,1,1);
/* MODIFIED: fork() after sem_init() */
pid=fork();
if(pid==0){
printf("In child\n");
func();
}
else {
func();
}
}
And check the return values of the sem_* functions! From the man page:
The sem_trywait() function shall lock
the semaphore referenced by sem only
if the semaphore is currently not
locked; that is, if the semaphore
value is currently positive.
Otherwise, it shall not lock the
semaphore.
So if you don't check what it returns, you don't know if you've locked anything at all.
You are using sem_trywait function,then you should check the value returned by this call so as to ensure sysnchronization...
Can refer this for more help....
Hope this helps...

Resources