sleep.c
#include"../kernel/types.h"
#include"user.h"
int main(int argc,char* argv[])
{
if(argc != 2){
fprintf(2,"Usage: sleep [microseconds]\n");
exit(0);
}
int second=atoi(argv[1]);
sleep(second);
exit(0);
}
pingpong.c
原理
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
int parent[2];
int child[2];
int p = pipe(parent);
int c = pipe(child);
char buf[1];
if(p == -1) {
printf("error create pipe parent");
}
if(c == -1) {
printf("error create pipe child");
}
if (fork() == 0) //child process, fork = 0
{
close(parent[1]); //close write of parent
close(child[0]); //close read of child
read(parent[0], buf, 1);
printf("%d: received p%sng\n", getpid(), buf);
write(child[1], "o", 1);
close(child[1]); //close write of child
}
else if (fork() > 0) //parent process, fork > 0
{
close(parent[0]); //close read of parent
close(child[1]); //close write of child
write(parent[1], "i", 1);
close(parent[1]); //close write of parent
read(child[0], buf, 1);
printf("%d: received p%sng\n", getpid(), buf);
}
exit(0);
}
primes.c
这题要麻烦一点,主要就是利用pipe和fork搞出一个素数筛子一样的东西
例如2,3,4,5,6,7,8,9,10,11中把素数筛出来,不过我们的这个程序是并行
的
这题主要做法就是主进程parent先fork了一个子进程child去执行sieve,然后主进程循环将2,3,4,…, 11输入pipe(parent),然后child子进程先看能不能从parent那里读到东西,读不到就exit,读的到就先fork一个子进程child2去执行sieve,child自己继续读parent的东西,以此类推
parent—>child—>child2—>child3—>…—>end
原理图
#include "kernel/types.h"
#include "user/user.h"
void sieve(int* parent);
//we have two pipe: parent[2], child[2]
int main(int argc, char *argv[])
{
int status;
int parent[2];//create a pipe
pipe(parent);
if (fork() < 0) //fork error
{
fprintf(2, "fork error\n");
exit(0);
}
if (fork() == 0) {// child process
sieve(parent);// child process begin sieve
}
close(parent[0]);//close parent read port
for (int i = 2; i <= 11; i++) {
write(parent[1], &i, sizeof(int)); //write numbers into pipe
}
close(parent[1]); //close wiring of parent
wait(&status); //wait for all child process to end
exit(0);
}
void sieve(int* parent)
{
close(parent[1]);
int ans;
// read parent and write it into n
// if there is nothing to read, this child process can go die
if (read(parent[0], &ans, sizeof(int)) == 0)
{
close(parent[0]);
exit(0);
}
printf("prime %d\n",ans);
int child[2];
pipe(child);
if (fork() == 0) // child process continue sieve
{
close(parent[0]);
sieve(child); //start a new child process
}
close(child[0]);
int prime = ans;
while (read((parent[0]), &ans, sizeof(int)) != 0) {
if (ans%prime != 0) {// is prime
write(child[1], &ans, sizeof(int));
}
}
close(parent[0]);
close(child[1]); //close writing of parent
wait((int *)0);
exit(0);
}
find.c
一些前置知识
fd
文件描述符是什么?
其实文件描述符(fd
)就是一个数组下标,通过它我们能顺藤摸瓜的找到对应进程所指向的文件
fstat,stat
fstat是什么?
fstat 将fd所指的文件状态装入st所指的结构中(struct stat)
struct stat
stat是什么?
stat结构体是文件(夹)信息的结构体
struct dient
目录信息结构体
原理
下面是ls.c
的实现代码,find.c
其实只用在ls.c
的基础上实现BFS就可以了
#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 ls(char *path)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type)
{
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf)
{
printf("ls: 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(stat(buf, &st) < 0)
{
printf("ls: cannot stat %s\n", buf);
continue;
}
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[])
{
int i;
if(argc < 2)
{
ls(".");
exit(0);
}
for(i=1; i<argc; i++)
ls(argv[i]);
exit(0);
}
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
const char* this_dir=".";
const char* parent_dir="..";
char* fmtname(char *path)//把路径中最后一个名字分离出来
{
static char buf[DIRSIZ+1];
char *p;
// find the filename before the last "/"
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 search(char* dir_name,const char* file_name)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
//打开文件描述符
if((fd = open(dir_name, 0)) < 0)
{
fprintf(2, "find: cannot open dir %s\n", dir_name);
return;
}
// fstat 将fd所指的文件状态装入st所指的结构中(struct stat)
// stat是文件(夹)信息结构体
if(fstat(fd, &st) < 0)
{
fprintf(2, "find: cannot stat dir %s\n", dir_name);
close(fd);
return;
}
// 如果传入的是文件名(递归边界条件)
if(st.type==T_FILE)
{
//和file_name进行匹配
if(!strcmp(fmtname(dir_name),file_name))
{
printf("%s\n",dir_name);//直接打印目录
}
return;
}
// 如果是文件夹
if(st.type==T_DIR)
{
if(strlen(dir_name) + 1 + DIRSIZ + 1 > sizeof buf)
{
printf("find: path too long\n");
return;
}
// dir_name写入buf数组
strcpy(buf, dir_name);
p = buf+strlen(buf);//p是定位指针
// p指向buf字符串的后一位
*p++ = '/';
//临时文件信息结构体
struct stat st_tmp;
//遍历目录下的文件
//将fd读入de(struct dirent 目录项结构)
while(read(fd, &de, sizeof(de)) == sizeof(de))
{
if(de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);//把文件名复制到字符串buf的最后面
p[DIRSIZ] = 0;//这里准备遍历文件名 准备好生成文件或者目录的相对路径,存在buf里面,把这串字符的后一位置为0来生成字符串
// 将buf的路径填入st_tmp(struct stat)
if(stat(buf, &st_tmp) < 0)
{
//printf("ls: cannot stat %s\n", buf);
continue;
}
if(st_tmp.type==T_FILE)//如果是普通文件
{
if(!strcmp(de.name,file_name))//找到文件
{
printf("%s\n",buf);//打印文件的相对路径
}
}
if(st_tmp.type==T_DIR)//如果是目录
{
//递归搜索,使用BFS遍历directory tree
//禁止遍历. .. 这两个目录
if((!strcmp(de.name,this_dir))||(!strcmp(de.name,parent_dir)))
{
continue;
}
search(buf,file_name);//递归搜索
}
}
}
return;
}
int main(int argc, char *argv[])
{
if(argc==2)
{
search(".",argv[2]);
}
else
{
search(argv[1],argv[2]);
}
exit(0);
}
xargs.c
先解释一些东西
argc
:参数个数,表示argv数组的长度
argv
:参数数组
|
:管道符,负责管道重定向
exec
:exec函数族提供了一个在进程中启动另一个程序执行的方法。 它可以根据指定的文件名
或目录
名找到可执行文件
,并用它来取代原调用进程的数据段
、代码段
和堆栈段
,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了
stdin
:标准输入流
在本题中,shell将
xargs
的stdin重定向到pipe的读端,所以可以用read直接从0号文件stdin里读取数据
原理
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
#include "kernel/fs.h"
int main(int argc,char* argv[])
{
int status;
if(argc==1)//如果xargs后面没参数
{
printf("Usage: xargs [OPTION]... COMMAND [INITIAL-ARGS]...\n");
exit(0);
}
printf("--------argc--argv-------\n");
printf("argc: %d\n",argc);
for(int i=0;argv[i]!=NULL;i++)
{
printf("argv[%d]: %s\n",i,argv[i]);
}
printf("-----------end-----------\n");
//指针数组argument
char** argument=(char**)malloc(sizeof(char*)*MAXARG);//稍后传exec的参数
//1. 从argv[1]开始是需要执行的程序和其参数。先将这部分参数传入argument中
int i;
//把xargs 后面的参数装入 argument
for(i=0;i<argc-1;i++)
{
int len=strlen(argv[i+1])+1;
//把argv[i+1]传到argument[i]
argument[i]=(char*)malloc(sizeof(char)*len);//开辟空间
strcpy(argument[i],argv[i+1]);//复制
}
//2. 现在传入管道流过来的数据, 然后存到argument中
char buffer[60];//buffer是暂存空间
char* p=buffer;//指示器,指示读入的地址
/*shell的管道是把xargs的stdin文件重定向到了管道读端,
所以可以用read直接从0号文件stdin里读取数据*/
read(0,p,1);//从stdin读入一个字符
while (*p)//不断读入,直到遇到空字符位置
{
if(*p=='\n')//读到一行末尾
{
*p='\0';//附加上空字符从而构成字符串
//存入argument中
int len=strlen(buffer)+1;
argument[i]=(char*)malloc(sizeof(char)*len);
strcpy(argument[i],buffer);
//指示器复位到暂存空间开头
p=buffer;
//暂存空间清0
memset(buffer,0,60);
i++;
read(0,p,1);
continue;
}
p++;//读入本行下一个字符
read(0,p,1);
}
printf("-----------argument------------\n");
for(int j=0;j<i;j++)
{
printf("%s\n",argument[j]);
}
printf("arugument lens: %d\n",i);
printf("-------------end---------------\n");
argument[i]=0;//argument最后一项应该置为0?
if(fork()==0)
{
//执行xargs后的命令
if(exec(argv[1],argument)==-1)
{
fprintf(2,"xargs: exec error\n");
exit(1);
}
}
//记得收回子进程
wait(&status);
exit(0);
}
结果
做完之后收获超级大,MIT牛逼!!