6.S081 lab1 utilities
#sleep
-
main函数有两个参数argc和argv,其中argc表示命令行参数个数(argument count),argc表示命令行向量(argument vector),比如本例中的sleep程序,命令行参数个数为2,命令行向量分别为{"sleep",ticks},其中ticks为用户指定的睡眠时钟周期数,当argc!=2时,应该打印错误信息,Xv6默认打开了三个文件描述符,分别为stdin表示标准输入,stdout表示标准输出,stderr表示标准错误,文件描述符分别为整数0,1,2 -
使用
atoi(ascii to integer)函数将字符串转化为整型数字,直接系统调用sleep即可
代码如下:
#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<number>\n");
exit(1);
}
int t = atoi(argv[1]);
sleep(t);
exit(0);
}
#pingpong
pipe(int *p)系统调用创建一个管道,p[0]代表管道的读端,p[1]代表管道的写端int getpid()返回当前进程的PID,int fork()系统调用对于父进程的返回值为子进程的PID,对于子进程的返回值为0- 在
main函数中利用fork()系统调用创建一个子进程。并且分别为父子进程创建两个管道p1和p2。对于父进程,关闭管道p1的读端和p2的写端,利用int write(int fd, char *buf, int n)系统调用往管道p1的写端写入一个字节,之后利用int read(int fd, char *buf, int n)系统调用在管道p2的读端读取一个字节(当p2的读端没有数据时,程序将被阻塞,等待p2的读端传入数据),子进程的操作类似。
代码如下:
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
#define STD_ERR 2
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
if (argc != 1){
fprintf(STD_ERR, "Usage:pingpong\n");
exit(1);
}
int p1[2], p2[2];
char buf[1];
pipe(p1), pipe(p2);
if (fork() == 0){
close(p1[WRITE_END]);
close(p2[READ_END]);
if (read(p1[READ_END], buf, 1) == 1){
printf("%d: received ping\n", getpid());
close(p1[WRITE_END]);
}
write(p2[WRITE_END], buf, 1);
}
else {
close(p2[WRITE_END]);
close(p1[READ_END]);
write(p1[WRITE_END], buf, 1);
close(p1[WRITE_END]);
if (read(p2[READ_END], buf, 1) == 1){
printf("%d: received pong\n", getpid());
close(p2[READ_END]);
}
}
exit(0);
}

