20150126:TP:C:Times:EN

De wiki-prog
Aller à : navigation, rechercher

Introduction

Our goal is now to use execvp to run programs from our C code.

Replacing our Program

  • Write the program runls.c that replace itself with the command ls -l

Running any command

Simple replacement

We now want to run any command, the idea is to use the argv array of our program. We want the following behavior:

> make runcmd
clang -Wall -Wextra -std=c99 -O3    runcmd.c   -o runcmd
> ./runcmd
runcmd: Need a command
> ./runcmd ls
>>> running command <<<
	ls
Makefile  foo.c  forkexec.c  mytime.c  runcmd  runcmd.c  runls.c
> ./runcmd ls -l
>>> running command <<<
	ls -l
total 32
-rw-r--r-- 1 feanor feanor  118 19 janv. 18:52 Makefile
-rw-r--r-- 1 feanor feanor   87 19 janv. 19:32 foo.c
-rw-r--r-- 1 feanor feanor  367 19 janv. 18:55 forkexec.c
-rw-r--r-- 1 feanor feanor 1489 19 janv. 19:44 mytime.c
-rwxr-xr-x 1 feanor feanor 7723 22 janv. 09:01 runcmd
-rw-r--r-- 1 feanor feanor  479 19 janv. 19:25 runcmd.c
-rw-r--r-- 1 feanor feanor  219 19 janv. 18:52 runls.c
> ./runcmd cat foo.c
>>> running command <<<
	cat foo.c
# include <stdlib.h>

int main() {
  int           *p = NULL;
  *p = 42;
  return 0;
}
>
  • Write the program runcmd.c that take on its command line a program name and its arguments and run it using execvp

Using fork

We now want to extend the previous program using fork(2). The schema is pretty simple: we first fork a child process, in the child we perform the exec, while in the parent, we wait for the child termination. In order to see the parent waiting, you'll print an extra message after the child termination.

The output is pretty similar to the previous one:

> make runcmd
clang -Wall -Wextra -std=c99 -O3    runcmd.c   -o runcmd
> ./runcmd
runcmd: Need a command
> ./runcmd ls
>>> running command <<<
	ls
Makefile  foo.c  forkexec.c  mytime.c  runcmd  runcmd.c  runls.c
>>> done <<<
> ./runcmd ls -l
>>> running command <<<
	ls -l
total 32
-rw-r--r-- 1 feanor feanor  118 19 janv. 18:52 Makefile
-rw-r--r-- 1 feanor feanor   87 19 janv. 19:32 foo.c
-rw-r--r-- 1 feanor feanor  367 19 janv. 18:55 forkexec.c
-rw-r--r-- 1 feanor feanor 1489 19 janv. 19:44 mytime.c
-rwxr-xr-x 1 feanor feanor 7723 22 janv. 09:01 runcmd
-rw-r--r-- 1 feanor feanor  479 19 janv. 19:25 runcmd.c
-rw-r--r-- 1 feanor feanor  219 19 janv. 18:52 runls.c
>>> done <<<
> ./runcmd cat foo.c
>>> running command <<<
	cat foo.c
# include <stdlib.h>

int main() {
  int           *p = NULL;
  *p = 42;
  return 0;
}
>>> done <<<
>

Time

We now want to implement a program similar to time(1): it runs a command and print timing information. We want the behavior of time -p. Here is an example of running time (note that we call it with full path to avoid using the shell version):

> cat ack.c
# include <stdio.h>
# include <stdlib.h>

unsigned acker(unsigned m, unsigned n) {
  return (m?(n?(acker(m-1,acker(m,n-1))):acker(m-1,1)):n+1);
}

int main() {
  printf("%u\n", acker(3,12));
  return 0;
}
> make ack
clang -Wall -Wextra -std=c99 -O3    ack.c   -o ack
> /usr/bin/time -p ./ack
32765
real 4.50
user 4.49
sys 0.00
>

How it work

In order to implement your version of time, you can start with the code of the previous exercise that fork/exec command passed as argument and extend it with time measure using clock_gettime(2) (for real time) before the fork and after the wait. In order to get user and sys time, you'll need to replace wait with wait3 in order to access the rusage structure of the child.

For the display, you can store your various time in variables of type double print them using the format %g in printf(3).

Status and execution failure

The time program leave with the same exit code as the one of its child (which can be obtain using wait3(2) and macros like WEXITSTATUS).

> cat exit42.c
int main() {
  return 42;
}
> make exit42
clang -Wall -Wextra -std=c99 -O3    exit42.c   -o exit42
> ./exit42
> echo $?
42
> /usr/bin/time -p ./exit42
Command exited with non-zero status 42
real 0.00
user 0.00
sys 0.00
> echo $?
42

As you can see in this example, the command also print a message when the exit code is different from 0.

For execution failure, we also follow the behavior of time:

> /usr/bin/time -p toto
/usr/bin/time: cannot run toto: No such file or directory
Command exited with non-zero status 127
real 0.00
user 0.00
sys 0.00
> echo $?
127
> cat foo.c
# include <stdlib.h>

int main() {
  int           *p = NULL;
  *p = 42;
  return 0;
}
> make foo
clang -Wall -Wextra -std=c99 -O3    foo.c   -o foo
> /usr/bin/time -p ./foo
Command terminated by signal 4
real 0.18
user 0.00
sys 0.00

Your program must have the same behavior as time(1) for all these cases.

Program

  • Write the program mytime that take its arguments as a command to be run and output the execution time following the format of time -p.
> make mytime
clang -Wall -Wextra -std=c99 -O3    mytime.c   -o mytime
> ./mytime ./ack
32765
real: 4.5098
user: 4.5
sys: 0
> ./mytime toto
mytime: cannot run toto: No such file or directory
Command exited with non-zero status 127
real: 0.000632002
user: 0
sys: 0
> echo $?
127
> ./mytime ./foo
Command terminated by signal 4
real: 0.00936465
user: 0
sys: 0.003333
> echo $?
4
> ./mytime ./exit42
Command exited with non-zero status 42
real: 0.00198869
user: 0
sys: 0
> echo $?
42

Things you need:

  • wait3(2) and associated macros (WIFEXITED, WEXITSTATUS, WIFSIGNALED and WTERMSIG)
  • execvp
  • fork