操作系统实验报告
1. myecho.c
1.1. 实验内容
- myecho.c的功能与系统echo程序相同
- 接受命令行参数,并将参数打印出来
1.2. 效果展示
myecho$ ./myecho a b c
a b c
1.3. 实验思路和关键代码
读取输入的参数,按顺序输出
int main(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
{
printf("%s ", argv[i]);
}
printf("\n");
return 0;
}
2. mycat.c
2.1. 实验内容
- mycat.c的功能与系统cat程序相同
- mycat将指定的文件内容输出到屏幕
- 要求使用系统调用open/read/write/close实现
2.2. 效果展示
mycat$ ./mycat mycat.c
//以下显示mycat.c文件的内容
2.3. 实验思路和关键代码
- 读取参数, 根据参数打开相应文件
FILE *fp;
int fsize;
int fr;
char *buffer;
fp = fopen(argv[1], "r");
if (!fp)
{
printf("不能打开该文件\n");
exit(0);
}
- 通过设置文件指针读取文件大小,分配缓冲区
fseek(fp, 0, SEEK_END);
fsize = ftell(fp);
rewind(fp);
buffer = (char *)malloc((1 + fsize) * sizeof(char));
if (!buffer)
{
printf("分配空间失败\n");
exit(0);
}
- 将文件读取到缓冲区并输出
fr = fread(buffer, 1, fsize, fp);
if (!fr)
{
printf("读文件失败\n");
exit(0);
}
printf("%s\n", buffer);
3. mycp.c
3.1. 实验内容
- mycp.c的功能与系统cp程序相同
- 将源文件复制到目标文件
- 要求使用系统调用open/read/write/close实现
3.2. 效果演示
mycp$ ./mycp mycp.c mycp1.c
//将mycp.c的内容复制到了mycp1.c
3.3. 实验思路和关键代码
- 与2.3.中相同, 不过在将源文件写到缓冲区后, 需要打开/创建目的文件, 将缓冲区内容写到目的文件
fp = fopen(argv[2], "w");
if (!fp)
{
printf("打开目的文件失败\n");
exit(0);
}
fwrite(buffer, 1, fsize, fp);
4. mysys.c
4.1. 实验内容
实现函数mysys,用于执行一个系统命令,要求如下:
- mysys的功能与系统函数system相同,要求用进程管理相关系统调用自己实现一遍
- 使用fork/exec/wait系统调用实现mysys
- 不能通过调用系统函数system实现mysys
4.2. 效果演示
测试程序
int main()
{
printf("---------------------------------\n");
mysys("echo a b c d");
printf("---------------------------------\n");
mysys("ls /");
printf("---------------------------------\n");
return 0;
}
输出结果
---------------------------------
a b c d
---------------------------------
bin boot dev etc home init lib lib32 lib64 libx32 media mnt opt proc root run sbin snap srv sys tmp usr var
---------------------------------
4.3. 实验思路和关键代码
通过在子进程中使用execl()函数调用sh命令实现简单的系统命令调用
pid = fork();
if (pid == 0)
execl("/bin/sh", "sh", "-c", str, NULL);
wait(NULL);
sh命令是shell命令语言解释器,执行命令从标准输入读取或从一个文件中读取。通过用户输入命令,和内核进行沟通
5. sh3.c
5.1. 实验内容
- 该程序读取用户输入的命令,调用函数mysys执行用户的命令
- 考虑如何实现内置命令cd、pwd、exit
- 实现文件重定向
- 实现管道
- 只要求连接两个命令,不要求连接多个命令
- 不要求同时处理管道和重定向
5.2. 效果演示
- 打开后自动显示工作路径(pwd)
sh$ ./sh3
当前工作目录是: /计算机操作系统/实验/homework/sh
>
- 读取指令并执行
当前工作目录是: /计算机操作系统/实验/homework/sh
> echo a b c
a b c
当前工作目录是: /计算机操作系统/实验/homework/sh
>
- cd指令
当前工作目录是: /计算机操作系统/实验/homework/sh
> cd /
当前工作目录是: /
> ls
bin boot dev etc home init lib lib32 lib64 libx32 media mnt opt proc root run sbin snap srv sys tmp usr var
- exit指令
当前工作目录是: /
> exit
已经退出shell
- 重定向
当前工作目录是: /计算机操作系统/实验/homework/sh
> echo a b c >out.txt
out.txt文件内容
a b c
- 管道
当前工作目录是: /计算机操作系统/实验/homework/sh
> cat /etc/passwd | wc -l
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
// 内容过多以下省略
5.3. 实验思路和关键代码
- 实验的要求是从sh1到sh3逐渐增加的,其代码也是从简单到复杂逐步构建
字符串分割
- 由于要处理输入的命令,所以我们要进行字符串的分割
在实验过程中分析需求,决定构建以下数据结构,储存命令的缓冲区和需要进行分割得到的结构体
#define BUF_LEN 1024
char buffer[BUF_LEN];
#define ARGV_LEN 32
struct Command
{
int argc; //分割串的数量
int redirectF; //重定向标记
int pipLineF; //管道标记
char *argv[ARGV_LEN];
};
struct Command command;
- 对命令进行分割, 使用strtok函数
C 库函数 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。
- 对命令进行分割时,需要判断是否有’>‘重定向和’|‘管道命令。由于实验中并未同时支持重定向和管道命令,所以判断到’>‘或’|'时直接将它们对应的标记位置为其在char* argv[]中的位置
- 分割部分代码
//进行分割
char *buf;
buf = strtok(buffer, " ");
while (buf != NULL)
{
if (buf[0] == '>')
{
command.argv[command.argc] = NULL;
command.redirectF = command.argc;
command.argc++;
command.argv[command.argc] = (char *)malloc(sizeof(char) * (strlen(buf) +