HITsz os-lab xv6-util

本文介绍了四个操作系统实验,包括使用sleep系统调用暂停进程,通过管道实现pingpong通信,使用管道实现质数筛选,以及实现find和xargs命令。这些实验展示了如何利用无名管道进行父子进程间的通信以及在xv6操作系统中处理进程交互和文件系统操作。
摘要由CSDN通过智能技术生成

HITsz os-lab xv6-util

1.sleep

题目

输入 sleep 命令后,命令行会 ”暂停“ 一段时间 (10个ticks,ticks由内核定义),然后输出"(nothing happens for a little while)"。

实现原理

调用系统调用 sleep(int)

代码

#include "kernel/types.h"
#include "user.h"

int main(int argc,char* argv[]){
    if(argc != 2){
        printf("Sleep needs one argument!\n"); //检查参数数量是否正确
        exit(-1);
    }
    int ticks = atoi(argv[1]); //将字符串参数转为整数
    sleep(ticks);              //使用系统调用sleep
    printf("(nothing happens for a little while)\n");
    exit(0); //确保进程退出
}

2.pingpong

题目

父进程向子进程发送 ping, 子进程接收并打印后向父进程打印 pong,父进程接受并打印

实现原理

使用 pipe(int *) 创建无名管道在父子进程中通信

代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char **argv)
{
    //无名管道为半双工通道:可以接受和发送信息,但在同一时间只能接受或发送信息
    int fd1[2];	//父进程向子进程发送通道
    int fd2[2];	//子进程向父进程发送通道

    pipe(fd1);	//创建管道
    pipe(fd2);	//创建管道

    if(fork() == 0)	//子进程
    {
        close(fd1[1]);	//该通道子进程只读,关闭写端,下同
        close(fd2[0]);	//该通道子进程只写,关闭读端,下同

        char buf[32];	

        int pi = read(fd1[0], buf, 32 * sizeof(char));	//读入最多32字节的数据,返回值为实际读到数据大小,下同

        buf[pi] = '\0';	//置字符串末尾下一个字符为'\0',便于打印

        printf("%d: received %s\n", getpid(), buf);
        

        write(fd2[1], "pong", 4 * sizeof(char));	//写入"pong"字符串,大小为4 * sizeof(char)
        exit(0);
    }
    else		//父进程
    {
        close(fd1[0]);
        close(fd2[1]);

        write(fd1[1], "ping", 4 * sizeof(char));

        char buf[32];
        int pi = read(fd2[0], buf, 32 * sizeof(char));

        buf[pi] = '\0';

        printf("%d: received %s\n", getpid(), buf);

        wait(0);	//等待子进程结束
        exit(0);
    }

}

3.primes

题目

在xv6上使用管道实现“质数筛选”, 输出2~35之间的而所有质数。

实现原理

以管道为筛子, 将接收到的第一个数为本轮的基本质数并输出,然后继续循环接收整数,若该数为基数的倍数则丢弃,否则放入数组,并在接收结束后发送给子进程。

代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

void get_prime(int fd[])
{
    close(fd[1]);	//本进程只读,关闭写端

    int nums[35];	//储存未被筛掉的数
    int cnt = 0;	//记录筛选出的数字数量
    int fir = 0;	//本轮基本质数
    int temp;		//缓存读取到的数
    int len = 0;	//读取到的数据长度

   
    len = read(fd[0], (void *)&fir, sizeof(int));	//读入基本质数

    if(fir == 0) exit(0);	//没有读到基本质数,程序结束

    printf("prime %d\n", fir);
    
    //read the remain num
    while(len > 0)	//循环读入数字直到读取完毕
    {
        len = read(fd[0], &temp, sizeof(int));
        if(len > 0)
        {
            if(temp%fir != 0)	//读入数字不为基本质数的倍数,放入数组
            {
                nums[cnt++] = temp;
            }
        }
    }

    close(fd[0]);	//关闭读端,释放文件描述符,防止递归深度过大将文件描述符消耗完,下同
    
    int m_fd[2];	//本轮父子进程通信管道
    pipe(m_fd);
    if(fork() != 0)
    {
        close(m_fd[0]);
        write(m_fd[1], &nums, cnt * sizeof(int));	//父进程发送数据给子进程
        close(m_fd[1]);
        wait(0);	//等待子进程结束
        exit(0);
    }
    else
    {
        get_prime(m_fd);	//子进程嵌套进行质数筛选
    } 

}

