https://pdos.csail.mit.edu/6.S081/2020/labs/util.html
sleep
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(2, "error input\n");
exit(1);
}
int sec = atoi(argv[1]);
sleep(sec);
exit(0);
}
~
~
pingpong
这个涉及的东西比较多, 要解决问题,得先明白问题是什么,
pipe 命令,用来新建一个管道,那么管道到底是什么?
管道(pipe)是进程用来通讯的共享内存区域。一个进程往管道中写入信息,而其它的进程可以从管道中读出信息。
匿名管道是不命名的,它最初用于在本地系统中父进程与它启动的子进程之间的通信。
参考 https://zhuanlan.zhihu.com/p/58489873
关于dup,参考 https://www.cnblogs.com/pengdonglin137/p/3286627.html
看这段代码,我tm看了好久都没看懂
int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p); //创建一个新的管道并且将读写描述符记录在数组 p 中
if(fork() == 0) {
//unix里面,一切皆文件,所以stdin stdout都可以看成是文件
close(0); //关闭stdin, 对应文件描述符0,这样0就空出来了
dup(p[0]); // 把p[0]拷贝到0,因为0已经关闭了(或者说0号位置空缺),这样wc就从管道的p[0]读取数据,而不是stdin
close(p[0]);
close(p[1]);
exec("/bin/wc", argv);
} else {
write(p[1], "hello world\n", 12);
close(p[0]);
close(p[1]);
}
所以管道怎么写我想想看
管道读数据需要重定向输入流,那么可以想到如果要输出数据,就必须重定向输出流
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
int p[2];
int q[2];
pipe(p);
pipe(q);
if(fork() == 0) {
close(p[1]);// 关闭管道p[1] 为什么要关掉p1? 因为我们要读取p[0],如果不关闭p[1],读写端口同时开启,如果这个时候p[1]又被写入信息,那么会怎么样
close(q[0]);// 为什么要关掉q0 我的理解是,如果要写入到q1,避免一写入就被读取,那么我们就要关闭q0,读写同时开启不是个好事
write(q[1], "received pong\n", 16 );
char buf[1024] = {"\0"};
read(p[0], buf, 16); //读取父进程发过来的消息,而父进程是通过write(p[1])来写入到p缓冲区的,然后在读端口被子进程读到
printf("%d: %s",getpid(),buf);
close(p[0]);
close(p[1]);
exit(0);
} else {
close(p[0]);
close(q[1]);
write(p[1], "received ping\n", 16 );
char buf[1024] = {"\0"};
read(q[0], buf, 16);
printf("%d: %s",getpid(),buf);
close(q[0]);
close(q[1]);
exit(0);
}
exit(0);
}
~
如果删掉
close(p[1]);
close(q[0]);
这4行,那么后面会有并发问题,如下图最下,但删掉了测试还是能通过的
find
主要参考ls,需要修改的地方是传入的参数由一个变为两个,以及控制buf结束符'\0'的位置,还有文件名之间的比较
#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.
memmove(buf, p, strlen(p) + 1);
return buf;
}
void
find(char *path, char* findName)
{
char buf[512], *p;
int fd;
struct dirent de;
/**
struct dirent
{
long d_ino; //inode number 索引节点号
off_t d_off; /* offset to this dirent 在目录文件中的偏移
unsigned short d_reclen; // length of this d_name 文件名长
unsigned char d_type; // the type of d_name 文件类型
char d_name [NAME_MAX+1]; // file name (null-terminated) 文件名,最长255字符
}
*/
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "find: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:// 如果是文件类型,那么比较,文件名是否匹配,匹配则输出
if(strcmp(fmtname(path), findName) == 0)
printf("%s\n", path);
// else
// printf("path:%s %d, findName:%s %d\n", fmtname(path),strlen(fmtname(path)), findName, strlen(findName));
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++ = '/';//buf是一个绝对路径,p是一个文件名,并通过加"/"前缀拼接在buf的后面
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0) {
continue;
}
memmove(p, de.name, DIRSIZ);//memmove, 把de.name信息复制p,其中de.name是char name[255],代表文件名
p[strlen(de.name)] = 0; // 设置文件名结束符
if(strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0) {
continue;
}
find(buf, findName);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[])
{
if(argc < 3){
printf("error argc num");
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}
xargs
cat url-list.txt | xargs wget -c
上面的代码就是通过wget下载url-list.txt里面所有的url链接内容
xargs是什么,就是能接受管道的输出作为命令行参数,运行命令,所以需要调用exec,并把输出存入到参数中,如何读到标准输出(其实输出以后,我们应该从标准输入读),标准输出是什么,也相当于一个管道,我们读的时候read(p[0]),所以我们应该read(0,), 注意不是read(1)
管道两边是如何达成通信: 参考https://blog.csdn.net/Leafage_M/article/details/78893822 这也解答了我关于读和写阻塞的疑惑
exec执行的其实是 其后面跟着的命令,为什么exec(char[] cmd, char * args[])的时候, args[0]不生效? 哦 好像exec的时候args[0]默认是"exec",它自己,所以你改了也没用
#include "kernel/types.h"
#include "user/user.h"
int main(int argc, char *argv[]) {
int p[2];
pipe(p);
char buf[1024] = {"\0"};
char* params[100]; // 用来保存命令行参数
int index = 0;
int k;
for(int i=1; i< argc;i++){
params[index++] = argv[i];
}
while((k = read(0, buf, 1024)) > 0 ) { //0代表的是管道的0,也就是我们从管道读取,而不是标准输入stdin,比如 echo ab | xargs echo cd,我们读取0其实读到的是 ab
char tmp[1024] = {"\0"};
params[index] = tmp;
for(int i=0; i< strlen(buf); i++) {
if(buf[i] == '\n') {
if (fork() ==0) { //创建子线程执行命令
exec(argv[1], params);
}
wait(0); //父线程等待子线程执行完
}
else {
tmp[i] = buf[i]; //读取左边管道的输出作为输入 比如 echo abc | xargs echo def 读取的就是abc
}
// printf("buf %d: %c\n",i, buf[i]);
}
}
exit(0);
}
exec为啥要把echo传进去,然后又只用params的从1开始的元素,好像是exec执行的时候,比如
exec(echo, params),其实是这样的exec(echo,["echo", "abc"]),这样其实执行的就是echo 加后面的"abc",至于为啥要这么设置,而不是直接传一个["abc"]进去,我也不知道
我草,我发现把问题在博客中写出来,问题就解决了?以后遇见问题,就写博客。。。