CSC374 Sep25

slide version

single file version

Contents

  1. Some Linux Commands
  2. Using waitpid - version 1
  3. Using waitpid - version 2
  4. Using waitpid - version 3
  5. Using waitpid - version 4
  6. Using waitpid - version 5
  7. Race Problem - version 1
  8. Solution: Blocking/Unblocking Signals
  9. Process Group Example - version 1
  10. Process Group Example - version 2
  11. Process Group Example - version 3
  12. SIGCONT signal
  13. Shell Parsing Problem
  14. C functions - scanf, fscanf, sscanf
  15. Parsing Problem using sscanf
  16. tsh.c and job control
  17. tsh.c and job control - (continued)
  18. tsh.c and job control - (continued)
  19. Compiling the tsh Shell
  20. Running tsh Shell Tests
  21. Using etags in emacs

Some Linux Commands[1] [top]

Sometimes zombie or stopped process may hang around because of faulty code that created them.

It is useful to know these commands to manually identify and/or get rid of them:

Using waitpid - version 1[2] [top]

    1	#include <stdio.h>
    2	#include <stdlib.h>
    3	#include <sys/types.h>
    4	#include <unistd.h>
    5	#include <sys/wait.h>
    6	#include <string.h>
    7	#include <fcntl.h>
    8	#include <signal.h>
    9	#include <errno.h>
   10	#include "csapp.h"
   11	void handler(int sig)
   12	{
   13	  int status;
   14	  pid_t pid;
   15	
   16	  pid = waitpid(-1, &status, 0);
   17	
   18	  if ( pid < 0 ) {
   19	    printf("waitpid error: %s\n", strerror(errno));
   20	  } else {
   21	    printf("child %d terminated\n", pid);
   22	  }
   23	}
   24	
   25	int main()
   26	{
   27	  int i;
   28	  pid_t pid;
   29	
   30	  Signal(SIGCHLD, handler);
   31	  for(i = 0; i < 5; i++) {
   32	    if ( (pid = fork()) == 0 ) {
   33	      exit(10+i);
   34	    }
   35	  }
   36	
   37	  printf("parent waits in infinite loop: (ctrl-c to end)\n");
   38	  while(1) {
   39	  }
   40	
   41	  return 0;
   42	}

Using waitpid - version 2[3] [top]

    1	
    2	void handler(int sig)
    3	{
    4	  int status;
    5	  pid_t pid;
    6	
    7	  pid = waitpid(-1, &status, 0);
    8	
    9	  if ( pid < 0 ) {
   10	    printf("waitpid error: %s\n", strerror(errno));
   11	  } else {
   12	    printf("child %d terminated ", pid);
   13	    if (WIFEXITED(status)) {
   14	      printf("with exit value: %d\n", WEXITSTATUS(status));
   15	    } else {
   16	      printf("(did not terminate normally)\n");
   17	    }
   18	  }
   19	}
   20	
   21	int main()
   22	{
   23	  int i;
   24	  pid_t pid;
   25	
   26	  Signal(SIGCHLD, handler);
   27	  for(i = 0; i < 5; i++) {
   28	    if ( (pid = fork()) == 0 ) {
   29	      exit(10+i);
   30	    }
   31	  }
   32	
   33	  printf("parent waits in infinite loop: (ctrl-c to end)\n");
   34	  while(1) {
   35	  }
   36	
   37	
   38	  return 0;
   39	}

Using waitpid - version 3[4] [top]

    1	
    2	void handler(int sig)
    3	{
    4	  int status;
    5	  pid_t pid;
    6	
    7	  pid = waitpid(-1, &status, 0);
    8	
    9	  if ( pid < 0 ) {
   10	    printf("waitpid error: %s\n", strerror(errno));
   11	  } else {
   12	    printf("child %d terminated\n", pid);
   13	    if (WIFEXITED(status)) {
   14	      printf("child exit value: %d\n", WEXITSTATUS(status));
   15	    } else {
   16	      printf("(did not terminate normally)\n");
   17	    }
   18	  }
   19	}
   20	
   21	int main()
   22	{
   23	  int i;
   24	  pid_t pid;
   25	
   26	  Signal(SIGCHLD, handler);
   27	  for(i = 0; i < 5; i++) {
   28	    if ( (pid = fork()) == 0 ) {
   29	      sleep(1);
   30	      printf("child %d exiting\n", getpid());
   31	      exit(10+i);
   32	    }
   33	  }
   34	
   35	  printf("parent waits in infinite loop: (ctrl-c to end)\n");
   36	  while(1) {
   37	  }
   38	  return 0;
   39	}

