I'm writing myshell program.
it should supportpipe processing. It should also recognize the '&' at the end of the command and run the child process in the background if the '&' is attached.
The problem with my program is,
ls | grep txt works well, but ls | greptxt & is not working.
To this end, I declared globally the pid array that stores the IDs of the generated child processes and the reaped_id array that stores the IDs of the reaped child processes.
The second process are blocked(sigsuspended) if the first child process had not been reapped. Also, when the first process ends, sigchld handler sends a SIGCONT signal to the second process if the second process has been created that moment.
Debugging results in Sigchld handler: it does not send SIGCONT signals to the second process even after the second process has been blocked
More specifically, the incremental pcount (number of child processes created) in the parent process is not reflected in the Sigchld handler.(globally defined) Here's a part of my shell.
while(buffer[idx] != '\0'){
/*check if it is piped process*/
pipe_start_flag = pipe_end_flag; // if last process was piping process, then this process is a piped process
parse_line(&buffer[idx], argv, &idx, &pipe_end_flag); // parse until it meets '\0' or '|'(assign its (index-1) on idx)
pid[pcount] = Fork();
if(pid[pcount] == 0){
int big_bro_idx = pcount - 1; // index of last process
if(!pipe_start_flag && pipe_end_flag){
dup2(fd[WRITEEND],STDOUT_FILENO);
close(fd[READEND]);
}
else if(pipe_start_flag && !pipe_end_flag){
dup2(fd[READEND], STDIN_FILENO);
close(fd[WRITEEND]);
write(STDOUT_FILENO, "second process spawned\n", 23); // debugger
while(reaped_id[big_bro_idx] == 0){ // while last process are executing. If last process terminate, this will be its pid
write(STDOUT_FILENO, "this line works\n", 15); // debugger code
Sigsuspend(&blocking_mask);
}
}
if(execvp(argv[0], argv) < 0){
printf("%s: command not found\n", argv[0]);
exit(1);
}
}
else{ // parent process"
close(fd[WRITEEND]);
if(!bg){
while(reaped_id[pcount] <= 0 && sigtstp_flag == false){
Sigsuspend(&blocking_mask); // wait for sigchld
}
sigtstp_flag = false; // reset flag to false(it changed at sigtstp_flag)
}
else; // background process
write(STDOUT_FILENO, "pcount increased\n", 17); // debugging code
++pcount;
}
++idx;
}
Next is my SIGCHLD handler.
void sigchld_handler(int sig){
sigset_t mask, prev;
int status;
int olderrno = errno;
Sigfillset(&mask);
Sigprocmask(SIG_BLOCK, &mask, &prev);
while((reaped_id[reap_count] = waitpid(-1, &status, WNOHANG)) > 0)
{
write(STDOUT_FILENO, "process terminated\n", 19);
reap_count ++;
}
printf("%d %d %d %d ", pid[reap_count], reap_count, pcount, reaped_id[reap_count -1]);
if(pid[reap_count] > 0 || WIFSTOPPED(status)){ // if there is little brother process
Kill(pid[reap_count], SIGCONT); // send SIGCONT singal to blocked sibling process
write(STDOUT_FILENO, "I alarmed my sibling", 20); // debuggging code; not executed
}
Sigprocmask(SIG_SETMASK, &prev, NULL);
errno = olderrno;
}
process graph:
Shell spawn both child processes
The second process is blocked because the first process is not finished
First process terminated
Sigcont signal should be sent from Sigchld handler to second process but not.
(It shows that the pcount is increased, but when I print a pcount in sigchld handler, it doesn't increase at all.)
I've been up all night for this problem. If you need a full code to execute, we will send it to you.
If there is anything I need to supplement, please tell me and I will supplement it right away.
Related
I have the following code in my main function
pid_t pid;
pid = fork(); //Two processes are made
if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
int status;
if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
{
printf("Exitstatus [");
for (int i = 0; i < noOfTokens; i++)
{
printf("%s ", commands[i]);
}
printf("\b] = %d\n", WEXITSTATUS(status));
}
}
else if (pid == 0) //Child process. Executes commands and prints error if something unexpected happened
{
if (runBGflag==1) insertElement(getpid(),ptr);
execvp(commands[0], commands);
printf ("exec: %s\n", strerror(errno));
exit(1);
}
In a nutshell, a child process is made and if the runBackGround flag is set, the parent process will not wait for the child process to exit, but rather continue running. If a background process is made, the PID of the background process is stored in a list. At a later point, this function is called
void delete_zombies(void)
{
pid_t kidpid;
int status;
char buffer[1337];
while ((kidpid = waitpid(-1, &status, WNOHANG)) > 0)
{
removeElement(kidpid,buffer,1337);
printf("Child %ld terminated\n", kidpid);
printf("its command was %s\n",buffer);
}
}
This function simply checks if any child processes have died and in that case deletes them. It will then search for the childs PID in the list, remove it and print it out.
The problem is, the delete_zombies function will find that a child has died and will then try to remove it from the list, but it only finds an empty list, as if the child process never inserted its PID into the list.
This is really strange, because delete_zombies only finds a dead child process, when there was one created with the background flag set, so we know insertElement must have been called, but strangely when the parent checks in the list nothing is there
Is the cause for that, that child process and parent process have seperate lists, or is the PID maybe wrong?
I'm trying to create a program where a process forks, creating a child process, and the parent must always finish printing to the screen before the child is finished printing to the screen, no matter what. I also wish to accomplish this using signals instead of pipelining.
It is a similar problem to the question asked here: Explanation of sigsuspend needed
I understand that kill(pid,signal); is to send a signal to that pid and tell it to finish executing and terminate.
The problem is, when it executes, the child doesn't print after the suspend. Heres the code:
int main(void){
pid_t pid;
int i;
pid = fork();
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
exit(0);
}
}
I have managed to accomplish my task (printing the parent before the child) by using a global variable int x = 0; and a signal handler method void handle1(int s){x = 1;}before the main. In the main I added signal(SIGUSR1,handle1); In the child I removed all the sigset and sigsuspend lines and instead wrote while(x==0){/*do_nothing*/} 1 line before the printf statement. So when the parent executes kill(pid,SIGUSR1) the signal handler which is inherited by the child process also gets executed and sets x=1. So the child now leaves the while loop and can print it's statement.
However I believe it would be helpful to know how to accomplish this task using sigmask_t and sigsuspend() but i cant get it to work that way.
There are 3 problems in your code:
SIGUSR1 is the signal you want to deliver to the child. You can't use sigaddset(&mask,SIGUSR1);, it does exactly the opposite of your intention.
According to POSIX standard sigsuspend() below, you should install a signal handler for SIGUSR1 to make sigsuspend() continue the following code, since the default behavior of SIGUSR1 is termination.
The sigsuspend() function shall replace the current signal mask of the calling thread with the set of signals pointed to by sigmask and then suspend the thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process.
It would be better if you collect the child from the parent, otherwise there is a race condition.
The code below will work:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void handler(int sig) {}
int main(void){
pid_t pid;
int i;
pid = fork();
signal(SIGUSR1, handler);
if(pid==0){
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
sigsuspend(&mask);
printf("The child is now complete \n");
}
else{
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
}
wait(NULL);
exit(0);
}
}
You have a few issues.
Your parent process should wait for the child to complete. This allows for diagnostics (such as properly waiting for the child to print), but is otherwise a bookkeeping task that is a good habit even when the waiting process will just exit:
printf("This is the parentProcess id = %d \n",getpid());
printf("The parentProcess is complete\n");
sleep(1);
int j = kill(pid,SIGUSR1);
if (j!=0)
{
perror(NULL);
exit(0);
}
waitpid(pid, NULL, 0);
exit(0);
Now, you have set SIGUSR1 in your mask to sigsuspend(), which causes the signal to be ignored. This is now more obvious once the parent is made to wait, because the parent will never exit. So, remove the line of code that sets SIGUSR1.
Finally, the default handler for SIGUSR1 will simply cause the process to exit, and so the printf will not get a chance to print. If you want it to print, you should add a signal handler for SIGUSR1. It doesn't have to do anything.
void h (int s) {}
...
sigset_t mask;
sigemptyset(&mask);
//sigaddset(&mask,SIGUSR1);
printf("This is the child Process id = %d \n",getpid());
struct sigaction sa = { .sa_handler = h };
sigaction(SIGUSR1, &sa, NULL);
sigsuspend(&mask);
printf("The child is now complete \n");
This is for a class so I am trying to understand why the variable nChars is not being set when the child process returns. I have read that waitpid() reaps the child process but when I try to print nChars it still shows zero when the childs' nChars is the number of the commandline characters
int main(int argc, char **argv)
{
// set up pipe
int fd[2], status;
pid_t childpid;
pipe(fd);
// call fork()
if((childpid = fork()) == -1){
perror("pipe");
return -1;
}
if (childpid == 0) {
// -- running in child process --
int nChars = 0;
char ch;
close(fd[1]);
// Receive characters from parent process via pipe
// one at a time, and count them.
while(read(fd[0], &ch, 1) == 1)nChars++;
// Return number of characters counted to parent process.
printf("child returns %d\n", nChars);
close(fd[0]);
return nChars;
}
else {
// -- running in parent process --
int nChars = 0;
close(fd[0]);
printf("CS201 - Assignment 3 - \n");
// Send characters from command line arguments starting with
// argv[1] one at a time through pipe to child process.
for(int i=1; i < argc; i++)
write(fd[1], argv[i], strlen(argv[i]));
// Wait for child process to return. Reap child process.
// Receive number of characters counted via the value
// returned when the child process is reaped.
waitpid(childpid, &status, WNOHANG);
printf("child counted %d characters\n", nChars);
close(fd[1]);
return 0;
}
Parent and child don't share memory, so they have different variables nChars. Child is a COPY of parent, so when you change some variables in copy they doesn't get changed in original.
If you need to have one variable visible from two execution flows use threads.
You're returning nChars from child as process exit code, so it'll be in status variable.
Try:
waitpid(childpid, &status, 0);
// removed WNOHANG because with it parent won't wait for child to exit
printf("child counted %d characters\n", status);
But it would better to use come IPC mechanism like pipes or sockets to transfer data between child and parent, because exit codes are for program exit status, exit code 0 means all is okay, and other exit codes mean that something gone wrong, exit code is not for transferring arbitrary data
I'm hoping someone could shed some light on how to make the parent wait for ALL child processes to finish before continuing after the fork. I have cleanup code which I want to run but the child processes need to have returned before this can happen.
for (int id=0; id<n; id++) {
if (fork()==0) {
// Child
exit(0);
} else {
// Parent
...
}
...
}
pid_t child_pid, wpid;
int status = 0;
//Father code (before child processes start)
for (int id=0; id<n; id++) {
if ((child_pid = fork()) == 0) {
//child code
exit(0);
}
}
while ((wpid = wait(&status)) > 0); // this way, the father waits for all the child processes
//Father code (After all child processes end)
wait waits for a child process to terminate, and returns that child process's pid. On error (eg when there are no child processes), -1 is returned. So, basically, the code keeps waiting for child processes to finish, until the waiting errors out, and then you know they are all finished.
POSIX defines a function: wait(NULL);. It's the shorthand for waitpid(-1, NULL, 0);, which will suspends the execution of the calling process until any one child process exits.
Here, 1st argument of waitpid indicates wait for any child process to end.
In your case, have the parent call it from within your else branch.
Use waitpid() like this:
pid_t childPid; // the child process that the execution will soon run inside of.
childPid = fork();
if(childPid == 0) // fork succeeded
{
// Do something
exit(0);
}
else if(childPid < 0) // fork failed
{
// log the error
}
else // Main (parent) process after fork succeeds
{
int returnStatus;
waitpid(childPid, &returnStatus, 0); // Parent process waits here for child to terminate.
if (returnStatus == 0) // Verify child process terminated without error.
{
printf("The child process terminated normally.");
}
if (returnStatus == 1)
{
printf("The child process terminated with an error!.");
}
}
Just use:
while(wait(NULL) > 0);
This ensures that you wait for ALL the child processes and only when all have returned, you move to the next instruction.
Based on this: http://man7.org/tlpi/code/online/book/procexec/multi_wait.c.html
int
main(int argc, char *argv[])
{
int numDead; /* Number of children so far waited for */
pid_t childPid; /* PID of waited for child */
int j;
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s sleep-time...\n", argv[0]);
setbuf(stdout, NULL); /* Disable buffering of stdout */
for (j = 1; j < argc; j++) { /* Create one child for each argument */
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Child sleeps for a while then exits */
printf("[%s] child %d started with PID %ld, sleeping %s "
"seconds\n", currTime("%T"), j, (long) getpid(),
argv[j]);
sleep(getInt(argv[j], GN_NONNEG, "sleep-time"));
_exit(EXIT_SUCCESS);
default: /* Parent just continues around loop */
break;
}
}
numDead = 0;
for (;;) { /* Parent waits for each child to exit */
childPid = wait(NULL);
if (childPid == -1) {
if (errno == ECHILD) {
printf("No more children - bye!\n");
exit(EXIT_SUCCESS);
} else { /* Some other (unexpected) error */
errExit("wait");
}
}
numDead++;
printf("[%s] wait() returned child PID %ld (numDead=%d)\n",
currTime("%T"), (long) childPid, numDead);
}
}
On error, wait returns -1. One possible error is that the calling
process has no (previous unwaited-for) children, which is indicated by
the errno value ECHILD.
$ ./multi_wait 7 1 4
[13:41:00] child 1 started with PID 21835, sleeping 7 seconds
[13:41:00] child 2 started with PID 21836, sleeping 1 seconds
[13:41:00] child 3 started with PID 21837, sleeping 4 seconds
[13:41:01] wait() returned child PID 21836 (numDead=1)
[13:41:04] wait() returned child PID 21837 (numDead=2)
[13:41:07] wait() returned child PID 21835 (numDead=3)
No more children - bye!
Question
How does the system know there is no more unwaited-for children and return ECHILD.
For example, in this example, what if some children sleep for very long time?
The system keeps track of every process. You may use the command ps xf to see the process tree on your Ubuntu system.
In particular, in this list you can see the state of each process: R = running, S = sleeping, T = stopped, Z = zombie (terminated, waiting for parent to collect exit status with wait() system call).
Using this information, the system knows when to return ECHILD (no more children left).
The kernel maintains a data structure for each running process. It also maintains data structures for dead but unwaited-for processes (zombies). The parent process's data structure has information on the children it hasn't waited for yet (whether thay are zombies or are still alive, the main difference of zombies is that they have almost no flesh, i.e. their data structures only contain the exit status, resource usage, and not much more, so they are very loghtweight).