Make fork execute equal amounts of times, infinitely - c

I am trying to make the following code never allow the amount of + to be greater than the amounts of - . I am thinking about adding a simple line of just c sleep(1) but I was curious to see if there is a better way to make the amounts equal. This is supposed to stay an infinite loop.
int main(){
if(fork() ==0)
while(1){
write(1,"-",1)
}
else
while(1){
write(1, "+",1);
}
return 0;
}
Would this function correctly using semaphores?
int main(){
int parent_sem = get_semaphore(0);
int child_sem = get_semaphore(1);
if(fork() ==0)
while(1){
sem_wait(child_sem);
write(1,"-",1);
sem_signal(parent_sem);
}
else
while(1){
sem_wait(parent_sem);
write(1, "+",1);
sem_signal(child_sem);
}
return 0;
}

Here's a fixed version of your semaphore example. I have allocated shared memory so that the semaphores can be accessed by both processes across the fork (I forgot this was necessary, sorry) and fixed the semaphore types and calls.
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
int main() {
// Create shared memory for two semaphores
sem_t* sem = mmap(NULL, 2 * sizeof(sem_t), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
// Initialise parent and child semaphores: parent = 1, child = 0
sem_t* parent_sem = sem;
sem_t* child_sem = sem + 1;
sem_init(parent_sem, 1, 1);
sem_init(child_sem, 1, 0);
if (fork() == 0) {
while(1) {
// Child process
sem_wait(child_sem);
write(1, "-", 1);
sem_post(parent_sem);
}
} else {
while(1) {
// Parent process
sem_wait(parent_sem);
write(1, "+", 1);
sem_post(child_sem);
}
}
return 0;
}

This is close (Use signals):
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void usr1handler() {
}
int main(void) {
int pid;
signal(SIGUSR1, usr1handler);
if ((pid = fork()) == 0) {
while (1) {
pause();
write(1, "-", 1);
fflush(stdout);
kill(getppid(), SIGUSR1);
}
}
else {
sleep(1);
while (1) {
write(1, "+", 1);
fflush(stdout);
kill(pid, SIGUSR1);
pause();
}
}
}
The main issue for this is the race condition. If a process makes it to the kill() function before the other makes it to the pause(), the program freezes. This is probably good enough to start you off though. I left a single sleep in there so you could see that it does indeed print evenly.

Related

How to create a waiting process (worker) with fork?

I don't know if I'm asking the right question, but after several hours of searching, I can't find my answer. So I explain what I need.
I would like to create a child process with fork and that child process will wait until it receives a signal or changes a variable value. I mean I don't want to be creating a process every time I need it, I want it to wait and run when I tell it to, as many times as it needs to.
I have tried several things, but they have not worked, I put the code, I really do not know what to look for to do what I need without thread, or is it only possible to do it with thread?
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
volatile sig_atomic_t runChild = 0;
static void handler(int signum, siginfo_t *sinfo, void *unused){
if (signum == SIGUSR1) {
printf("SIGUSR1 received\n");
runChild = 1;
}
}
int main(int argc, char *argv[]) {
char *newArgv[] = {"myecho", "hello", "world", NULL};
char *newEnviron[] = { "MY_NAME=jcarlosweb", NULL };
struct sigaction sa;
sa.sa_flags =0;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
while (!runChild) {
usleep(200);
}
runChild = 0;
execve("./process-pipe-fork/execve/myecho", newArgv, newEnviron);
/* The execve function returns only if an error occurs. */
perror("An error occurred in execve");
abort();
}
// continue execution of parent process without waiting for child process to finish
waitpid(-1, NULL, WNOHANG);
printf("Run myecho:\n");
kill(pid,SIGUSR1);
sleep(3);
printf("Run myecho:\n");
kill(pid,SIGUSR1);
printf("Done\n");
exit(EXIT_SUCCESS);
}
The execve of my myecho program only runs once, and I would like to run it when I say so throughout the main program.
In this case I'm using the exec in the child process, but I could have just put a function that prints a message, for example. The idea I am looking for is the same
Block on a pipe. This might look something like...
#include <err.h>
#include <errno.h>
#include <unistd.h>
int main(void) {
char ch;
int fd[2];
pid_t pid;
ssize_t ret;
pipe(fd); /* to block on */
pid = fork();
if (pid < 0) {
err(1, "fork failed");
} else if (pid == 0) { /* child */
close(fd[1]);
warnx("child %d start", getpid());
/* block until parent goes away */
ret = read(fd[0], &ch, 1);
warnx("child %d is go", getpid());
close(fd[0]);
/* child now does things here ... */
} else { /* parent */
sleep(3);
close(fd[0]);
close(fd[1]);
/* parent does more things here ... */
}
return 0;
}
You cannot wait on a child process for a variable change because the parent process and the child are different processes, and so, they have each their own virtual address space (with no intersections)
There are several ways to do what you want. One has been suggested in another answer, so I will not extend on it, as it looks fine to me.
You can create a thread. A thread runs in a separate stack (separate from main thread) but the whole virtual address space is the same. Both running threads (main and the one you create) share the same addresses and so, one can modify a variable and the other can be looking at it for a change. This solution is feasible, but not very efficient, as while the waiting process is waiting for the variable to change (the variable needs to be defined as volatile, so the compiler doesn't cache the value anywhere and the true value is the one the other thread stores in it) it is consuming cpu making your resources wasted for no reason.
You can use (in a different process) a semaphore, (man semop) for the parent process to change it.
You can use shared memory between processes (see man shmop or man mmap) and so, only the memory corresopnding to the shared memory segment is shared between both processes.
You can use a socket. A socket is a bidirectional communication channel between parent and child, and so it allows you to use a complex protocol to do the most variable communication messages between both processes. You can use reliable unix sockets for this (they are fast, reliable, but require both processes to be in the same machine) or network sockets (the API is the same, which gives you a versatile approach that you can later change in case your needs change)
I recommend you to read:
pthread_create(3) and similar manual pages. A reference of threads.
pthread_mutex(3) and similar manual pages. A reference to synchronization between threads.
semop(2) and friends. Reference to sysv5 semaphores.
mmap(2) and friends. Reference to shared memory segments.
pipe(2) and fifo related documentation. This will show you how a pipe can be used, as it blocks readers and writers when the internal buffer it has is full/empty. This can be a good starting point.
The following code fork()s a child and then it feeds its child with all its own input, while the parent is writing to standard output its own input, it also feeds to the child its input, so the child will work on it and write to a file output.txt:
###pru.c
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd[2];
/* we need to get the pipe before fork() so we have it in both,
* parent and child. */
int res = pipe(fd);
if (res < 0) {
perror("pipe");
exit(1);
}
int child_pid = fork();
if (child_pid < 0) {
perror("fork");
exit(1);
}
/* both processes get here. */
if (child_pid == 0) { /* we are the child only */
close(fd[1]); /* we are not using the writing end */
FILE *out = fopen("output.txt", "a+");
FILE *f = fdopen(fd[0], "r");
int c;
while ((c = fgetc(f)) != EOF) {
/* print an hex representation of char */
fprintf(out, "<%02x>", c);
}
/* let's close both */
fclose(out); fclose(f);
/* remember the child gets 0 from fork, so it needs to
* call getpid() syscall to get its pid. */
printf("Child (pid=%d) exiting.\n", getpid());
exit(0);
/* NOTREACHED */
} else {
close(fd[0]); /* we don't use the reading end */
/* we are writing on the writing side of the pipe */
FILE *out = fdopen(fd[1], "w");
int c;
while ((c = fgetc(stdin)) != EOF) {
/* make a copy for the child */
fputc(c, out);
/* and write to stdout */
fputc(c, stdout);
}
fclose(out); /* signal the child that there's no more output */
fprintf(stderr,
"Waiting for the child (whose pid should be %d)"
" to finish\n",
child_pid);
int status;
pid_t exited_pid = wait(&status);
if (exited_pid < 0) {
perror("wait");
exit(1);
}
fprintf(stderr,
"%d exited with status %d\n",
exited_pid, status);
exit(0);
/* NOTREACHED */
} /* else */
} /* main */
whose output is:
$ echo "Hello, world" | ./a.out
Hello, world
Waiting for the child (whose pid should be 2833) to finish
Child (pid=2833) exiting.
2833 exited with status 0
$ _
and the contents of output.txt:
$ cat output.txt
<48><65><6c><6c><6f><2c><20><77><6f><72><6c><64><0a>$ _
I suppose your complaint is that myecho runs after the first kill(pid, SIGUSR1) but not the second time? But as you said yourself, execve doesn't return if it succeeds!
Upon successful execve the child process is no longer running your program at all, it's running the myecho program, which doesn't handle signals, and when myecho calls _exit() the process exits. So by the time the parent calls kill(pid, SIGUSR1) the second time, your child process is a zombie. The second kill won't fail, but it also won't have any effect; a zombie can't respond to signals.
If you want the child to stick around and keep responding to signals, then the child cannot call execve itself to run myecho. Instead, each time the signal is received, the child must fork again, creating a grandchild process to do the exec. The original child doesn't call execve but just returns to its sleep loop to wait for another signal.
As a side remark, I think you've misunderstood the role of waitpid. Your waitpid(-1, NULL, WNOHANG); effectively does nothing at all. It would be used to test whether the child had already exited and reap it if so, immediately returning 0 if yes and -1 if no, but you ignore the return value. You don't need any system call for the parent and child to run independently; that's what happens by default when you fork. You call waitpid or one of its relatives when you want the parent to wait instead of going on running. I think you may have misunderstood something in the documentation.
Which raises the question of who is going to clean up all those zombies. They will stay around and pollute the process table until their parent calls waitpid. You could have the child process, on every wakeup, call waitpid(WNOHANG) in a loop until it returns -1 to reap all grandchildren that have exited. Or you could use a SIGCHLD handler. Likewise, the parent should really be arranging somehow for the child to terminate before the parent does (kill it with another signal, for instance), and then the parent should waitpid for the child, without WNOHANG, before it itself exits.
Finally, instead of usleep in a loop, the preferred way to wait for a signal is with sigwait(). In that case you don't actually need the runChild flag. You still have to install a signal handler for SIGUSR1 but it can just do nothing.
I can try to come back later and post a fixed version of your program as an example.
Thanks to the answers of Nate Eldredge and Luis Colorado I have been able to create 3 examples. I'm going to post them in case it helps other people, I've really done this as an exercise to learn C, but these the examples are nothing without the help of Nate Eldredge and Luis Colorado. I also did another example with sigwait, but I've seen that with Semaphore it's simpler.
1 - Posix Semaphore:
struct sigaction sa; // signal
short int *runChildProcess; // mmap shared memory
sem_t *sem; // semaphore
static void handleChildTerm(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(pid_t pid) {
if (sem_post(sem) == -1) {
perror("sem_post run child");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid) {
*runChildProcess = 0; // set to 0 to exit the loop
runChild(pid);
if(munmap(runChildProcess, sizeof *runChildProcess) == -1) {
perror("Munmap");
exit(EXIT_FAILURE);
}
if (sem_destroy(sem) == -1) {
perror("sem_destroy");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1;
// semaphore
sem = mmap(0, sizeof(*sem), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sem_init(sem, 1 /*shared*/, 0 /*init val*/) == -1) {
perror("sem_init");
exit(EXIT_FAILURE);
}
// signal handler for SIGCHLD (exit child process)
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handleChildTerm;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
while (*runChildProcess) {
sem_wait(sem);
if (*runChildProcess == 0) {
break;
}
printf("Child process started: %d\n", getpid());
}
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
runChild(pid);
runChild(pid);
runChild(pid);
termChild(pid);
printf("Done\n");
exit(EXIT_SUCCESS);
}
2 - Pipes
#include <errno.h> // for errno
#include <signal.h> // sigaction
#include <stdbool.h> // bool
#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <string.h> // strlen
#include <sys/mman.h> // mmap
#include <sys/types.h> // pid_t
#include <unistd.h> // fork
bool *runChildProcess; // mmap shared memory
int fd[2]; // pipe
static void runTermChildHandler(int signum, siginfo_t *sinfo, void *unused) {
*runChildProcess = 0;
}
static void termChildHandler(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(int pipeFd, char *buffer) {
if (write(pipeFd, buffer, strlen(buffer) + 1) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid, int pipeFd) {
kill(pid, SIGUSR1);
close(pipeFd);
unsigned short int sleepTime = 1;
while ((sleepTime = sleep(sleepTime)) > 0);
munmap(runChildProcess, sizeof *runChildProcess);
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1; // set to 1 to run the loop
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_sigaction = runTermChildHandler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction SIGUSR1");
exit(EXIT_FAILURE);
}
sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP;
sa.sa_sigaction = termChildHandler;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
if (pipe(fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char buffer[100];
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
close(fd[1]); // close write end of pipe
printf("Child process: %d\n", getpid());
while (*runChildProcess) {
ssize_t nbytes = read(fd[0], buffer, sizeof(buffer)); // block until data is available from parent process
if (nbytes == -1) { // EINTR: interrupted by a signal, although SA_RESTART is set no is no need to check for EINTR
if (errno == EINTR) {
continue;
}else{
perror("read");
exit(EXIT_FAILURE);
}
}
if (nbytes == 0) {
continue;
}
printf("Buffer: %s\n", buffer);
}
close(fd[0]); // close read end of pipe
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
close(fd[0]); // close read end of pipe
runChild(fd[1], "Thanks Nate Eldredge");
runChild(fd[1], "Thanks Luis Colorado");
runChild(fd[1], "Thanks www.stackoverflow.com");
termChild(pid, fd[1]);
printf("Done\n");
exit(EXIT_SUCCESS);
}
3 - Semaphore with execve
#include <errno.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
struct sigaction sa; // signal
short int *runChildProcess; // mmap shared memory
sem_t *sem; // semaphore
static void handleChildTerm(int signum, siginfo_t *sinfo, void *unused) {
printf("Child %d exited\n", (int)sinfo->si_pid);
}
static void runChild(pid_t pid) {
if (sem_post(sem) == -1) {
perror("sem_post");
exit(EXIT_FAILURE);
}
// When SIGCHLD is caught, sleep(3) is interrupted
// https://stackoverflow.com/questions/14266485/understanding-sigchld-when-the-child-process-terminates
unsigned short int sleepTime = 3;
while ((sleepTime = sleep(sleepTime)) > 0)
;
}
static void termChild(pid_t pid) {
*runChildProcess = 0; // set to 0 to exit the loop
runChild(pid);
signal(SIGCHLD, SIG_DFL); // restore default signal handler
if(munmap(runChildProcess, sizeof *runChildProcess) == -1) {
perror("Munmap");
exit(EXIT_FAILURE);
}
if (sem_destroy(sem) == -1) {
perror("sem_destroy");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL); // make stdout unbuffered
// mmap: memory mapping file, shared with child process with the flag MAP_ANONYMOUS | MAP_SHARED
runChildProcess = mmap(NULL, sizeof *runChildProcess, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (runChildProcess == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
*runChildProcess = 1;
// semaphore
sem = mmap(0, sizeof(*sem), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sem_init(sem, 1 /*shared*/, 0 /*init val*/) == -1) {
perror("sem_init");
exit(EXIT_FAILURE);
}
// signal handler for SIGCHLD (exit child process)
sa.sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handleChildTerm;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction SIGCHLD");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
char rootChildPidParam[10];
snprintf(rootChildPidParam, 10, "%d", getpid());
char *childArgv[] = {"process-pipe-fork/execve/myecho", "hello", "world", rootChildPidParam, NULL};
char *childEnviron[] = {"MY_NAME=jcarlosweb", NULL};
printf("Root Child started: %s\n", rootChildPidParam);
while (*runChildProcess) {
sem_wait(sem);
// check again
if (*runChildProcess == 0) {
break;
}
pid_t leafChildPid = fork();
if (leafChildPid == -1) {
perror("fork sibling");
_exit(EXIT_FAILURE);
}
if (leafChildPid == 0) { // child process
printf("Leaf process with execve started: %d\n", getpid());
// TODO: make child process in background
execve("process-pipe-fork/execve/myecho", childArgv, childEnviron);
perror("execve myecho");
_exit(EXIT_FAILURE);
}
}
_exit(EXIT_SUCCESS);
}
printf("Parent process: %d\n", getpid());
runChild(pid);
runChild(pid);
runChild(pid);
termChild(pid);
printf("Done\n");
exit(EXIT_SUCCESS);
}

Processes with group of 2 semaphores and shared memory

I write a program containing two processes: the first one contains a group of two semaphores and creates the child process that reads all data in the shared memory segment and prints them.
In the second one, the child process computes the data using a compute function that returns 0 when all data are computed. It transmits them to the parent through the shared memory segment.
To write data:
On the 1st semaphore the child makes P and the parent make V.
On the 2nd semaphore the child makes V and the parent make P.
But as I'm new in this topic and still getting hardness to understand, it seems like I'm doing something wrong because it's not working as it has to be.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/wait.h>
int sum =0;
int compute(int data){
sum += data;
return sum;
}
int main(){
int i;
int shm_id;
int data;
pid_t pid;
key_t shm_key;
sem_t *sem;
// unsigned int sem_value =2;
shm_key = ftok("/dev/null", 65);
shm_id = shmget(shm_id, sizeof(int), 0644 | IPC_CREAT);
if (shm_id < 0){
perror("shmgget");
exit(EXIT_FAILURE);
}
// data = shmat(shm_id, NULL, 0);
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 2);
for (i = 0; i < 2; i++){
pid = fork();
if (pid < 0)
{
perror("fork");
sem_unlink("semaphore");
sem_close(sem);
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
break;
}
}
if (pid == 0)
{
puts("Enter the data:");
scanf("%d", &data);
//child process
sem_wait(sem);
printf("Child - %d is in critical section\n", i);
sleep(1);
puts("Enter the data:");
scanf("%d", &data);
// *shrd_value += data;
printf("Child - %d: new value of data = %d\n", i, data);
printf("Child - %d: sum of whole data by far = %d\n", i, compute(data));
sem_post(sem);
exit(EXIT_SUCCESS);
}
else if (pid > 0)
{
//parent process
while (pid = waitpid(-1, NULL, 0))
{
if (errno == ECHILD)
{
break;
}
}
puts("All children exited");
shmdt(&data);
shmctl(shm_id, IPC_RMID, 0);
sem_unlink("semaphore");
sem_close(sem);
exit(0);
}
}
Output:
Enter the data:
Enter the data:
2
Child - 0 is in critical section
1Enter the data:
Child - 1 is in critical section
Enter the data:
3
Child - 0: new value of data = 3
Child - 0: sum of whole data by far = 3
2
Child - 1: new value of data = 2
Child - 1: sum of whole data by far = 2
All children exited
I have also modified the way they write to shared memory: they write directly at the address given by shmat call that is missing in your code.
I have fixed some bugs and simplifed the code (removed the array - added detailed logging especially before and after entering the critial section):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/wait.h>
int main(){
int i;
int shm_id;
pid_t pid;
int *addr;
int data;
pid_t current_pid;
key_t shm_key;
sem_t *sem;
shm_key = ftok("/dev/null", 65);
shm_id = shmget(shm_key, sizeof(int), 0644 | IPC_CREAT);
if (shm_id < 0){
perror("shmget");
exit(EXIT_FAILURE);
}
sem_unlink("semaphore");
sem = sem_open("semaphore", O_CREAT, 0644, 1);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
addr = (int *) shmat(shm_id, (void *) 0, 0);
if (addr == (void *) -1) {
perror("shmat");
exit(EXIT_FAILURE);
}
*addr = 0;
for (i = 0; i < 2; i++){
pid = fork();
if (pid < 0)
{
perror("fork");
sem_close(sem);
sem_unlink("semaphore");
exit(EXIT_FAILURE);
}
}
if (pid == 0)
{
current_pid = getpid();
printf("Child %d: waiting for critical section \n", current_pid);
sem_wait(sem);
printf("Child %d: enters in critical section \n", current_pid);
printf("child %d: Enter the data:\n", current_pid);
scanf("%d", &data);
printf("Child %d: new value of data = %d\n", current_pid, data);
printf("Child %d: sum of whole data so far = %d\n", current_pid, *addr += data);
sem_post(sem);
printf("Child %d exits from critical section\n", current_pid);
exit(EXIT_SUCCESS);
}
else if (pid > 0)
{
//parent process
while (pid = waitpid(-1, NULL, 0))
{
if (errno == ECHILD)
{
break;
}
}
puts("All children exited");
shmdt(addr);
shmctl(shm_id, IPC_RMID, 0);
sem_close(sem);
sem_unlink("semaphore");
exit(0);
}
exit(0);
}
Note that semaphore initial value must be 1 to have a true critical section for 2 processes.
I have also removed the sleep calls and we can see that one of the process is waiting:
Child 22514: waiting for critical section
Child 22514: enters in critical section
child 22514: Enter the data:
Child 22515: waiting for critical section
333
Child 22514: new value of data = 333
Child 22514: sum of whole data so far = 333
Child 22514 exits from critical section
Child 22515: enters in critical section
child 22515: Enter the data:
666
Child 22515: new value of data = 666
Child 22515: sum of whole data so far = 999
Child 22515 exits from critical section
All children exited
All children exited
Here's the code with producer and consumer process
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> // O_CREAT, O_EXEC
#include <errno.h> // errno, ECHILD
#include <unistd.h>
#include <sys/shm.h> // shmat(), IPC_RMID
#include <sys/wait.h>
#include <semaphore.h> // sem_open(), sem_destroy(), sem_wait()...
#include <sys/types.h> // key_t, sem_t, pid_t
#include <pthread.h>
#define BUFF 10
typedef struct data{
int buff[BUFF];
int size;
int index;
}DATA;
int main(int argc, char const *argv[])
{
sem_t *full, *empty, *access;
key_t shm_key;
int shm_id;
full = sem_open ("fullname", O_CREAT , 0644, 15);
empty = sem_open ("empty", O_CREAT , 0644, 0);
access = sem_open ("access", O_CREAT , 0644, 1);
if (argc!=2)
{
exit(1);
}
int value=atoi(argv[1]);
//initialize a shared variable in shared memory
shm_key = ftok("/dev/null", 60);
shm_id = shmget(shm_key, sizeof(DATA), 0);
// shared memory error check
if (shm_id < 0){
shm_id = shmget(shm_key, sizeof(DATA), 0644 | IPC_CREAT);
DATA *data = (DATA*) shmat (shm_id, NULL, 0);
data->size=0;
data->index=0; //index
}
printf("Shared memory id: %d\n",shm_id );
printf("Checking buffer...,\n");
//If in the buffer have free space then will wait for consumer to consume the data\n"
sem_wait(empty);
printf("\nLocking buffer to produce data\n");
sem_wait(access);
//initialize a shared variable in shared memory
shm_key = ftok("/dev/null", 60);
shm_id = shmget(shm_id, sizeof(DATA), 0644 | IPC_CREAT);
// shared memory error check
if (shm_id < 0){
perror("semaphore");
exit(EXIT_FAILURE);
}
//Shared variable
DATA *data = (DATA*) shmat (shm_id, NULL, 0);
int index=(data->size + data->index) % 15;
data->buff[index]=value;
data->size++;
printf("%d is located in %d on the buffer\n",value,index );
//consusming
// attach data to shared memory
index=data->index;
value=data->buff[index];
printf("%d now consumed\n",value );
data->size--;
data->index++;
sem_post(access);
sem_post(full);
return 0;
}

What alternatives I have against sleep() to synchronize transfer between parent and child process?

I'm facing a synchronization problem, the problem I'm trying to solve involves sending string from parent to child, reversing it and sending it back to child ( using shared memory ).
However to make sure child is waiting for parent I'm using sleep(3) to give 3 seconds to parent process to enter string, however this is limiting my programs efficiency, I don't want to force user to wait for 3 seconds.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#define SHMSIZE 27
int main() {
int shmid;
char *shm;
if(fork() == 0) {
sleep(3);
shmid = shmget(29009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
printf ("Child : Reading %s \n",shm) ;
int len=strlen(shm);
char rev[100],temp;
int i = 0;
int j = strlen(shm) - 2;
while (i < j) {
temp = shm[i];
shm[i] = shm[j];
shm[j] = temp;
i++;
j--;
}
shmdt(shm);
}else {
shmid = shmget(29009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
printf("Parent : Enter String \n ");
char *s = (char *) shm;
*s = '\0';
char a[100];
fgets(a,100,stdin);
strcat(s,a);
printf ("Parent: sending %s \n",shm);
sleep(3);
printf("Parent: receiving %s" ,shm);
shmdt(shm);
}
return 0;
}
Question:
How could this be implemented in a better way, so that the program is more efficient?
I would suggest using semaphores, this is not a case where you use 'sleep':
http://man7.org/linux/man-pages/man7/sem_overview.7.html
You can use them like in this example:
http://www.csc.villanova.edu/~mdamian/threads/posixsem.html
You cannot know for sure that it will not take more than 3 seconds, so sleep is a realy bad choice. So, it goes something like this:
#include <stdio.h>
#include <semaphore.h>
int main(void)
{
sem_t *sem = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
sem_init(sem, 1, 1);
if(fork() == 0) {
printf("Child: Waiting to acquire semaphore\n");
sem_wait(sem);
printf("Child acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem);
} else {
printf("Parent: Waiting to acquire semaphore\n");
sem_wait(sem);
printf("Parent acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem);
}
sem_destroy(sem);
return 0;
}
Oh and if you want it parent to be followed by child always (or the other way around), you can use two semaphores, and initialize them accordingly(with 1 and 0, or 0 and 1).
sem_wait(sem1);
printf("Parent acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem2);
/* Other things will be happening here */
sem_wait(sem2);
printf("Child acquires lock\n");
/* do whatever you want then relese*/
sem_post(sem1);
Edit
If you do not have to use shared memory, it would be better to do the communication with sockets.
Thanks to amazing StackOverflow community for coming to my rescue! I have resolved solved the issue using semaphores! I'm sharing my final code so it can be of use for anyone who gets struck in a situation like mine!
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#include <semaphore.h>
#include <sys/mman.h>
#include<fcntl.h>
#include<stdlib.h>
#define SHMSIZE 27
typedef struct {
sem_t one;
sem_t two;
} SemPair;
int main() {
int shm = shm_open("/test", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(shm, sizeof(sem_t));
SemPair *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm, 0);
sem_init(&(sem->one), 1, 0);
sem_init(&(sem->two), 1, 0);
int shmid;
char *shmz;
if(fork() == 0) {
sem_wait(&(sem->one));
shmid = shmget(29009, SHMSIZE, 0);
shmz = shmat(shmid, 0, 0);
printf ("Child : Reading %s \n",shmz) ;
int len=strlen(shmz);
char rev[100],temp;
int i = 0;
int j = strlen(shmz) - 2;
while (i < j) {
temp = shmz[i];
shmz[i] = shmz[j];
shmz[j] = temp;
i++;
j--;
}
shmdt(shmz);
sem_post(&(sem->two));
}
else {
shmid = shmget(29009, SHMSIZE, 0666 | IPC_CREAT);
shmz = shmat(shmid, 0, 0);
printf("Parent : Enter String \n ");
char *s = (char *) shmz;
*s = '\0';
char a[100];
fgets(a,100,stdin);
strcat(s,a);
printf ("Parent: sending %s \n",shmz);
sem_post(&(sem->one));
sem_wait(&(sem->two));
printf("Parent: receiving %s" ,shmz);
shmdt(shmz);
}
return 0;
}

Shared memory linux

I'm trying to work with shared memory at the first time. I created one child process and I write to the shared memory from Parent and change it from Child, before program ends I print shared memory from Parent and shared memory hasn't change, here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <signal.h>
sem_t *semaphore;
int main(){
int i = 0, status;
pid_t pid=fork(), w;
int id;
if ((id = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666)) == -1){
perror("shmget");
exit(1);
}
int *sh;
if ((sh =(int *) shmat(id, NULL, 0)) == NULL){
perror("shmat");
exit(1);
}
*sh = 10;
if ((semaphore = sem_open("/semaphore", O_CREAT, 0666, 0)) == SEM_FAILED){
perror("semaphore");
exit(1);
}
if (pid==0) {
while(1){
sleep(1);
*sh = 50;
printf("child %d, %d\n", i, *sh);
i++;
if(i == 5){
sem_post(semaphore);
exit(0);
}
}
} else if (pid==-1) {
perror("process error\n");
} else {
printf("Parent, %d\n", *sh);
sem_wait(semaphore);
printf("child end => parent end\n");
printf("Parent, %d\n", *sh);
}
shmctl(id, IPC_RMID, NULL);
sem_close(semaphore);
sem_unlink("/semaphore");
return 0;
}
If I understand shared memory little bit than I can change it from everywhere if I have a pointer in my case is a "sh".
Output of program is:
Parent, 10
child 0, 50
child 1, 50
child 2, 50
child 3, 50
child 4, 50
child end => parent end
Parent, 10
Why is the number in shared memory different in Parent and in Child?
You fork() before you create the shared memory with the key IPC_PRIVATE, so both processes create their own shared memory and don't actually share it.
If you remove the =fork() from
pid_t pid=fork(), w;
and insert
pid = fork();
somewhere after the shmget call, it works the way you expect, since the child process will inherit the shared memory identifier from the parent and not create a different one.

Creating multiple children of a process and maintaining a shared array of all their PIDs

I have forked a couple of times and have created a bunch of child processes in C. I want to store all their PIDs in a shared array. The ordering of the PIDs does not matter. For instance, I created 32 processes. I would like to have a 32 integer long array that would store each of their PIDs and is accessible to each of these processes. What could be the best way to do this.
Here's a program that illustrates what you want using mmap():
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define MAX_PIDS 32
volatile pid_t *pids;
// Called for each child process
void do_child(void)
{
int c;
printf("Child: %d - pid array: ", getpid());
for (c=0; c<10; c++) {
printf("%d%s", pids[c], c==9?"\n":" ");
}
}
int main(int argc, char **argv)
{
int c;
pid_t pid;
// Map space for shared array
pids = mmap(0, MAX_PIDS*sizeof(pid_t), PROT_READ|PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (!pids) {
perror("mmap failed");
exit(1);
}
memset((void *)pids, 0, MAX_PIDS*sizeof(pid_t));
// Fork children
for (c=0; c<10; c++) {
pid = fork();
if (pid == 0) {
// Child process
do_child();
exit(0);
} else if (pid < 0) {
perror("fork failed");
} else {
// Store in global array for children to see
pids[c] = pid;
sleep(1);
}
}
exit(0);
}

Resources