Computer-Science

Shell (Exit, Wait)

1. exit, wait

exit: A program calls exit() to exit. - remove the body - becomes a zombie until the parent calls wait() wait: A program calls wait() and waits for the child to exit. - if the child already called exit() - remove its process descriptor - else - wait until the child exits

2. shell

algorithm:

 for(;;){
          printf("$");
          scanf("%s", buf);
          x=fork();
          if (x==0) execve(buf,0,0);
          else wait();
        }

Actual code:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(){
   int x,y,status, i;
   char buf[50];
   char * argv[10];

   for(i=0;i<10;i++){ // use a finite loop instead of an infinite one
      printf("$");
      scanf("%s", buf); // get command.
      argv[0]=buf;
      argv[1]=0;

      x=fork();
      if (x==0){ // child
          printf("I am child to execute %s\n", buf);
          y=execve(buf, argv, 0);
          if (y<0){
             perror("exec failed");
             exit(1);
          }
      } else wait(&status);
   }
}

3. process tree

kernel (pid=0)
fork: init (pid=1)
exec: /sbin/init (pid=1)
fork & exec: iscsid
rsyslogd
..........
/usr/sbin/sshd (pid=1262)
fork : sshd : linuxer (pid=5198)
fork : sshd: linuxer@pts/0 (pid=5201)
fork & exec: -bash (pid=5202)
fork & exec: vi cli.c (pid=6420)

4. debugging a program with fork

In gdb, use set follow-fork-mode child or set follow-fork-mode parent to debug child or parent process.

$ gdb ex1
(gdb) set follow-fork-mode child

5. Example

1) Try the shell example. What is the difference between your shell and the system shell?

2) Find all ancestor processes of your shell.

pstree๋Š” ์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋ฅผ ํŠธ๋ฆฌํ˜•ํƒœ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์œ ๋‹‰์Šค ๋ช…๋ น์–ด์ด๋‹ค. -p๋Š” โ€œshow pidsโ€๋กœ PID๋ฅผ ํ•จ๊ป˜ ์ถœ๋ ฅํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.

3) (Builtin Command)
Change the shell such that it exits when the user types โ€œexitโ€.

scanf("%s", buf);๋กœ buf๋ฅผ ์ž…๋ ฅ๋ฐ›์€ ๋’ค,

if (strcmp(buf, "exit") == 0) exit(1);

๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, exit์„ ์ž…๋ ฅ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ ํ”„๋กœ๊ทธ๋žจ์„ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์˜€๋‹ค.

4) Change the shell such that it can handle a command with arguments. Use gets() or fgets() to read the command.

fgets()๋กœ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ๊ณต๋ฐฑ์„ ํฌํ•จํ•˜์—ฌ ๋ฐ›์•„์™”๋‹ค. ๊ทธ ํ›„, strtok๋กœ ์ž…๋ ฅ์„ ๊ณต๋ฐฑ์„ ๊ธฐ์ค€์œผ๋กœ ๋ถ„๋ฆฌํ•ด์ฃผ๊ณ , ๊ทธ๊ฒƒ์„ argv[] ๋ฐฐ์—ด์— ์ €์žฅํ•˜์—ฌ execve ํ•จ์ˆ˜์— ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค.

5) (Handling &)
Change the shell such that it can handle & at the end of the command.

์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์˜ ๋งจ ๋งˆ์ง€๋ง‰์ด &์ธ์ง€์— ๋”ฐ๋ผ wait ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ, 45๋ฒˆ ์ค„๋ถ€ํ„ฐ 49๋ฒˆ ์ค„๊นŒ์ง€ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.

์‹คํ–‰์—ฌ๋ถ€๋ฅผ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฌดํ•œ๋ฃจํ”„๋ฅผ ๋„๋Š” โ€œloopโ€๋ผ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ ํ›„ ์•„๋ž˜์™€ ๊ฐ™์ด ์‹คํ–‰ํ–ˆ์„ ๋•Œ, ์ •์ƒ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

