1.1 Process and memory
1.2 I/O and File descriptors
fd 0 - standard input
fd 1 - standard output
fd 2 - standard error
1.3 Pipes
pipe创建一个管道,返回两个fd,其中一个用于写入,另外一个用于读取,当调用read读取管道中的内容时,如果管道中没有数据,那么会等待数据写入管道或者写入端fd被close,当写入端fd被关闭后,read返回0
1.4 File system
cat program原理
1.从fd 0读取内容
2.将读取到的内容写入fd 1
make qemu
实现xv6中的sleep程序,sleep可以根据用户设置的停止的ticks来暂停(tick在xv6 kernel中被定义,即一个时间片的时间),需要在文件user/sleep.c中实现
#include "kernel/types.h" // 这里定义了int
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
if (argc != 2) { // 判断输入是否正确
printf("input error\n");
exit(0);
}
int tick_count = atoi(argv[1]);
sleep(tick_count);
exit(0);
}
killall qemu-system-riscv64
使用pipe实现一个ping-pong程序,使用两个方向不同的pipe进行通信,一开始parent通过pipe发送一个byte到child中,child打印": received ping",然后通过pipe发送一个字节到parent中,然后exit。parent读取到管道中的数据后,打印": received pong",然后exit。在文件user/pingpong.c中实现程序
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
int p1[2];
int p2[2];
pipe(p1);
pipe(p2);
if (fork() == 0) {
char buf[8];
read(p1[0], buf, sizeof(8));
printf("%d: received ping\n", getpid());
write(p2[1], "a", 1);
}
else {
char buf[8];
write(p1[1], "a", 1);
read(p2[0], buf, sizeof(buf));
printf("%d: received pong\n", getpid());
}
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
exit(0);
}
通过fork和pipe实现并发素数筛算法,打印2-35中的素数,在文件user/primes.c中实现
并发primes
并发素数筛算法:
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
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void child_process(int p[]) {
int child_p[2];
close(p[1]);
int prime;
int len = read(p[0], &prime, sizeof(int));
if (len == 0) {
close(p[0]);
exit(0);
}
printf("prime %d\n", prime);
pipe(child_p);
int num;
if (fork() == 0) {
close(p[0]);
child_process(child_p);
}
else {
close(child_p[0]);
while (1) {
len = read(p[0], &num, sizeof(int));
if (len == 0) {
close(p[0]);
close(child_p[1]);
wait(0);
exit(0);
}
else {
if (num % prime != 0) {
write(child_p[1], &num, sizeof(int));
}
}
}
}
}
int main(int argc, char *argv[]) {
int p0[2];
pipe(p0);
if (fork() == 0) {
child_process(p0);
}
else {
close(p0[0]);
for (int i = 2; i <= 35; i++) {
write(p0[1], &i, sizeof(int));
}
close(p0[1]);
wait(0);
}
exit(0);
}
实现在目录树中的文件查找,文件的显示路径的前缀与参数中的路径一致,实现在文件user/find.c中
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
char* fmtname(char *path) {
static char buf[DIRSIZ+1];
char *p;
// Find first character after last slash.
for(p=path+strlen(path); p >= path && *p != '/'; p--)
;
p++;
// Return blank-padded name.
if(strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p));
memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
return buf;
}
void find(char *path, char *filename) {
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if ((fd = open(path, 0)) < 0) {
printf("find: cannot open %s\n", path);
return;
}
// 根据fd获取stat
if (fstat(fd, &st) < 0) {
printf("find: cannot stat %s\n", path);
close(fd);
return;
}
switch (st.type)
{
case T_FILE:
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
// 不处理.和..目录
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
// 根据文件名获取stat
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
if (st.type == T_DIR)
find(buf, filename);
else if (st.type == T_FILE) {
if (strcmp(de.name, filename) == 0) {
printf("%s\n", buf);
}
}
}
break;
}
close(fd);
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("input error\n");
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}
xargs的作用为将标准输入作为参数,放到xargs后面的命令的后面,例如
echo hello too | xargs echo bye实际上是在执行echo bye hello too
输出:bye hello too
在文件user/xargs.c文件中实现
xargs实现思路为:
(1)获取标准输入中的信息,读取标准输入中的每行数据,将每行数据作为一个参数
(2)使用一个char* exec_args[MAXARG]作为exec传入参数,先将传入的参数放入exec_args中,注意argv中第一个参数为xargs,所要执行的参数为第二个参数
(3)声明一个buf用于保存标准输入中的参数,读取标准输入,如果读取到’\n’将该字符变为0(标识字符串结束),将每行数据放到exec_args中
(4)使用fork和exec函数执行真正的命令,其中运行的程序为argv[1],exec中argv参数第一个参数也必须为程序名,然后才为传入的参数
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"
int main(int argc, char *argv[]) {
char buf[512];
char *exec_argv[MAXARG] = {0};
int argv_idx = 0;
for (int i = 1; i < argc; i++) {
exec_argv[argv_idx++] = argv[i];
}
int buf_idx = 0;
int len = 0;
while (1) {
int begin_idx = buf_idx;
while (1) {
len = read(0, &buf[buf_idx], 1);
if (len == 0 || buf[buf_idx] == '\n') {
buf[buf_idx] = 0;
break;
}
buf_idx++;
}
if (len == 0)
break;
if (buf_idx == begin_idx)
continue;
buf[buf_idx++] = 0;
exec_argv[argv_idx++] = buf + begin_idx;
}
if (fork() == 0) {
exec(argv[1], exec_argv);
exit(0);
}
else {
wait(0);
}
exit(0);
}