mit6.s081 lab1实验记录

本文记录了MIT操作系统课程lab1的四个实验:使用Unix系统调用实现进程间的pingpong通信,通过素数筛法找素数,实现递归查找的find命令,以及构建类似xargs的功能,将标准输入作为命令参数。
摘要由CSDN通过智能技术生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值