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);
}
Related
I'm a c beginner and wrote a multiprocess program. I want to let my child process invoke strace and then pipe to the parent process so that parent process could print it.
But my parent progress seem to be getting stuck in wait(NULL); . I tried commenting code wait(NULL); and I got the output from my child process. I can't figure out why parent process keeping waiting. Hasn't the child process returned yet?
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t pid;
char *exec_argv[] = { "/bin/strace", "-T", "tree", "/bin", NULL};
char *exec_envp[] = { "PATH=/bin", NULL };
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) { // child
close(pipefd[0]); /* close unused read end */
close(STDOUT_FILENO);
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
perror("dup2");
exit(EXIT_FAILURE);
}
// invoke strace
execve(exec_argv[0], exec_argv, exec_envp);
perror(exec_argv[0]);
exit(EXIT_FAILURE);
} else { // parent
close(pipefd[1]); /* close unused write end */
if (dup2(pipefd[0], STDIN_FILENO) == -1) {
perror("dup2");
exit(EXIT_FAILURE);
}
printf("I'm parent!!!\n");
wait(NULL);
char *line = NULL;
size_t len;
while (getline(&line, &len, stdin) != -1) {
printf("%s", line);
}
free(line);
}
return 0;
}
You didn't close pipefd[0] in the parent.
You didn't close pipefd[1] in the child.
Another problem is that your code is susceptible to deadlocks. If the child writes enough to the pipe to fill it, it will block until it has space to accept more. And since the the pipe is not emptied until the child exits, the child will never unblock.
This is easy to fix: Read until EOF, then call wait to reap the child. In other words, move the wait so it's after the loop.
I am new to linux programming. I am trying to spawn two child processes and connect them through pipe. The generating child process should generate random numbers and the other child process should run a binary which takes those two numbers and finds their greatest common divider. The binary already works well with stdin so I am trying to redirect it to read end of a pipe. Similarly I do that with generating process with stdout connected to write end of a pipe.
But I think I didn't do good job wiring it together, because there is no output. Any help would be much appreciated!
I didn't find many materials on this online so suggesting those would also help a lot. Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define exec_path "nsd"
void create_pipe(int *proc_pipe);
int create_proc();
void sig_handler(int sig);
int main() {
int proc_pipe[2];
pid_t gen_proc;
pid_t nsd_proc;
int proc_stat;
create_pipe(proc_pipe);
gen_proc = create_proc();
if (gen_proc == 0) {
// child exec
struct sigaction act;
act.sa_handler = &sig_handler;
close(proc_pipe[0]);
dup2(proc_pipe[1], 1);
close(proc_pipe[1]);
while (1) {
printf("%d %d\n", rand() % 4096, rand() % 4096);
sleep(1);
if (sigaction(SIGTERM, &act, NULL) == -1) exit(2);
}
} else {
nsd_proc = create_proc();
if (nsd_proc == 0) {
// child exec
close(proc_pipe[1]);
dup2(proc_pipe[0], 0);
close(proc_pipe[0]);
execl(exec_path, "nsd", (char *) NULL);
} else {
close(proc_pipe[0]);
close(proc_pipe[1]);
sleep(5);
kill(gen_proc, SIGTERM);
wait(&proc_stat);
if (proc_stat != 0) {
perror("ERROR");
return 1;
} else {
printf("OK\n");
return 0;
}
}
}
}
void sig_handler(int sig) {
if (sig == SIGTERM) {
perror("GEN TERMINATED");
exit(0);
}
}
pid_t create_proc() {
pid_t pid = fork();
if (pid == -1) {
perror("error forking new process");
exit(2);
}
return pid;
}
void create_pipe(int *proc_pipe) {
if (pipe(proc_pipe) == -1) {
perror("failed to create pipe");
exit(2);
}
}
printf doesn't call write until it fills a buffer. You aren't giving your child enough time to fill that buffer. Add fflush(stdout) before the child goes to sleep so some data is actually written into the pipe.
I am working on a ncurses based file manager in C. The problem is that some child processes can take some time to complete and till that happens it remains stuck due to waitpid.
I can't use the WNOHANG flag because the next block of code is dependent on the output of the child process.
void getArchivePreview(char *filepath, int maxy, int maxx)
{
pid_t pid;
int fd;
int null_fd;
// Reallocate `temp_dir` and store path to preview file
char *preview_path = NULL;
allocSize = snprintf(NULL, 0, "%s/preview", cache_path);
preview_path = malloc(allocSize+1);
if(preview_path == NULL)
{
endwin();
printf("%s\n", "Couldn't allocate memory!");
exit(1);
}
snprintf(preview_path, allocSize+1, "%s/preview", cache_path);
// Create a child process to run "atool -lq filepath > ~/.cache/cfiles/preview"
pid = fork();
if( pid == 0 )
{
remove(preview_path);
fd = open(preview_path, O_CREAT | O_WRONLY, 0755);
null_fd = open("/dev/null", O_WRONLY);
// Redirect stdout
dup2(fd, 1);
// Redirect errors to /dev/null
dup2(null_fd, 2);
execlp("atool", "atool", "-lq", filepath, (char *)0);
exit(1);
}
else
{
int status;
waitpid(pid, &status, 0);
getTextPreview(preview_path, maxy, maxx);
free(preview_path);
}
}
In this case, I would like to carry forward with the rest of the program if the user decides to go to some other file. In what way can I change the architecture of the program?
If I have understood the question correctly then you want to unblock parent on either completion of child or any user input.
As suggested in this comment, you could handle SIGCHLD and one more signal say SIGUSR1. SIGUSR1 will be raised when you get user input. Following is the example where both SIGCHLD and 'SIGUSR1' is handled. If use inputs any number then it raises SIGUSR1 to parent and parent kill child. Else child will raise SIGCHLD on exit.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
int raised_signal = -1;
static void cb_sig(int signal)
{
if (signal == SIGUSR1)
raised_signal = SIGUSR1;
else if (signal == SIGCHLD)
raised_signal = SIGCHLD;
}
int main()
{
int pid;
int i, a;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = cb_sig;
if (sigaction(SIGUSR1, &act, NULL) == -1)
printf("unable to handle siguser1\n");
if (sigaction(SIGCHLD, &act, NULL) == -1)
printf("unable to handle sigchild\n");
pid = fork();
if (pid == 0) {
/* child */
for (i = 0; i < 10; i++) {
sleep(1);
printf("child is working\n");
}
exit(1);
} else {
/* parent */
if (-1 == scanf("%d", &a)) {
if (errno == EINTR)
printf("scanf interrupted by signal\n");
} else {
raise(SIGUSR1);
}
if (raised_signal == SIGUSR1) {
printf("user terminated\n");
kill(pid, SIGINT);
} else if (raised_signal == SIGCHLD) {
printf("child done working\n");
}
exit(1);
}
return 0;
}
I have a program that is supposed to do this:
The main program creates a chunk of shared memory (to share with his future children) the size of a struct called ClientInfo which contains 2 integers and a string. Then the program asks the user for a number which is stored in the variable n
The main program creates n children.Then the children wait for a SIGUSR1 signal from their father.
The main program sends a SIGUSR1 signal to all his children.
Each child reads a string from the terminal, writes it in the shared memory and also increments in one unit both shared integers. Then he sends a SIGUSR1 to his father, sleeps between 1 and 10 seconds and then ends.
Each time the father recieves a SIGUSR1 it prints the contenent of the shared memory, then only ends when all of his children end.
The "catch" is that this is college homework and they told us that the father must be able to print the content of the shared memory one time for each children, so n times in total. Also we can't use sigwait() or global variables (unless global variables are the only way).
Also each time I run the program it just hangs indefinitely asfter asking for n
I know there must be some sort of race condition but I'm really bad at that and I can't figure out what's wrong.
Thank you in advance.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define SHM_NAME "/shm_example"
#define SEM1 "/example_sem1"
#define SEM2 "/example_sem2"
#define NAME_MAX 100
typedef struct{
int previous_id; //!< Id of the previous client.
int id; //!< Id of the current client.
char name[NAME_MAX]; //!< Name of the client.
} ClientInfo;
int main(void) {
int i,n,*pids;
int fd_shm;
int error;
struct sigaction act;
ClientInfo *example_struct;
sigset_t mask, oldmask;
sem_t *sem_write = NULL,*sem_read = NULL;
sigemptyset(&mask);
sigemptyset(&oldmask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
printf("Introduzca un numero:\n");
scanf("%d",&n);
if(!(pids = malloc(n*sizeof(int))))
exit(EXIT_FAILURE);
if ((sem_write = sem_open(SEM1, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1)) == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
if ((sem_read = sem_open(SEM2, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1)) == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
sigemptyset(&(act.sa_mask));
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
if (sigaction(SIGUSR1, &act, NULL) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
fd_shm = shm_open(SHM_NAME,O_RDWR | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR);
if(fd_shm == -1) {
fprintf (stderr, "Error creating the shared memory segment \n");
return EXIT_FAILURE;
}
error = ftruncate(fd_shm, sizeof(ClientInfo));
if(error == -1) {
fprintf (stderr, "Error resizing the shared memory segment \n");
shm_unlink(SHM_NAME);
return EXIT_FAILURE;
}
/* Map the memory segment */
example_struct = (ClientInfo *)mmap(NULL, sizeof(*example_struct), PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm, 0);
if(example_struct == MAP_FAILED) {
fprintf (stderr, "Error mapping the shared memory segment \n");
shm_unlink(SHM_NAME);
return EXIT_FAILURE;
}
(example_struct->previous_id)=-1;
(example_struct->id)=0;
for(i=0;i<n;i++){
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pids[i] == 0) {
char nombre[NAME_MAX];
srand(getpid() ^ (i * 1091));
sigsuspend(&oldmask);
sem_wait(sem_write);
(example_struct->previous_id)++;
printf("Introduzca un nombre:\n");
scanf("%s",nombre);
memcpy(example_struct->name, nombre, sizeof(nombre));
(example_struct->id)++;
kill(getppid(),SIGUSR1);
sem_post(sem_write);
sleep(1 + (rand()%10));
exit(EXIT_SUCCESS);
}
}
sigprocmask(SIG_UNBLOCK, &mask, &oldmask);
kill(0,SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while(1){
sigsuspend(&oldmask);
/*if(wait(NULL)<0){
sem_close(sem_write);
sem_close(sem_read);
sem_unlink(SEM1);
sem_unlink(SEM2);
munmap(example_struct, sizeof(*example_struct));
shm_unlink(SHM_NAME);
exit(EXIT_SUCCESS);
}*/
sem_wait(sem_read);
sem_wait(sem_write);
sem_post(sem_read);
printf("El cliente es %s con id %d y el previo es %d\n",example_struct->name,example_struct->id,example_struct->previous_id);
fflush(stdout);
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
sem_wait(sem_read);
sem_post(sem_write);
sem_post(sem_read);}
}
You can try something like that:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdatomic.h>
#define SHM_NAME "/shm_example"
#define NAME_MAX 100
typedef struct{
char name[NAME_MAX]; //!< Name of the client.
} ClientInfo;
atomic_bool done = ATOMIC_VAR_INIT(false);
void child_handler(int dummy) {
(void)dummy;
done = true;
}
int main(void) {
int i,n,*pids;
int fd_shm;
int error;
struct sigaction act;
ClientInfo *example_struct;
printf("Introduzca un numero:\n");
scanf("%d",&n);
if(!(pids = malloc(n*sizeof(int))))
exit(EXIT_FAILURE);
sigemptyset(&(act.sa_mask));
act.sa_flags = 0;
act.sa_handler = child_handler;
if (sigaction(SIGUSR1, &act, NULL) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
fd_shm = shm_open(SHM_NAME,O_RDWR | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR);
if(fd_shm == -1) {
fprintf (stderr, "Error creating the shared memory segment \n");
return EXIT_FAILURE;
}
error = ftruncate(fd_shm, sizeof(ClientInfo));
if(error == -1) {
fprintf (stderr, "Error resizing the shared memory segment \n");
shm_unlink(SHM_NAME);
return EXIT_FAILURE;
}
/* Map the memory segment */
example_struct = (ClientInfo *)mmap(NULL, sizeof(*example_struct), PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm, 0);
if(example_struct == MAP_FAILED) {
fprintf (stderr, "Error mapping the shared memory segment \n");
shm_unlink(SHM_NAME);
return EXIT_FAILURE;
}
for (i = 0; i < n ; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pids[i] == 0) {
/* Child */
while (!done) {
/* Wait for signal */
sleep(1);
}
printf("child %d wakes up\n", getpid());
sprintf(example_struct->name, "my pid is %d", getpid());
kill(getppid(),SIGUSR1);
sleep(1 + (rand()%10));
exit(EXIT_SUCCESS);
}
}
/* only father here */
for (i = 0; i < n; i++) {
done = false;
kill(pids[i], SIGUSR1);
printf("Waiting child %d\n", pids[i]);
while (!done) {
/* Wait for signal */
sleep(1);
}
printf("Answer from %d: %s\n", pids[i], example_struct->name);
wait(NULL);
}
}
In the end, I don't really understand why you used semaphores if you are asked to use signal only, thus I removed them. I also removed the id do get a less chatty code, you may add them back if you need it.
The point here is I perform the wait with a while() loop waiting for a boolean atomic variable.
So the father trig a signal on the first child, then waits for it to signal back, then do the same with the next one, and so on...
so you get such a sequence:
$ ./example
Introduzca un numero:
5
Waiting child 14314
child 14314 wakes up
Answer from 14314: my pid is 14314
Waiting child 14315
child 14315 wakes up
Answer from 14315: my pid is 14315
Waiting child 14316
child 14316 wakes up
Answer from 14316: my pid is 14316
Waiting child 14317
child 14317 wakes up
Answer from 14317: my pid is 14317
Waiting child 14318
child 14318 wakes up
Answer from 14318: my pid is 14318
The program i am trying to write is trying to demonstrate how IPC works on Linux, but i keep getting a core dump error. It compiles fine and will run up until the last output statement in the parent process.
My code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <err.h>
#include <sysexits.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#define SHM_SIZE 15
int main (int argc, char ** argv[]) {
pid_t pid; //pid variable of type pid
int shmid; //shared memory id
int key = 1776; //randomly chosen key
char *shm; //shared memory name
int pipefd[2];
char buff;
pid = fork(); //creating child process
pipe(pipefd); //creating pipe
if (pid < 0) {
fprintf(stderr, "Fork Failed");
return -1;
} else if (pid == 0) {
shmid = shmget(key, SHM_SIZE, 0);
shm = shmat(shmid, 0, 0);
char *n = (char *) shm;
printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
scanf("%s", n);
printf("\n");
///////////////////////////////////////////////////////////////////////////////////////
close(pipefd[1]);
printf("pipe opened on child end");
printf("\n");
while(read(pipefd[0], &buff, 1) > 0) {
write(1, &buff, 1);
}
write(1, "\n", 1);
close(pipefd[0]);
printf("pipe successfully closed");
printf("\n");
exit(EXIT_SUCCESS);
} else {
shmid = shmget(key, SHM_SIZE, 0777 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
wait(NULL);
printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
printf("\n");
//////////////////////////////////////////////////////////////////////////////////////
close(pipefd[0]);
printf("pipe open on parent end");
printf("\n");
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]);
printf("pipe successfully closed");
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
Does it have something to do with my args[]? Such as could i be accessing memory out of reach? Or am trying to access some invalid pointer?
Many Thanks!
You have several problems in your code
Create the pipe before the fork. You create the pipe twice, once for
the parent process and one for the child process. That makes no sense, the pipe
that the child created cannot be used by the parent. The pipe must already
exists so that the child inherits the file descriptors when the child is
created.
Usually the parent creates the shared memory and the child gets the shmid
from the parent when it does the fork. Otherwise you will have to synchronize
the child and parent. So I would put the creation of the shared memory before
the fork, so that the child inherits the shmid from the parent.
In the line char *n = (char *) shm; the cast is not needed, shm is
already a char*.
In the parent block after the fork, you do wait(NULL); and then proceed to
write into the pipe. That makes no sense and you block both parent and child.
The child blocks on read because the parent hasn't send anything through the
pipe, yet. And the parent blocks on wait, because the child never exits and thus
cannot send anything through the pipe. The parent must first send data
through the pipe, then wait for the child to exit.
In the child block you do scanf("%s", n);, you are not protecting you
against buffer overflows. scanf("%14s", n) would be better. Also you are not
checking if scanf read anything at all. If the user presses
CtrlD then stdin is closed, scanf fails. In that case
n might not be '\0'-terminated and this would lead to undefined behaviour
when the parent tries to print it. So it would be better:
if(scanf("%14s", n) != 1) // avoid buffer overflow
{
fprintf(stderr, "Child: cannot read from stdin\n");
n[0] = 0; // 0-terminating
}
In the parent block after the fork, you do wait twice, why?
Your main is wrong, it should be
int main(int argc, char **argv);
The parent sends the contents of argv[1] to the child through the pipe, but
you fail to check if argv[1] is not NULL. Use this at the start of the
program:
if(argc != 2)
{
fprintf(stderr, "usage: %s string\n", argv[0]);
return 1;
}
So the correct version would be:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <err.h>
#include <sysexits.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#define SHM_SIZE 15
int main (int argc, char **argv) {
pid_t pid; //pid variable of type pid
int shmid; //shared memory id
char *shm; //shared memory name
if(argc != 2)
{
fprintf(stderr, "usage: %s string\n", argv[0]);
return 1;
}
int pipefd[2];
char buff;
// create shared memory before the fork,
// otherwise you will need to syncronize parent
// and child
pipe(pipefd); //creating pipe before the fork
// parent creates shared memory, child inherits shmid
// after fork
shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
pid = fork(); //creating child process
if (pid < 0) {
fprintf(stderr, "Fork Failed");
return 1; // return -1 would be the same as return 255
} else if (pid == 0) {
shm = shmat(shmid, 0, 0);
char *n = shm; // shm is already a char*
printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
if(scanf("%14s", n) != 1) // avoid buffer overflow
{
fprintf(stderr, "Child: cannot read from stdin\n");
n[0] = 0; // 0-terminating
}
printf("\n");
///////////////////////////////////////////////////////////////////////////////////////
close(pipefd[1]);
printf("pipe opened on child end");
printf("\n");
printf("Parent sends: ");
fflush(stdout);
while(read(pipefd[0], &buff, 1) > 0) {
write(1, &buff, 1);
}
write(1, "\n", 1);
close(pipefd[0]);
printf("pipe successfully closed");
printf("\n");
exit(EXIT_SUCCESS);
} else {
shm = shmat(shmid, 0, 0);
close(pipefd[0]);
printf("pipe open on parent end");
printf("\n");
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]);
printf("pipe successfully closed");
// not we wait for child to exit
wait(NULL);
printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
printf("\n");
//////////////////////////////////////////////////////////////////////////////////////
exit(EXIT_SUCCESS);
}
return 0;
}
And the output is:
$ ./b "message to child: stop playing video games!"
pipe open on parent end
hello i am the child process. my pid is 10969. what is your name?: Pablo
pipe opened on child end
Parent sends: message to child: stop playing video games!
pipe successfully closed
pipe successfully closed
This is Child's Parent. My pid is 10968. Nice to me you Pablo.
You are reading and writing from the same end of the pipe you create. Common practice is to read from end [1] and write to end [0]. Tell me if that helps. Additionally, it is also common practice to not have too much going on between the child and parent processes. Attempting to execute code in between segments (parent and child) usually ends up with a segmentation fault, even if your code compiles.