Using waitpid - version 4[5] [top]

    1	
    2	void handler(int sig)
    3	{
    4	  int status;
    5	  pid_t pid;
    6	
    7	  while( (pid = waitpid(-1, &status, 0)) > 0 ) {
    8	    printf("child %d terminated ", pid);
    9	    if (WIFEXITED(status)) {
   10	      printf("with exit value: %d\n", WEXITSTATUS(status));
   11	    } else {
   12	      printf("(did not terminate normally)\n");
   13	    }
   14	  }
   15	  if ( pid < 0 ) {
   16	    printf("waitpid error: %s\n", strerror(errno));
   17	  }
   18	}
   19	
   20	int main()
   21	{
   22	  int i;
   23	  pid_t pid;
   24	
   25	  Signal(SIGCHLD, handler);
   26	  for(i = 0; i < 5; i++) {
   27	    if ( (pid = fork()) == 0 ) {
   28	      sleep(1);
   29	      printf("child %d exiting\n", getpid());
   30	      exit(10+i);
   31	    }
   32	  }
   33	  printf("parent waits in infinite loop: (ctrl-c to end)\n");
   34	
   35	  while(1) {
   36	  }
   37	  return 0;
   38	}

Using waitpid - version 5[6] [top]

    1	
    2	void handler(int sig)
    3	{
    4	  int status;
    5	  pid_t pid;
    6	
    7	  while( (pid = waitpid(-1, &status, 0)) > 0 ) {
    8	    printf("child %d terminated ", pid);
    9	    if (WIFEXITED(status)) {
   10	      printf("with exit value: %d\n", WEXITSTATUS(status));
   11	    } else {
   12	      printf("(did not terminate normally)\n");
   13	    }
   14	  }
   15	  if ( pid < 0 && errno != ECHILD) {
   16	    printf("waitpid error: %s\n", strerror(errno));
   17	  }
   18	}
   19	
   20	int main()
   21	{
   22	  int i;
   23	  pid_t pid;
   24	
   25	  Signal(SIGCHLD, handler);
   26	  for(i = 0; i < 5; i++) {
   27	    if ( (pid = fork()) == 0 ) {
   28	      sleep(1);
   29	      printf("child %d exiting\n", getpid());
   30	      exit(10+i);
   31	    }
   32	  }
   33	  printf("parent waits in infinite loop: (ctrl-c to end)\n");
   34	
   35	  while(1) {
   36	  }
   37	  return 0;
   38	}

Race Problem - version 1[7] [top]

    1	
    2	pid_t pid;
    3	int counter = 0;
    4	
    5	void handler1(int sig)
    6	{
    7	  counter++;
    8	  printf("counter = %d\n", counter);
    9	  fflush(stdout);
   10	  kill(pid, SIGUSR1);
   11	}
   12	
   13	void handler2(int sig)
   14	{
   15	  counter += 3;
   16	  printf("counter = %d\n", counter);
   17	  exit(0);
   18	}
   19	
   20	int main()
   21	{
   22	
   23	  Signal(SIGUSR1, handler1);
   24	
   25	  if ((pid = fork()) == 0) {
   26	    Signal(SIGUSR1, handler2);
   27	    kill(getppid(), SIGUSR1);
   28	    while(1) {
   29	    }
   30	  } else {
   31	    pid_t p; 
   32	    int status;
   33	
   34	    if ((p = wait(&status)) > 0) {
   35	      counter += 2;
   36	      printf("counter = %d\n", counter);
   37	    }
   38	  }
   39	
   40	  return 0;
   41	}

Solution: Blocking/Unblocking Signals[8] [top]

    1	
    2	pid_t pid;
    3	int counter = 0;
    4	
    5	void handler1(int sig)
    6	{
    7	  counter++;
    8	  printf("counter = %d\n", counter);
    9	  fflush(stdout);
   10	  kill(pid, SIGUSR1);
   11	}
   12	
   13	void handler2(int sig)
   14	{
   15	  counter += 3;
   16	  printf("counter = %d\n", counter);
   17	  exit(0);
   18	}
   19	
   20	int main()
   21	{
   22	  sigset_t s;
   23	
   24	  Signal(SIGUSR1, handler1);
   25	
   26	  sigemptyset(&s);
   27	  sigaddset(&s, SIGUSR1);
   28	  sigprocmask(SIG_BLOCK, &s, 0);
   29	
   30	  if ((pid = fork()) == 0) {
   31	    sigprocmask(SIG_UNBLOCK, &s, 0);
   32	    Signal(SIGUSR1, handler2);
   33	    kill(getppid(), SIGUSR1);
   34	    while(1) {
   35	    }
   36	  } else {
   37	    pid_t p; 
   38	    int status;
   39	    sigprocmask(SIG_UNBLOCK, &s, 0);
   40	    if ((p = wait(&status)) > 0) {
   41	      counter += 2;
   42	      printf("counter = %d\n", counter);
   43	
   44	    }
   45	  }
   46	
   47	  return 0;
   48	}

Process Group Example - version 1[9] [top]

    1	
    2	void sigint_handler(int sig)
    3	{
    4	  printf("pid %d got SIGINT, terminating\n", getpid());
    5	  exit(0);
    6	}
    7	
    8	
    9	int main()
   10	{
   11	  pid_t pid;
   12	
   13	  Signal(SIGINT, sigint_handler);
   14	  if ( (pid = fork()) == 0 ) {
   15	    printf("child pid = %d, gid = %d\n", getpid(), getpgrp());
   16	    pause();
   17	  }
   18	  printf("parent pid = %d, gid = %d\n", getpid(), getpgrp());
   19	  pause();
   20	
   21	  return 0;
   22	}

