【xv6操作系统实验】01.操作系统接口
理论准备
操作系统为程序员提供了进程、内存和文件的抽象,如果程序员需要操作它们,就需要用到操作系统提供的接口。从而,使用这些接口进行编程又被称为系统编程。
为操作系统设计接口绝非易事:一方面,接口不能太大,以保证它的实现没有bug;另一方面,用户往往希望接口的功能足够强大。计算机界的两位大师——Ken Thompson和Dennis Ritchie,他们设计了著名的Unix操作系统,其中包含了一套简单实用的操作系统接口,xv6参考了Unix的接口设计。
以下是xv6提供的所有接口:(如无特殊说明,函数正常结束时返回0,出现错误时返回-1)
系统调用 | 功能描述 |
int fork() |
创建一个子进程,并返回子进程的PID. |
int exit(int status) |
退出当前进程;进程退出时的状态会传给wait(). 此函数不会返回。 |
int wait(int *status) |
等待子进程退出;子进程退出时的状态会被写入*status; 返回子进程的PID. |
int kill(int pid) |
终止指定PID的进程。成功杀死进程时返回0,出现错误时返回-1. |
int getpid() |
返回当前进程的PID. |
int sleep(int n) |
暂停n个时钟周期。 |
int exec(char *file, char*argv[]) |
加载并运行一个给定了参数的可执行文件;仅当出现错误时函数才会返回。 |
char *sbrk(int n) |
将进程的内存扩大n个字节。返回新内存的首地址。 |
int open(char *file, int flags) |
打开文件;flags指定打开方式为读或写;返回一个文件描述符(fd, file descriptor). |
int write(int fd, char *buf, int n) |
从buf向fd指定的文件写入n个字节;返回n. |
int read(int fd, char *buf, int n) |
从fd指定的文件向buf读入n个字节;返回读取的字节数;如果读到文件末尾,返回0. |
int close(int fd) |
关闭fd指定的文件。 |
int dup(int fd) |
返回一个新的文件描述符,它与fd使用的是同一个文件。 |
int pipe(int p[]) |
创建一个通道(pipe), 将用于读/写的文件描述符写入p[0]和p[1]. |
int chdir(char *dir) |
改变当前的工作目录 |
int mkdir(char *dir) |
新建文件夹 |
int mknod(char *file, int, int) |
新建一个设备文件(device file) |
int fstat(int fd, struct stat *st) |
将一个已打开文件的信息写入结构体*st. |
int stat(char *file, struct stat *st) |
将一个有名字文件的相关信息写入结构体*st. |
int link(char *file1, char *file2) |
为文件file1创建别名file2 |
int unlink(char *file) |
删除文件 |
具体实现
在本次实验中,用户需要使用以上系统接口实现一些简单的功能,具体实验要求参见此处。
警告
以下代码实现仅供参考,本人不保证其正确性。
sleep【简单】
通过sleep n
指定程序休眠的时间。具体实现很简单:首先解析命令行,获取休眠的时间,之后调用sleep
即可. 参考实现如下:
user/sleep.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(2, "Usage: sleep ticks\n");
exit(1);
}
char* tick_str = argv[1];
for (int i = 0; tick_str[i] != '\0'; i++) {
if (!(tick_str[i] >= '0' && tick_str[i] <= '9')) {
fprintf(2, "error: %s is not a integer\n", tick_str);
exit(1);
}
}
sleep(atoi(tick_str));
exit(0);
}
提示
在
main
函数中,需要调用exit
终止程序。
pingpong【简单】
需要通过fork
克隆一个子进程,并通过pipe
创建管道,通过正确设置管道的读/写端,实现父子进程间的数据传输。参考实现如下:
user/pingpong.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char* argv[]) {
int p1[2], p2[2];
pipe(p1);
pipe(p2);
if (fork() > 0) {
// parent process
close(p1[0]);
write(p1[1], "c", 1);
close(p1[1]);
close(p2[1]);
char str[2];
str[1] = '\0';
read(p2[0], str, 1);
printf("%d: receive pong %s\n", getpid(), str);
close(p2[0]);
}
else {
// child process
close(p1[1]);
char buf[2];
buf[1] = '\0';
read(p1[0], &buf, 1);
printf("%d: receive ping %s\n", getpid(), buf);
close(p1[0]);
close(p2[0]);
write(p2[1], "s", 1);
close(p2[1]);
}
exit(0);
}
primes【中等/困难】
通过筛法筛选质数,不过是基于进程间通信的实现,每筛选出一个质数,都会有一个新的进程被创建。参考实现如下:
user/primes.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void hoare(int prime, int *p)
{
printf("prime %d\n", prime);
close(p[1]);
int triggered = 0;
int value;
int p2[2];
pipe(p2);
while (read(p[0], &value, 1) != 0)
{
if (value % prime != 0)
{
if (triggered == 0)
{
triggered = 1;
if (fork() == 0)
{
hoare(value, p2);
}
else
{
close(p2[0]);
}
}
else
{
write(p2[1], &value, 1);
}
}
}
close(p2[1]);
close(p[0]);
}
int main(int argc, char *argv[])
{
int p[2];
pipe(p);
if (fork() == 0)
{
// child process
hoare(2, p);
}
else
{
// parent process
close(p[0]);
for (int i = 3; i <= 35; i++)
{
write(p[1], &i, 1);
}
close(p[1]);
}
while (wait(0) >= 0) {
;
}
exit(0);
}
find【中等】
find
用于在文件系统中查找与指定名称匹配的文件,通过系统接口了解如何遍历文件系统。参考实现如下:
user/find.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find_file(const char* dirpath, const char* filename) {
int fd = open(dirpath, 0);
if (fd < 0) {
fprintf(2, "can't open %s\n", dirpath);
exit(1);
}
struct dirent de;
char buf[512], *p;
if (strlen(dirpath) + 1 + DIRSIZ + 1 > sizeof(buf)) {
fprintf(2, "find: path too long\n");
exit(1);
}
strcpy(buf, dirpath);
p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0 || strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) {
continue;
}
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
struct stat st;
if (stat(buf, &st) < 0) {
printf("cannot stat %s\n", buf);
continue;
}
switch (st.type) {
case T_DEVICE:
case T_FILE:
if (strcmp(de.name, filename) == 0) {
printf("%s\n", buf);
}
break;
case T_DIR:
find_file(buf, filename);
break;
}
}
close(fd);
}
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(2, "Usage: find directory filename\n");
exit(1);
}
find_file(argv[1], argv[2]);
exit(0);
}
xargs【中等】
这里需要实现的是命令xargs
的简化版本。参考实现如下:
user/xargs.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
int getline(char* buf) {
int n_read = 0;
char ch;
while (read(0, &ch, 1) != 0) {
if (ch == '\n') {
buf[n_read] = '\0';
return n_read;
}
else {
buf[n_read] = ch;
n_read += 1;
}
}
return n_read;
}
int main(int argc, char* argv[]) {
char* argbuf[MAXARG];
for (int i = 0; i < argc; i++) {
argbuf[i] = (char*)malloc(sizeof(char) * (strlen(argv[i+1]) + 1));
strcpy(argbuf[i], argv[i+1]);
}
char buf[512];
int n_read = 0;
while ((n_read = getline(buf)) != 0) {
int n_token = argc - 1;
if (n_read >= 511) {
fprintf(2, "error: line too long\n");
exit(1);
}
buf[n_read] = ' ';
n_read += 1;
buf[n_read] = '\0';
char tmp[512];
int j = 0;
for (int i = 0; i < n_read; i++) {
char ch = buf[i];
if (ch == ' ' || ch == '\r') {
tmp[j] = '\0';
j = 0;
argbuf[n_token] = (char*)malloc(sizeof(char) * (strlen(tmp) + 1));
strcpy(argbuf[n_token], tmp);
n_token += 1;
if (n_token >= MAXARG) {
fprintf(2, "error: argumens too long\n");
exit(1);
}
}
else {
tmp[j] = ch;
j += 1;
}
}
argbuf[n_token] = 0;
if (fork() == 0) {
exec(argbuf[0], argbuf);
}
wait(0);
}
exit(0);
}