Solved

Mini shell background processes

Posted on 2003-10-23
13
1,082 Views
Last Modified: 2010-08-05
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
0
Comment
Question by:scentcenter
13 Comments
 

Author Comment

by:scentcenter
ID: 9604872
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
 
LVL 5

Expert Comment

by:mtmike
ID: 9605760
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
 

Expert Comment

by:raluca
ID: 9607773
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
 
LVL 5

Expert Comment

by:mtmike
ID: 9607945
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
 

Author Comment

by:scentcenter
ID: 9608276
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
 

Author Comment

by:scentcenter
ID: 9608331
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 5

Expert Comment

by:mtmike
ID: 9608337
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
 

Author Comment

by:scentcenter
ID: 9608409
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
 
LVL 5

Expert Comment

by:mtmike
ID: 9608771
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
 

Author Comment

by:scentcenter
ID: 9610108
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
 
LVL 5

Accepted Solution

by:
mtmike earned 125 total points
ID: 9610550
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
 
LVL 9

Expert Comment

by:tinchos
ID: 10546065
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

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now