Process Group Example - version 2[10] [top]

    1	
    2	pid_t pid;
    3	
    4	void sigint_handler(int sig)
    5	{
    6	  printf("pid %d got SIGINT, terminating\n", getpid());
    7	  exit(0);
    8	}
    9	
   10	
   11	int main()
   12	{
   13	
   14	  Signal(SIGINT, sigint_handler);
   15	  if ( (pid = fork()) == 0 ) {
   16	    setpgid(0,0);
   17	    printf("child pid = %d, gid = %d\n", getpid(), getpgrp());
   18	    pause();
   19	  }
   20	  printf("parent pid = %d, gid = %d\n", getpid(), getpgrp());
   21	  pause();
   22	
   23	  return 0;
   24	}

Process Group Example - version 3[11] [top]

    1	
    2	pid_t cpid;
    3	
    4	void sigint_handler(int sig)
    5	{
    6	  printf("pid %d got SIGINT, ", getpid()); 
    7	  if (cpid != 0 ) {
    8	    printf("sent SIGINT to child %d, then ", cpid);
    9	    kill(cpid, SIGINT);
   10	  }
   11	  printf("exited\n");
   12	  exit(0);
   13	}
   14	
   15	
   16	int main()
   17	{
   18	
   19	  Signal(SIGINT, sigint_handler);
   20	  if ( (cpid = fork()) == 0 ) {
   21	    setpgid(0,0);
   22	    printf("child pid = %d, gid = %d\n", getpid(), getpgrp());
   23	    pause();
   24	  }
   25	
   26	  printf("parent pid = %d, gid = %d\n", getpid(), getpgrp());
   27	  pause();
   28	
   29	  return 0;
   30	}

SIGCONT signal[12] [top]

Your shell should be able to stop the foreground process if the user types ctrl-z.

Your shell can do this by forwarding the SIGTSTP signal to the foreground job.

The signal should be forwarded to all processes in the foreground job, not just the process group leader.

To restart the job later when the shell command is fg or bg, send a SIGCONT signal to all processes in the process group of the stopped job.

Shell Parsing Problem[13] [top]

Some possible user input commands for job control (not all correct):

Some correct commands (assuming the pid and job numbers are valid

fg 1400
fg %2      
fg
    

Some invalid commands

fg 99999999
fg a
fg
    

In each case when do_bgfg(argv) is called, what will be in the string array argv?

C functions - scanf, fscanf, sscanf[14] [top]

 #include <stdio.h>
 int scanf(const char *format, ...);
 int fscanf(FILE *stream, const char *format, ...);
 int sscanf(const char *str, const char *format, ...);
    

Examples

 int ret;
 int n,m;
  
 ret = scanf("%d%d", &n, &m);
    

The return value is the number of successful conversions.

Parsing Problem using sscanf[15] [top]

    argv[0] = "fg";
    argv[1] = "%2"; or argv[1] = "1400";
    argv[2] = NULL;

    int pid;
    int jid;

    int ret;

    ret = sscanf(argv[1], "%d", &pid);

or

    ret = sscanf(argv[1], "%%%d", &jid);

    

If ret is 0 for one of these, the conversion failed. So try the other one.

tsh.c and job control[16] [top]

 int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
    

The shell will need to call this each function to add a job the process it creates for any command that is not built in.

Where does the job id get created? (by addjob)

Is the job id an index into the jobs array? (no)

How do you get the job entry if you know the process id? the job id?

 struct job_t {              /* The job struct */
     pid_t pid;              /* job PID */
     int jid;                /* job ID [1, 2, ...] */
     int state;              /* UNDEF, BG, FG, or ST */
     char cmdline[MAXLINE];  /* command line */
 };
 struct job_t jobs[MAXJOBS]; /* The job list */
    

tsh.c and job control - (continued)[17] [top]

struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
struct job_t *getjobjid(struct job_t *jobs, int jid);
    

Example: If you know the pid how do you change the state to BG:

 struct job_t *p;

 p = getjobpid(jobs, pid);
 if ( p != NULL ) {
      p->state = BG;
 }
    

Always pointers are not NULL before trying to access what it points to!

The return value of this function could be NULL if the pid is invalid. E.g., the user entered an incorrect pid or the job was not correctly added to the job list, etc.

tsh.c and job control - (continued)[18] [top]

 pid_t fgpid(struct job_t *jobs);
    

This function returns the pid of the foreground process if there is a foreground process.

If there currently is no foreground process, this function returns 0. So be careful!

Compiling the tsh Shell[19] [top]

A file named Makefile is included in the shlab-handout.tar file for the shell lab.

Running tsh Shell Tests[20] [top]

The Makefile also has rules to help you run each of the 16 tests.

For example, to print the instructions to run test 1, type

 make test01      
    

and then follow the instructions.

Using etags in emacs[21] [top]

In the directory containing the tsh.c file, create an etags index:

 etags tsh.c      
    

This will create a file named TAGS. You only need to do this once.

In emacs, search for a tag by typing M-. (Alt plus the "." or if that doesn't work, the ESC key and "."

Then at the mini-prompt area at the bottom of the emacs window, type the full name or a prefix of