OSTEP Chapter 5 homework
1
variable change in parent or child process doesn’t affect on the other.
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
int x = 100;
int cp = fork();
if (cp < 0) {
// failed
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("child - x: %d (initial value)\n", x);
printf("child - x: %d\n", x);
wait(NULL);
x = 111;
printf("child - x: %d (changed)\n", x);
wait(NULL);
} else {
// parent process
printf("parent - x: %d (initial value)\n", x);
x = 121;
printf("parent - x: %d (changed)\n", x);
if (argc > 1 && *argv[1] == 'p') {
printf("parent: wait\n");
wait(NULL);
}
x = 122;
printf("parent - x: %d (changed)\n", x);
}
return 0;
}
Output
% ./chapter-5/homework1.out p
0: ./chapter-5/homework1.out
1: p
parent - x: 100 (initial value)
parent - x: 122 (changed)
parent: wait
child - x: 100 (initial value)
child - x: 100
child - x: 111 (changed)
parent - x: 121 (changed)
% ./chapter-5/homework1.out
0: ./chapter-5/homework1.out
parent - x: 100 (initial value)
parent - x: 122 (changed)
parent - x: 121 (changed)
child - x: 100 (initial value)
child - x: 100
child - x: 111 (changed)
2
file writting happens in the order of process execution.
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
printf("## close STDOUT\n");
close(STDOUT_FILENO);
printf("## open the file.\n");
open("./homework2.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
int cp = fork();
if (cp < 0) {
// failed
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("hello, this is the child process.\n");
} else {
// parent process
printf("hello, this is the parent process.\n");
wait(NULL);
printf("hello, this is the parent process, again.\n");
}
return 0;
}
Output
% ./chapter-5/homework2.out
## show arguments
0: ./chapter-5/homework2.out
## close STDOUT
% cat homework2.output
hello, this is the parent process.
hello, this is the child process.
hello, this is the parent process, again.
3
using sleep
function, the parent can wait for a while.
Code
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
int cp = fork();
if (cp < 0) {
// failed
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("hello.\n");
} else {
// parent process
sleep(1);
printf("goodbye.\n");
}
return 0;
}
Output
% ./chapter-5/homework3.out
## show arguments
0: ./chapter-5/homework3.out
hello.
goodbye.
4
exec family is prepared for useful tools, so that many alternative functions were develped.
Code
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
if (argc < 2) {
printf("add function argument\n");
return 0;
}
/* setup arguments */
char *myargs[2];
myargs[0] = strdup("ls");
myargs[1] = NULL;
if (strcmp(argv[1], "execl") == 0) {
printf("## execute execl\n");
// absolute path is required
execl("/bin/ls", myargs[0], NULL);
printf("## finish execl\n");
}
if (strcmp(argv[1], "execle") == 0) {
printf("## execute execle\n");
// absolute path is required
execle("/bin/ls", myargs[0], myargs[1], NULL);
printf("## finish execle\n");
}
if (strcmp(argv[1], "execlp") == 0) {
printf("## execute execlp\n");
execlp(myargs[0], myargs[0], myargs[1], NULL);
printf("## finish execlp\n");
}
if (strcmp(argv[1], "execv") == 0) {
printf("## execute execv\n");
execv("/bin/ls", myargs);
printf("## finish execv\n");
}
if (strcmp(argv[1], "execvp") == 0) {
printf("## execute execvp\n");
execvp(myargs[0], myargs);
printf("## finish execvp\n");
}
// execvpe didn't work on macOS
//execvpe(myargs[0], myargs);
printf("no function matched.");
return 0;
}
Output
% ./a.out execl
## show arguments
0: ./a.out
1: execl
## execute execl
a.out homework1.c homework2.c homework3.c homework4.c p1.c p2.c p3.c p4.c
compile.sh homework1.out homework2.out homework3.out newfile.txt p1.out p2.out p3.out p4.out
% ./a.out execle
## show arguments
0: ./a.out
1: execle
## execute execle
a.out homework1.c homework2.c homework3.c homework4.c p1.c p2.c p3.c p4.c
compile.sh homework1.out homework2.out homework3.out newfile.txt p1.out p2.out p3.out p4.out
% ./a.out execlp
## show arguments
0: ./a.out
1: execlp
## execute execlp
a.out homework1.c homework2.c homework3.c homework4.c p1.c p2.c p3.c p4.c
compile.sh homework1.out homework2.out homework3.out newfile.txt p1.out p2.out p3.out p4.out
% ./a.out execv
## show arguments
0: ./a.out
1: execv
## execute execv
a.out homework1.c homework2.c homework3.c homework4.c p1.c p2.c p3.c p4.c
compile.sh homework1.out homework2.out homework3.out newfile.txt p1.out p2.out p3.out p4.out
% ./a.out execvp
## show arguments
0: ./a.out
1: execvp
## execute execvp
a.out homework1.c homework2.c homework3.c homework4.c p1.c p2.c p3.c p4.c
compile.sh homework1.out homework2.out homework3.out newfile.txt p1.out p2.out p3.out p4.out
5
wait returns the PID of the process to be waited. wait returns -1
when there is no process to be waited.
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
int cp = fork();
if (cp < 0) {
// fail
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("hello, this is the child process (PID: %d).\n", getpid());
int wait_rv = wait(NULL);
printf("wait return value in child process: %d\n", wait_rv);
} else {
// parent process
printf("hello, this is the parent process (PID: %d).\n", getpid());
int wait_rv = wait(NULL);
printf("wait return value in parent process: %d\n", wait_rv);
printf("hello, this is the parent process, again (PID: %d).\n", getpid());
}
return 0;
}
Output
% ./a.out
## show arguments
0: ./a.out
hello, this is the parent process (PID: 36927).
hello, this is the child process (PID: 36928).
wait return value in child process: -1
wait return value in parent process: 36928
hello, this is the parent process, again (PID: 36927).
6
waitpid
is useful when the process id to be waited is apparent or when we wait for the specific process.
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
int cp = fork();
if (cp < 0) {
// fail
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("hello, this is the child process (PID: %d).\n", getpid());
int wait_rv = waitpid(-1, NULL, 0);
printf("wait return value in child process: %d\n", wait_rv);
} else {
// parent process
printf("hello, this is the parent process (PID: %d).\n", getpid());
int wait_rv = waitpid(cp, NULL, 0);
printf("wait return value in parent process: %d\n", wait_rv);
printf("hello, this is the parent process, again (PID: %d).\n", getpid());
}
return 0;
}
Output
% ./a.out
## show arguments
0: ./a.out
hello, this is the parent process (PID: 37082).
hello, this is the child process (PID: 37083).
wait return value in child process: -1
wait return value in parent process: 37083
hello, this is the parent process, again (PID: 37082).
7
The child process stops printing out the message. It makes no effect on the parent process.
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
int cp = fork();
if (cp < 0) {
// fail
printf("fork failed.\n");
} else if (cp == 0) {
// child process
printf("hello, this is the child process.\n");
printf("## close STDOUT\n");
close(STDOUT_FILENO);
printf("hello, this is the child process, again.\n");
} else {
// parent process
printf("hello, this is the parent process.\n");
wait(NULL);
printf("hello, this is the parent process, again.\n");
}
return 0;
}
Output
% ./a.out
## show arguments
0: ./a.out
hello, this is the parent process.
hello, this is the child process.
## close STDOUT
hello, this is the parent process, again.
8
Code
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
typedef struct {
pid_t pid;
char *label;
} process_info;
void print_descriptors(pid_t *pipefd);
void print_message(process_info *pi, char *message);
int main(int argc, char *argv[]) {
printf("## show arguments\n");
for (int i = 0; i < argc; ++i)
printf("%d: %s\n", i, argv[i]);
// pipefd[0] - read end
// pipefd[1] - write end
int pipefd[2];
if (pipe(pipefd) < 0) {
perror("pipe");
_exit(1);
}
print_descriptors(pipefd);
pid_t pids[2];
pids[0] = fork();
if (pids[0] < 0 || pids[1] < 0) {
perror("fork");
return -1;
} else if (pids[0] == 0) {
// first child process
process_info pi;
pi.label = "child 1";
pi.pid = getpid();
print_message(&pi, "started");
print_message(&pi, "connect read end to standard input");
dup2(pipefd[0], STDIN_FILENO);
print_message(&pi, "close read end");
close(pipefd[0]);
print_message(&pi, "start cat");
execl("/bin/cat", "/bin/cat", NULL);
} else {
pids[1] = fork();
if (pids[1] < 0) {
// error
perror("fork");
return -1;
} else if (pids[1] == 0) {
// second child process
process_info pi;
pi.label = "child 2";
pi.pid = getpid();
print_message(&pi, "started");
print_message(&pi, "close read end");
close(pipefd[0]);
print_message(&pi, "connect write end to standard output");
dup2(pipefd[1], STDOUT_FILENO);
print_message(&pi, "close write end"); // this message is printed out from the 1st process
close(pipefd[1]);
printf("hello world\n"); // this message is printed out from the 1st process
} else {
process_info pi;
pi.label = "parent";
pi.pid = getpid();
print_message(&pi, "wait");
wait(NULL);
}
}
return 0;
}
void print_descriptors(pid_t *pipefd) {
printf("## print descriptors\n");
for (int i = 0; i < sizeof(pipefd) / sizeof(pid_t); ++i) {
printf("%d: %d\n", i + 1, pipefd[i]);
}
}
void print_message(process_info *pi, char *message) {
printf("%s (PID: %d): %s\n", pi->label, pi->pid, message);
}
Output
## show arguments
0: ./a.out
## print descriptors
1: 3
2: 4
parent (PID: 770): wait
child 1 (PID: 771): started
child 1 (PID: 771): connect read end to standard input
child 1 (PID: 771): close read end
child 2 (PID: 772): started
child 2 (PID: 772): close read end
child 2 (PID: 772): connect write end to standard output
child 2 (PID: 772): close write end
hello world
Comment
piped end points can be used by write
and read
functions, without connecting stdout/stdin.
char *text = "text";
char buf[128];
write(pipefd[1], text, strlen(text));
read(pipefd[0], buf, sizeof(buf))