lab util
sleep
-
介绍:主要用来熟悉下环境以及代码结构。
- See
kernel/sysproc.c
for the xv6 kernel code that implements thesleep
system call (look forsys_sleep
),user/user.h
for the C definition ofsleep
callable from a user program, anduser/usys.S
for the assembler code that jumps from user code into the kernel forsleep
.
- See
-
代码:
#include "kernel/types.h" #include "user/user.h" int main(int argc, char *argv[]) { if (argc <= 1) { fprintf(2, "usage: sleep `time`...\n"); } int tick_num = atoi(argv[1]); sleep(tick_num); exit(0); }
pingpong
-
单个管道一般用于单向通信,父子进程可通过两个管道进行双向通信。(管道详细行为参考
primes
实验部分) -
代码:
#include "kernel/types.h" #include "user/user.h" #define BUFFSIZE 128 void perror_exit(char* err_msg) { fprintf(2, "%s\n", err_msg); exit(-1); } int main(int argc, char *argv[]) { int toson_fd[2]; int toparent_fd[2]; int ret1 = pipe(toson_fd); int ret2 = pipe(toparent_fd); if (ret1 == -1 || ret2 == -1) { perror_exit("pipe error"); } int pid = fork(); if (pid == -1) { // perror_exit("fork error"); } else if (pid == 0) { // child process close(toson_fd[1]); close(toparent_fd[0]); // read from the pipe1 char buf[BUFFSIZE]; int rbytes = read(toson_fd[0], buf, sizeof(buf)); if (rbytes == -1) { perror_exit("read error"); } buf[rbytes] = '\0'; // print the msg from parent fprintf(1, "%d: received %s\n", getpid(), buf); // write response to parent (to pipe2) char resp[4] = "pong"; int ret = write(toparent_fd[1], resp, sizeof(resp)); if (ret == -1) { perror_exit("write error"); } } else { // parent process close(toson_fd[0]); close(toparent_fd[1]); // write to son char msg[4] = "ping"; int ret = write(toson_fd[1], msg, sizeof(msg)); if (ret == -1) { perror_exit("write error"); } // read from son char buf[BUFFSIZE]; int rbytes = read(toparent_fd[0], buf, sizeof(buf)); if (rbytes == -1) { perror_exit("read"); } buf[rbytes] = '\0'; // print the resp from son fprintf(1, "%d: received %s\n", getpid(), buf); } exit(0); }
primes
介绍
实验要求通过 fork 和 pipe 系统调用建立起如下素数筛的 pipeline.
p = get a number from left neighbor
print p
loop:
n = get a number from left neighbor
if (p does not divide n)
send n to right neighbor
思路
CSP 的关键点在于:单个步骤内部操作是串行的,所有步骤之间是并发的。步骤之间的通信通过特定的 channel 完成,这里通过 pipe
完成。
如上图,除去第一个进程和最后一个进程,每个进程有两种身份(父/子)。
分析上述 pipeline, 每个进程需做如下事情:
-
从 left-side-pipe 中读取数据,尝试打印素数 prime。
- 如果 left-side-pipe 的写端关闭且没读到数据,代表没有数据到达。本进程任务结束,正常 exit.
-
建立一个新的 right-side-pipe, fork 出一个子进程, 自身即作为“父身份”根据第一步得出的 prime 进行 filter, 将过滤后的数据传入 right-side-pipe. wait 子进程,等待子进程打印结束。
- 进程 p0 由 shell fork 创建,如果 p0 不 wait 子进程,父进程 p0 可能在所有子进程打印完成前结束,此时 shell 会向终端输出提示符
$
,造成$
穿插在打印结果中的现象。 - 不 wait:
- 子进程还在运行,父进程结束 -> 孤儿进程 -> 由 init 收养。缺点:原父进程得不到子进程的状态。
- 父进程还在运行,子进程结束 -> 僵尸进程。缺点:占用资源得不到释放 (
task_struct
)。
- 进程 p0 由 shell fork 创建,如果 p0 不 wait 子进程,父进程 p0 可能在所有子进程打印完成前结束,此时 shell 会向终端输出提示符
notes: fork 出来的子进程重复上述操作。
注意点
- 注意 close(pipe) 的时机,最保险的做法是尽可能早关闭不需要的读写端。
- wait 操作。
- 错误处理。
代码
#include "kernel/types.h"
#include "user/user.h"
#define NULL 0
void perror_exit(char* err_msg) {
fprintf(2, "%s\n", err_msg);
exit(-1);
}
void child_processing(int left_pipe[2]) {
// every process do things below:
// 0. read from left-side pipe, and try to print a prime.
// 1. create a new right-side pipe, do fork, pass the filtered dat