Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Mini shell background processes

Posted on 2003-10-23
13
Medium Priority
?
1,227 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
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
 
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 500 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

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
Article by: evilrix
Looking for a way to avoid searching through large data sets for data that doesn't exist? A Bloom Filter might be what you need. This data structure is a probabilistic filter that allows you to avoid unnecessary searches when you know the data defin…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

636 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