Results 1 to 8 of 8

Thread: Writing own shell

  1. #1
    Join Date
    Mar 2010
    Location
    Dhaka, Bangladesh
    Beans
    210
    Distro
    Ubuntu 12.04 Precise Pangolin

    Writing own shell

    I was trying to write a program which is similar to "sh" shell. However, I am having problem dealing with background process. Everything is working fine except the following case:

    As it should be, I fork() a child process and use execvp() in child to load the target program in it. And for a background process, in parent process, instead of waiting for this process to terminate, I add it to a list of currently background processes. Now, if somehow execvp() fails (for example: for bad names which are not correct program names) I still add that in main process.

    My question is, how I can notify parent process that whether execvp() actually worked or not? Can this be done without IPC? Like just checking process id of child or anything else?

    Thanks in advance.

  2. #2
    Join Date
    Nov 2008
    Location
    Melbourne Fl
    Beans
    66

    Re: Writing own shell

    Perhaps this helps: http://stackoverflow.com/questions/1...-or-not-by-pid

    Some of the other responses make good arguments about not using PID. Perhaps a mailbox or shared memory may be best.

  3. #3
    Join Date
    Jul 2010
    Beans
    86
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: Writing own shell

    well, when I implemented backgrounding in a shell, here's how I did it (in pseudocode)
    Code:
    main(){
    infinite loop 
        prompt for input
        foo=fork()
        if(foo>0) //parent process
            if they used backgrounding &
                continue. <-- this is the "keyword" continue, go to next loop iteration
            else if they didn't "background," this is a normal command
                waitpid(foo,0,0); //this is a system call in C.
                //              try "man waitpid" in terminal to learn about waitpid
        else if(foo==0) //child process
            do the command (involves execvp, looking for pipes)
        else //fork returned a negative value
            printf("fork error\n");
            return -1; //indicates error
    return 0 //indicates success
    } //end main()

  4. #4
    Join Date
    Mar 2010
    Location
    Dhaka, Bangladesh
    Beans
    210
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: Writing own shell

    Thanks for replying, I also did the same in my main(), I mean same algorithm. The problem is with implementing the "jobs" command which lists all the running background processes initiated by my shell. How can I do that? Should I check all the processes having same parent id / group id with main process? or there are smarter ways?
    Last edited by zobayer1; May 5th, 2011 at 08:46 AM. Reason: to clarify more

  5. #5
    Join Date
    Apr 2006
    Location
    Atlanta, USA
    Beans
    427

    Re: Writing own shell

    I assume your code looks something like this:

    Code:
        int rc = 0;
        pid_t pid = fork();
    
        switch(pid) {
        case 0:
            /* child */
            execvp(argv[0], argv);
            /* if exec returns there was an error. */
            perror(argv[0]);
            exit(-1);
        case -1:
            perror("fork");
            return -1;
        default:
            /* parent */
            if (in_bg) {
                printf("[proc %d started]\n", pid);
                /* add pid to some list to track jobs */
            } else {
                /* wait for child to exit */
                if ((waitpid(pid, &rc, 0) != -1)) {
                    shell->rc = WEXITSTATUS(rc);
                }
            }
            return 0;
        }
    Now, regardless of whether exec() succeeds or not, you have a new child process because of the fork. So there's no reason to worry about that when adding the process id to your list in the parent.

    What you then need to do is occasionally check to see if any of your child processes have exited (you can also get this in a signal via SIGCHLD but I like the following approach better):

    Code:
        int status;
        pid_t pid;
        /* waitpid() returns a PID on success */
        while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
            printf("[proc %d exited with code %d]\n",
                   pid, WEXITSTATUS(status));
            /* here you can remove the pid from your jobs list */
        }
    Generally you can do this right after running any other command -- for example, try in bash:

    Code:
    $ ls &
    You won't see that the job has exited until you press enter again.


    Hope this helps.
    Here we are, trapped in the amber of the moment. There is no why.

  6. #6
    Join Date
    Jan 2006
    Beans
    Hidden!
    Distro
    Ubuntu 10.10 Maverick Meerkat

    Re: Writing own shell

    Quote Originally Posted by heyandy889 View Post
    well, when I implemented backgrounding in a shell, here's how I did it (in pseudocode)
    Code:
    main(){
    infinite loop 
        prompt for input
        foo=fork()
        if(foo>0) //parent process
            if they used backgrounding &
                continue. <-- this is the "keyword" continue, go to next loop iteration
            else if they didn't "background," this is a normal command
                waitpid(foo,0,0); //this is a system call in C.
                //              try "man waitpid" in terminal to learn about waitpid
        else if(foo==0) //child process
            do the command (involves execvp, looking for pipes)
        else //fork returned a negative value
            printf("fork error\n");
            return -1; //indicates error
    return 0 //indicates success
    } //end main()
    your shell should prompt until it gets an EOF, not in an infinite loop.
    I am infallible, you should know that by now.
    "My favorite language is call STAR. It's extremely concise. It has exactly one verb '*', which does exactly what I want at the moment." --Larry Wall
    (02:15:31 PM) ***TimToady and snake oil go way back...
    42 lines of Perl - SHI - Home Site

  7. #7
    Join Date
    Mar 2010
    Location
    Dhaka, Bangladesh
    Beans
    210
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: Writing own shell

    Thanks to johnl and slavik. That was a great help
    I solved it using hints from your pseudo-codes. I used a map to store the pids, and in main, I used wait() instead of waitpid() in a slightly different way:

    Code:
        else if(pid) {
            if(bg) {
                // add pid to map
                // continue to read instructions
            }
            else {
                while(1) {
                    wpid = wait(&status);
                    if(wpid != pid) {
                        // in this case, wpid should be in map
                        // it is a background process
                        // print info and remove it from map
                    }
                    else if(wpid == pid) {
                        // current child terminated
                        break;
                    }
                }
            }
        }
    I did this because, the background process may terminate anytime and wait will get their pids as well. And it worked so far. But I am going to give it a try the way you have done here.

    Thanks

  8. #8
    Join Date
    Oct 2012
    Beans
    1

    Re: Writing own shell

    execvp() returns -1 on error.So,if it is returning -1 donot add it to the list...If execvp() is successful then u can use the above approaches...
    Last edited by bnsk; October 4th, 2012 at 04:25 PM.

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •