Mini shell background processes

I am coding a shell for an operating system class. I am almost done with it, but I do not know how to code my wait statement, so that if a process gets put in the background (the ampersand character is in the command line) stays there. Below is the fork() portion of the shell.

else if ((childpid = fork()) == 0)

            {

                  if (files[0] != 0)

                  {      
                        cpid = (int)getpid();

                        switch (io_check(files, noargs))

                        {

                              case 0:

                                    io_redirection(comm, noargs, files);

                                    break;

                              case 1:

                                    cout <<"Ambiguous input/ output redirect." <<endl;

                                    break;

                              case 2:

                                    break;

                        }

                  }

                  else

                  {

                        execvp(comm[0],comm);

                        perror(comm[0]);

                  }

                  exit(1);

            }

            else

            {

                        while ((pid = wait(&status)) != -1);

            }

How do I tell the parent not to wait for the process that should go in the background, and output a prompt right away? Also, I suppose I need to check if any of the background processes have finished each time I input a new command. Do I need to use SIGCHLD?

Thanks,
Paul
scentcenterAsked:
Who is Participating?
 
mtmikeCommented:
That's what this part is for:

pid = waitpid(-1, &status, WNOHANG);
while(pid>0)
{
  cout <<"Background process has exited" <<pid <<endl;
  pid = waitpid(-1, &status, WNOHANG);
}

It doesn't output the pids of finished background processes immediately, only before you enter a new command.

You never get to see the "Background process has exited" line?

I tried something similar (since I don't have the rest of your shell) and that worked for both background and foreground processes.
0
 
scentcenterAuthor Commented:
Also,
I am doing this assignment in the child
cpid = (int)getpid();

Why is it that if I try to output cpid in the parent I get garbage?

I tried declaring cpid as pid_t too and that doesnt work either
0
 
mtmikeCommented:
You can check whether a child has exited using waitpid instead of wait and passing it the WNOHANG option so it returns immediately when no child has exited, eg

waitpid(-1, &status, WNOHANG);

This should be done when you're about to prompt for a new command.

The parent (shell) process can determine whether the child gets executed in the background by scanning the command line for a '&'. No need to wait in that case.

The fork() call returns the pid of the child. The 'cpid = getpid()' is executed in the child process and is therefore not valid in the parent. The parent and child processes run in different address spaces.
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
ralucaCommented:
So then
in the parent I will have something like this?

{
              if (bg_process ==1)
                            waitpid(-1, &status, WNOHANG);
              else
                            while ((pid = wait(&status)) != -1);
}

Is that correct?

Also, how and where will I check if any of those processes that are in the background have finished? Will I have to use SIGCHLD for this?
0
 
mtmikeCommented:
You'll only have to wait when the process is a foreground process, otherwise you can start reading a new command immediately.

if (!bg_process) {
  while ((pid = wait(&status)) != -1);
}

Now, just before reading a new command you can check whether a background process has exited using waitpid(-1, &status, WNOHANG). There may be multiple processes exiting at the same time so you probably want to do something like:

pid = waitpid(-1, &status, WNOHANG);
while (pid > 0) {
  printf("Background process %i has exited\n", pid);
  pid = waitpid(-1, &status, WNOHANG);
}

Using SIGCHLD should not be necessary. Best to query the status of background processes before/after reading a command. It's a bit annoying when the shell prints out the status of a background process while you're in the middle of typing a command.
0
 
scentcenterAuthor Commented:
Hmm, this is what I have so far, and it does not really work. It tells me that a process has exited for processes that are not sent in the background, and for the ones that are it doesn't tell me anything.

            else if ((childpid = fork()) == 0)
            {
                  if (files[0] != 0)
                  {      
                        switch (io_check(files, noargs))
                        {
                              case 0:
                                    io_redirection(comm, noargs, files);
                                    break;
                              case 1:
                                    cout <<"Ambiguous input/ output redirect." <<endl;
                                    break;
                              case 2:
                                    break;
                        }
                  }
                  else
                  {
                        execvp(comm[0],comm);
                        perror(comm[0]);
                  }
                  exit(1);
            }
            else
            {
                  if (bg_process == 1)
                        while ((pid = wait(&status)) != -1);
                  sigsetjmp(reenter,1);
                  pid = waitpid(-1, &status, WNOHANG);
                  while(pid>0)
                  {
                        cout <<"Background process has exited" <<pid <<endl;
                        pid = waitpid(-1, &status, WNOHANG);
                  }
                  current_dir=strrchr(get_current_dir_name(),'/');
                  cout <<"[aaaaaaa@myshell ~" <<current_dir <<"]$ ";
                  signal(SIGINT,ctrl_c);
                  getline(cin,shell_status);
            }
0
 
scentcenterAuthor Commented:
my bad, I have a 0 instead of a 1 when checking for bg_process (0 means it is not a background process, 1 means it is)

THe above code does never tell me when a process has exited. Also, if I were to start another another instance of my shell from within my shell all the new commands have the new shell as the PPID, which is obviously not right since I do put the new shell in the background.

Thanks
0
 
mtmikeCommented:
You're waiting for background processes and not for foreground processes.

if (bg_process == 1)
  while ((pid = wait(&status)) != -1);

Should be the reverse

if (bg_process == 0)
  while ((pid = wait(&status)) != -1);
0
 
scentcenterAuthor Commented:
yup, I changed that, and it's still not behaving the way it should. It looks like it's not waiting for those processes but it does not tell me when a background process exits. Also, if I were to start another instance of my shell from within my shell all the new commands have the new shell as the PPID, which is obviously not right since I do put the new shell in the background.
0
 
mtmikeCommented:
The code should tell you when a process has exited. Whenever the shell spawns a foreground process it waits (using wait) for it to exit. The waitpid tells you when a background process has exited. Could be that the wait() returns for a background process though. Maybe a good idea to only wait only for the current foreground process:

if (bg_process == 0)
  while ((pid = waitpid(childpid, &status, 0)) != -1);

instead of

if (bg_process == 0)
  while ((pid = wait(&status)) != -1);


Whenever you spawn a new shell in the background, it tries to read from the tty. Now you have two shells trying to read from the same tty. This is indeed not right, since only the foreground shell should be able to read.

This can be solved by putting the background shell in a different process group than the foreground shell. The terminal driver sends the SIGTTIN/SIGTTOU signal to processes that try to read/write from/to the tty and are not in the same process group as the original shell. You can use setsid() to put a process in a new process group and setpgid() to put a background process back in the foreground. The SIGTTIN/SIGTTOU signals stop a process. A stopped process can be continued by sending it the SIGCONT signal.

This 'job control' thing is a bit tricky to implement though.
0
 
scentcenterAuthor Commented:
Ok,
I changed the code to:
if (bg_process == 0)
  while ((pid = waitpid(childpid, &status, 0)) != -1);

but the code I have does not check for the background processes. It only waits on the foreground process. How would I check if any of the background processes have finished, so I can output their PID?
0
 
tinchosCommented:
No comment has been added lately, so it's time to clean up this question.
I will leave the following recommendation for this question in the Cleanup topic area:

Accept: mtmike {http:#9610550}

Please leave any comments here within the next four days.
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Tinchos
EE Cleanup Volunteer
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.