lab1
最近在学习操作系统的s081课程,想着写一个博客记录一下当时遇到的问题的解决思路
pingpong
写一个程序,使用unix system calls在两个进程间”ping-pong“一个字节,使用一对pipe,一个pipe对应一个方向,另外一个pipe对应另外一个方向。因为管道只能实现半双工通信,所以要实现双向通信的话,需要定义两个管道,一个用于父进程写入,子进程读取;另一个用于子进程写入,父进程读取。
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main()
{
int father_p[2];
int child_p[2];
pipe(father_p);
pipe(child_p);
char buff[] = {'X'};
int pid = fork();
if (pid != 0)
{
// 父进程
close(father_p[0]);
close(child_p[1]);
if (write(father_p[1], buff, 1) != 1)
{
printf("error\n");
exit(1);
};
// read(child_p[0], buff, 1); // 在管道内容为空时,打开read会挂起进程 等待别的进程写入数据
if (read(child_p[0], buff, 1) != 1)
{
printf("error\n");
exit(1);
};
printf("%d: received pong\n", getpid());
wait(0);
}
else
{
// 进入子进程
close(father_p[1]);
close(child_p[0]);
// read(father_p[0], buff, 1);
if (read(father_p[0], buff, 1) != 1)
{
printf("error\n");
exit(1);
};
printf("%d: received ping\n", getpid());
// write(child_p[1], buff, 1);
if (write(child_p[1], buff, 1) != 1)
{
printf("error\n");
exit(1);
};
}
exit(0);
return 0;
}
primes素数筛
用到了素数筛的思想,每个进程保留当前数组最小的,并去除他所有的倍数,接着进入下一个进程。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void func(int *input, int num)
{
if (num == 0)
exit(0);
int p[2];
pipe(p);
int prime = *input;
printf("prime %d\n", prime);
// 子进程将数据写入管道并剔除当前素数的倍数
if (fork() == 0)
{
// 关闭管道输入口
close(p[0]);
for (int i = 0; i < num; i++)
{
if (*(input + i) % prime != 0)
{
write(p[1], input + i, sizeof(int));
}
}
}
// 主进程从管道读取数据并进入递归调用
else
{
int buf;
int counter = 0;
close(p[1]);
// wait(0);
//read会阻塞等待write写入 所以不会引发竞争
//父进程也无需使用wait等待子进程完成数据写入
while (read(p[0], &buf, sizeof(int)) != 0) /
{
*input = buf;
input++;
counter++;
}
func(input - counter, counter);
}
exit(0);
}
int main()
{
int input[34] = {0};
for (int i = 0; i < 34; i++)
{
input[i] = i + 2;
}
func(input, 34);
exit(0);
}
find实验
实现一个建议的find,可以在指定目录下寻找特定的文件,要实现递归查找,参考ls.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;
// 从后往前遍历字符指针 找到最后一个/并在其位置停下
for (p = path + strlen(path); p >= path && *p != '/'; p--)
;
p++; // 将指针移动到最后一个/的前一个字符位置
int i = 0;
while (*p != '\0') // 字符串结尾的'\0'也可以直接用ascII码0表示
{
buf[i++] = *p;
p++;
}
buf[i] = '\0';
// memmove(buf, p, strlen(p));
return buf;
}
void find(char *path, char *name)
{
char buf[512], *p;
int fd;
struct dirent de;
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;
}
// 判断当前的路径是否是要寻找的 若是则输出相关信息
if (strcmp(fmtname(path), name) == 0)
printf("%s\n", path);
switch (st.type)
{
case T_FILE:
// 当前路径是一个文件 因为前面已经判断过了
//只有文件夹才需要进行递归调用 所以这里可以直接break
// 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);
char *stdbuff = fmtname(buf);
if (strcmp(stdbuff, ".") == 0 || strcmp(stdbuff, "..") == 0)
continue;//跳过. 和 ..
p[DIRSIZ] = 0;
if (stat(buf, &st) < 0)
{
printf("ls: cannot stat %s\n", buf);
continue;
}
// if (strcmp(stdbuff, name) == 0)
// printf("%s\n", buf);
// 无论是文件还是文件夹 都通过递归判断
//其实也可以在这里就判断一次 但是这样写会更简洁一点
find(buf, name);
}
break;
}
close(fd);
}
int main(int argc, char *argv[])
{
if (argc <= 2)
{
// ls(".");
printf("Usage: find argc error\n");
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}
xargs实验
实现类似unix xargs类似功能,比如echo hello too|xargs echo bye,要输出bye hello too;
即等价于echo bye hello too,将上个命令输出的每行作为参数,拼接到xargs后面的指令后面。
echo hello too输出为hello too,将其拼接到echo bye后面,就是echo bye hello too。
xargs可以实现的功能是从标准输入读取参数,作为后续命令的命令行参数。具体可以了解一下find . b | args grep 123 和 find . b | grep 123 的区别,前者会将找到的文件作为grep的命令行参数一起执行,这样grep会返回文件内部含有123的行;后者是通过管道将find的结果作为grep的标准输入,所以grep会返回find找到的文件名中含有123的行。
这里需要用fork和exec实现,exec会执行新的程序,替换当前进程的资源和上下文内容,但会保留文件描述符,exec(程序路径, 程序执行的参数);参数数组以0结尾
代码中用到了C语言字符串的一些处理,利用了C语言中字符串数组以0结尾的特性
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"
// xargs: 将标准输入作为后续命令的命令行执行参数
// 加入管道符|后 标准输入就是管道的输出 这里涉及一个close) dup(fd[1])
int main(int argc, char *argv[])
{
char *args[MAXARG] = {0};
int k = 0;
int i = 0, j = 0;
for (i = 1; i < argc; i++)
{
args[k++] = argv[i];
}
char tmp[100] = {0};
int m = 0;
while (read(0, tmp + j, 1) != 0)
{
if (tmp[j] == ' ')
{
tmp[j++] = 0; // 添加字符串结尾标志
// printf("tmp: %s\n", tmp);
// args[k] = (char *)malloc(sizeof(tmp + 1)); // 最后一个需要设置为0
// strcpy(args[k++], tmp);
// 省去开辟堆区空间 直接保存地址
args[k++] = tmp + m;
m = j;
}
else if (tmp[j] == '\n')
{
tmp[j++] = 0;
// printf("tmp: %s\n", tmp);
// args[k++] = tmp;
// args[k] = (char *)malloc(sizeof(tmp + 1)); // 最后一个需要设置为0
// strcpy(args[k++], tmp);
// j = 0;
args[k++] = tmp + m;
m = j;
args[k] = 0;
if (fork() == 0)
{
exec(argv[1], args);
exit(0);
}
wait(0);
k = argc - 1;
}
else
- [ ] List item
1. List item
j++;
}
wait(0);
exit(0);
}
// echo hello world | xargs echo bye echo "1\n2" | xargs echo line
参考文献:
[1]:https://blog.csdn.net/u013577996/article/details/108680888