int main(int argc, char *argv[])
{
    int nums[34];
    int i;
    for(i = 2; i < 36; ++i)
    {
        nums[i - 2] = i;
    }

    int fd[2];
    pipe(fd);
    

    if(fork() != 0)
    {
        close(fd[0]);   
        write(fd[1], &nums, 34 * sizeof(int));
        close(fd[1]);
        wait(0);
        exit(0);
    } 
    else
    {
        get_prime(fd);
    }

    return 0;

}

4.find

题目

在xv6上实现用户程序find,即在目录树中查找名称与字符串匹配的所有文件,输出文件的相对路径。该程序的 命令格式为“find path file_name”

实现原理

类似ls实现,搜索本文件夹下所有文件,若名字相同则输出,若为文件夹则递归地进行搜索

代码

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

char*
fmtname(char *path)	//	从字符串 "a/b/c/d" 中获取 字符串 "d",基本原理为从后往前找到第一个'/'
{
  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));
  buf[strlen(p)] = '\0';

  return buf;
}

void cmp(char *a, char *b)	//比较两个文件名是否相同并打印路径
{
    if(!strcmp(fmtname(a), b))
        printf("%s\n", a);
}

void
find(char *path, char *target)
{
  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;
  }

  switch(st.type){
  case T_FILE://该文件为文件时
    cmp(path, target);
    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)){
  		//若文件夹下文件数量为0,1或者该文件夹名字为"."或".."则不进入,防止套娃
      if(de.inum == 0 || de.inum == 1 || strcmp(de.name, ".")==0 || strcmp(de.name, "..")==0)
        continue;
      memmove(p, de.name, strlen(de.name));	//将文件名追加到路径,递归find
      p[strlen(de.name)] = '\0';
      find(buf, target);
    }
    break;
  }
  close(fd);
}

int
main(int argc, char *argv[])
{
    

    if(argc < 3){
        printf("Please input the right arg!\n");
        exit(0);
    }
  
    find(argv[1], argv[2]);
    exit(0);
}

5.xargs

题目

在xv6上实现用户程序xargs,即从标准输入中读取行并 为每行运行一次 指定的命令,且将该行作为命令的参数提供。

实现原理

循环地从标准输出读取数据,并以’\n’为为分割, 将每一行输出作为xargs 后命令的参数并执行

代码

#include "kernel/types.h"
#include "user/user.h"



int main(int argc, char *argv[])
{
    int i;
    int j = 0;
    char *pi_buf[32];		//储存所有参数的字符串指针数组
    char total_buf[256];	//储存所有输入的缓存区
    char *buf = total_buf;	//指向当前处理到的缓存区位置
    char *pi;				//指向当前处理字符串的开头
    pi = buf;				
    for(i = 1; i < argc; ++i)	//首先载入 xargs 后指令自带的参数
    {
        pi_buf[j++] = argv[i]; 
    }

    int len;
    int sum = 0;
    while((len = read(0, buf, 32)) > 0)//循环读入标准输入 0
    {
        sum += len;	//当标准输入长度大于256时退出
        if(sum > 256)
        {
            printf("the args is too long!\n");
            exit(0);
        }
        
        for(i = 0; i < len; ++i)	//处理该轮读入的数据
        {
            if(buf[i] == ' ')//读到了一个字符串的结尾
            {
                buf[i] = 0;	//手动将字符串结尾置为'\0': c风格字符串以'\0’为结尾
                pi_buf[j++] = pi;	//在参数数组中加入新的参数字符串
                pi = &buf[i+1];	//pi指向下一个字符串的开头(如果有的话)
            }
            else if(buf[i] == '\n')	//读到了一行的结尾
            {
                buf[i] = 0;
                pi_buf[j++] = pi;
                pi = &buf[i + 1];
                //上同读到字符串结尾的操作
                
                //将参数列表尾置0, exec要求参数列表第一个为指令本身,列表尾为0,即{cmd,arg1,arg2,..,0}
                pi_buf[j] = 0;	
                if(fork() == 0)	//启动子进程执行命令
                {
                    exec(argv[1], pi_buf);
                    exit(0);
                }
                else
                {
                    //处理完标准输出的一行,将参数列表置回初始状态
                    //{cmd, arg1, arg2, extern_arg1, extern_arg1,.. , 0} -> {cmd, arg1, arg2, }
                    j = argc - 1;
                    wait(0);
                }
            }
        }
        //当前缓冲区处理位置更新
        buf = buf + len;
    }

    exit(0);
    return 0;

}

实验结果

请添加图片描述

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值