6) (Handling relative path)
Make your shell handle relative paths assuming the executable file always exists in โ€œ/binโ€ directory. When the user enters only the command name (e.g. ls -l, cp f1 f2, etc), build a full path such as โ€œ/bin/lsโ€, โ€œ/bin/cpโ€, etc. and perform exec. Use sprintf() to build the full path.

์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ์ž…๋ ฅ ๋ฐ›์€ ๋ช…๋ น์–ด์˜ ๊ฒฝ๋กœ๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ ์œ„ํ•ด ์šฐ์„ , 7๋ฒˆ ์ค„์— PATH๋ฅผ โ€œ/bin/โ€๋กœ ์ •์˜ํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ ํ›„, sprintf๋ฅผ ์ด์šฉํ•ด ์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ๋˜์–ด์žˆ๋Š” argv[0]๋ฅผ ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์—ˆ๋‹ค. (41๋ฒˆ ์ค„ ~ 43๋ฒˆ ์ค„)

6-1) Use getenv("PATH") to retrieve PATH environment variable and use strtok() to extract each system path. Display each system path line by line.

int main(void) {
    char * path = getenv("PATH");
    printf("%s\n",  path);
    return 0;
}

์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ์‹คํ–‰ํ–ˆ์„ ๋•Œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

/Users/oneonlee/opt/anaconda3/bin:/Users/oneonlee/opt/anaconda3/condabin:/usr/local/opt/bison/bin:/usr/local/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin

๊ฒฝ๋กœ๋“ค์ด :๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌ๋ถ„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์ด๋ฅผ strtok()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ„๋ฆฌํ•ด์ฃผ์—ˆ๋‹ค.

7) (Handling relative path)
Change the shell such that it can handle relative path for the command in general. The shell will search the PATH environment variable to compute the full path of the command when it is given as a relative path name. Use getenv("PATH") to obtain the pointer to the value of the PATH environment variable. Note you need to copy the string of the PATH variable into another char array before you start extracting each path component with strtok() since strtok() destroys the original string.

์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์ž…๋ ฅ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ(45๋ฒˆ ์ค„)์™€ ์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ์ž…๋ ฅ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ(55๋ฒˆ ์ค„)๋กœ ๋‚˜๋ˆ„์—‰ ๊ฐ๊ฐ์˜ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๋Œ€์‘ํ•˜์˜€๋‹ค.

์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ์ž…๋ ฅ ๋ฐ›์•˜์„ ๊ฒฝ์šฐ์—๋Š” getenv("PATH")๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, execve ํ•จ์ˆ˜ ์‹คํ–‰์ด ์„ฑ๊ณตํ•  ๋•Œ๊นŒ์ง€ env๋ฅผ ๋ฐ”๊ฟ”๊ฐ€๋ฉฐ while๋ฌธ์„ ์‹คํ–‰ํ•œ๋‹ค.


8) dup(x) duplicates fd[x] in the first empty entry in the fd table. Run following program and explain the output. Assume f1 has โ€œhello my boyโ€

x=open("f1", O_RDONLY, 00777);
int y;
y=dup(x);
printf("x:%d y:%d\n", x, y);
char buf[50];
int k=read(x, buf, 5);
buf[k]=0;
printf("buf:%s\n", buf);
k=read(y, buf, 5);
buf[k]=0;
printf("buf:%s\n", buf);

9) (Standard output redirection) Explain the output of the following code.

x=open("f2", O_WRONLY|O_CREAT|O_TRUNC,00777);
printf("x:%d\n", x);
int y;
close(1);
y=dup(x);
printf("x:%d y:%d\n", x, y);
write(1, "hi there", 8);

10) (Standard output redirection) Change the shell such that it can handle standard output redirection.

$ cat f1 > f3

will redirect the output of โ€œcat f1โ€ to file f3.