- 在
main函数中先创建一个管道,并创建一个新进程,对于新进程,执行work函数。对于旧进程,将数字2~35全部写入到管道。 work函数的实现逻辑参考上图,关闭pp的写端,从pp中读入一个数字i,i一定是质数(如果read函数返回0则说明pp中没有数据,直接关闭pp的读端并退出即可),将i输出后,创建当前进程的管道pc,在当前进程中,关闭pc的读端,并向pc循环写入不能整除i的数字,随后关闭pc的写端和pp的读端。对于当前进程的子进程,关闭pp的读端和pc的写端,递归调用work()。
程序代码如下:
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
#define STD_ERR 2
#define READ_END 0
#define WRITE_END 1
void work(int pp[]){
close(pp[WRITE_END]);
int i;
if (read(pp[READ_END], &i, 4) == 0){
close(pp[READ_END]);
exit(0);
}
printf("prime %d\n",i);
int pc[2], num; //pipe--child
pipe(pc);
if (fork() == 0){
close(pp[READ_END]);
close(pc[WRITE_END]);
work(pc);
}
else {
close(pc[READ_END]);
while (read(pp[READ_END], &num, 4) != 0){
if (num % i != 0){
write(pc[WRITE_END], &num, 4);
}
}
close(pc[WRITE_END]);
close(pp[READ_END]);
wait(0);
}
exit(0);
}
int main(int argc, char *argv[]){
if (argc != 1){
fprintf(STD_ERR, "Usage:primes\n");
exit(1);
}
int pp[2];
pipe(pp);
if (fork() == 0){
work(pp);
}
else {
close(pp[READ_END]);
for (int i = 2; i <= 35; i++){
write(pp[WRITE_END], &i, 4);
}
close(pp[WRITE_END]);
wait(0);
}
exit(0);
}
#find
-
fmtname函数的作用是以路径path来获取文件名,比如若路径path="a/bc/def",那么fmtname的返回值为def。 -
find函数参考user/ls.c的实现方法。DIRSIZ是文件名的最大长度,struct dirent为目录结构体,有两个属性:inum为目录下的文件数,name为目录名。struct stat为文件结构体 ,其中有用的属性是type,标识当前文件的类型(T_DIR为目录,T_FILE为文件)。 -
用
open打开需要查找的路径path并获取文件描述符fd,fstat(int fd, struct stat*)将文件描述符fd指向的文件信息存入到结构体指针stat中 。st保存了文件的信息,根据属性st.type判断文件的类型,如果当前文件类型为T_FILE则直接比较当前文件名与目标文件名是否相同,若相同,输出当前文件的路径。如果当前文件类型为T_DIR则说明当前文件是一个目录,用read读取此目录下的所有文件,并对每一个文件递归调用find函数即可。 -
注意:当目录下文件数为0时,即
de.inum==0,不应递归调用。 -
注意:当递归调用
find函数时,需要忽略.和..,.为当前目录,需要忽略,否则造成程序死循环,..为上一层目录,不应在上一层目录搜索。
程序代码如下:
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
#include "../kernel/fs.h"
#define STD_ERR 2
char *fmtname(char *path){
static char buf[DIRSIZ + 1];
char *p;
for (p = path + strlen(path); p >= path && *p != '/'; p--)
;
p++;
if (strlen(p) >= DIRSIZ)
return p;
memmove(buf, p, strlen(p));
buf[strlen(p)] = '\0';
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){
fprintf(STD_ERR, "find: cannot open %s\n",path);
return;
}
if (fstat(fd, &st) < 0){
fprintf(STD_ERR, "find: cannot stat %s\n",path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
if (strcmp(fmtname(path), filename) == 0){
printf("%s\n",path);
}
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;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) continue;
find(buf, filename);
}
break;
}
close(fd);
}
int main(int argc, char *argv[]){
if (argc != 3){
fprintf(STD_ERR, "Usage: find <path> <filename>\n");
exit(1);
}
find(argv[1], argv[2]);
exit(0);
}
#xargs
xargs命令的作用是将标准输入转换成命令行参数,一般配合管道运算符|使用,管道运算符将标准输出转换成后一个命令的标准输入。MAXARG定义在kernel/param.h中,是命令行参数的最大数目。需要执行的命令为argv[1](argv[0]="xargs")。- 创建一个大小为
MAXARG的字符串数组params存储命令行参数,将argv中的命令行参数拷贝至params中。 - 从标准输入循环读入命令行参数,若读入的字符为
'\n'则说明当前行的参数已经读完,将读入到的命令行参数追加到params中。 fork创建子进程,子进程调用exec(filename, *argv)以params为命令行参数执行argv[1]命令。父进程调用wait等待子进程结束。
程序代码如下:
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
#include "../kernel/param.h"
#define STD_IN 0
#define STD_OUT 1
#define STD_ERR 2
int main(int argc, char *argv[]){
if (argc < 2){
fprintf(STD_ERR, "Usage: xargs<arguments>\n");
exit(1);
}
if (argc + 1 > MAXARG){
fprintf(STD_OUT, "Too many arguments!\n");
exit(1);
}
char buf[512];
char *params[MAXARG];
int i;
for (i = 1; i < argc; i++) params[i - 1] = argv[i];
for (;;){
i = 0;
for (;;){
int n = read(STD_IN, &buf[i], 1);
if (n == 0 || buf[i] == '\n') break;
i++;
}
if (i == 0) break;
buf[i] = '\0';
params[argc - 1] = buf;
if (fork() == 0){
exec(argv[1], params);
}
else {
wait(0);
}
}
exit(0);
}

浙公网安备 33010602011771号