本次实验课的内容涉及到以下几个方面:
1.文件的内核实现
2.标准的输入输出
3.系统调用dup和pipe
文件最基本的系统调用是open和close,open一个文件后会返回一个fd,作为这个文件的标识,后续有关于这个文件的操作都使用fd去完成。
1.文件的内核实现
文件的内核实现中围绕着四个结构体:索引节点、file结构体、文件描述符表、进程控制块。下面介绍下它们各自存储的内容:
索引节点中保存了文件的大小、文件存放在磁盘的位置、文件的创建时间、修改时间、文件的所有者、文件的访问权限
file结构体file结构体存了被打开文件的信息、文件对应的索引节点(inode)、文件的当前访问位置、文件的打开模式(只读、只写、可读可写)
文件描述符表是一个数组,数组的元素类型是指针,指针指向一个file结构体,用于保存被打开的文件。当内核打开文件时,分配一个file结构体表示被打开的文件,将该file结构体指针保存在文件描述符表中
进程控制块是操作系统表示进程状态的数据结构,存放用于描述进程情况及控制进程运行所需的全部信息(进程标识信息、处理机状态、进程调度信息、打开文件列表,即文件描述符表,记录了该进程打开的文件)
2.标准的输入输出
标准的输入输出就是指write和read
write(1, buf, sizeof(buf)); // 标准输入,就是键盘输入
read(0, buf, sizeof(buf)); //标准输出,就是终端输出
3.系统调用dup和pipe
dup(重定向)
dup就是让一个新的fd去指向一个文件,这样文件就有了两个指向。可以把老fd解放出来,去完成其他操作。
还有一个变形的dup2,完成一样的功能。调用方式不同
pipe(管道)
pipe的实现是基于队列,一端作为输入端,一端作为输出端。整体结构如下:
以上就是这次实验课的全部理论学习啦,下面看看作业吧!这次的作业没有关于dup和pipe的内容,是上节课mysys实现的变形。
源代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
void mysys(char *command) {
char *argv[512]; // 这里必须要用动态指针数组,因为execvp最后有一个NULL作为结束标志,静态的二维数组是无法实现的。
int num = 0;
char temp[512];
for (int i=0; command[i]!='\0'; i++) {
int index = 0;
while (command[i]!='\0' && command[i] != ' ') {
temp[index++] = command[i++];
}
temp[index] = '\0';
if (command[i] == '\0')
break;
argv[num] = (char *)malloc(strlen(temp));
strcpy(argv[num], temp);
num++;
}
argv[num] = (char *)malloc(strlen(temp));
strcpy(argv[num], temp);
num++;
argv[num] = NULL;
pid_t pid;
if (command == NULL)
return ;
if ((pid = fork()) < 0) {
}
else if (pid == 0) {
execvp(argv[0], argv);
exit(127);
}
else {
sleep(1);
}
return ;
}
int main() {
while (1) {
write(1, "> ", 3); // 此处可以使用write
/* printf("> ");
如果这样使用printf是不可以的,printf有输出缓冲区,遇到\n才会统一调用一次write。实现的方式有两种:
1.printf("\n")
2.fflush(stdout);
*/
// 在底层编程时,使用printf就可以,更方便
char buf[80];
int count;
count = read(0, buf, sizeof(buf));
// buf[count] = 0; //这个是错的,输入回车后,末尾还会读入一个\n
buf[count-1] = 0;
if (strcmp(buf, "exit") == 0)
//exit(127)也可以;
break;
mysys(buf);
}
return 0;
}
第四次,打板儿!
第五次的内容和上次一样的,区别的是作业,就写在一篇里了。
源代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
void mysys(char *command) {
char *argv[512]; // 这里必须要用动态指针数组,因为execvp最后有一个NULL作为结束标志,静态的二维数组是无法实现的。
int num = 0;
char temp[512];
for (int i=0; command[i]!='\0'; i++) {
int index = 0;
while (command[i]!='\0' && command[i] != ' ') {
temp[index++] = command[i++];
}
temp[index] = '\0';
if (command[i] == '\0')
break;
argv[num] = (char *)malloc(strlen(temp));
strcpy(argv[num], temp);
num++;
}
argv[num] = (char *)malloc(strlen(temp));
strcpy(argv[num], temp);
num++;
argv[num] = NULL;
pid_t pid;
if (command == NULL)
return ;
if ((pid = fork()) < 0) {
}
else if (pid == 0) {
/*
思路:对输入命令进行判断,如果是echo再进一步判断有没有重定向标志: >目标文件,如果有按重定向方法写入指定文件,否则按正常echo输出`
*/
if (strcmp(argv[0], "echo") == 0) {
int i, j;
int tag = 0;
for (i=0; i<num; i++) {
for (j=0; argv[i][j] != '\0'; j++) {
if (argv[i][j] = '>' && argv[i][j+1] != '\0' && i == num-1) {
tag = 1;
break;
}
}
if (tag == 1)
break;
}
if (tag == 1) { // tag为1说明存在重定向,下面截取目标文件名及写入内容
// 截取文件名
char filename[10];
int filename_index = 0;
for (int j=1; argv[i][j]!='\0'; j++) {
filename[filename_index++] = argv[i][j];
}
filename[filename_index] = '\0';
char content[20];
int content_index = 0;
// 截取内容,已知为echo命令,从5号以后为内容
for (int k=5; command[k]!='\0'; k++) {
if (command[k] == '>') {
content[content_index] = '\0';
break;
}
else
content[content_index++] = command[k];
}
// 使用重定向方式,将fd改为1
int fd;
fd = open(filename, O_CREAT|O_RDWR, 0777);
dup2(fd, 1); // 函数格式(源,目标)
write(1, content, sizeof(content));
}
else {
execvp(argv[0], argv);
}
}
else
execvp(argv[0], argv);
exit(127);
}
else {
sleep(1);
}
return ;
}
int main() {
while (1) {
write(1, "> ", 3); // 此处需要使用write,用printf的话会不输出
char buf[80];
int count;
count = read(0, buf, sizeof(buf));
// buf[count] = 0; //这个是错的,输入回车后,末尾还会读入一个\n
buf[count-1] = 0;
if (strcmp(buf, "exit") == 0)
break;
mysys(buf);
}
return 0;
}
这次的作业主要是增加一个要求:echo另外一个内容写入指定文件(之前的echo是输出到屏幕,重定向后将1号指向文件,就不会在上输出了)。但悲催的是:我这份代码能够完成新功能,对于普通功能的echo没用了(呜呜呜),也就是echo a b c没了反应,到现在也没找到问题。如果有人看出来,请在下方评论区留言,感谢!
上节课老师还让实现pwd,cd,使用execvp无法完成,想了好久都没有结果,但其实是有系统调用的:
char *cwd = getcwd(buff,sizeof(buff)); // pwd,返回值cwd即为结果
chdir(comnd[1]); // cd,comnd为路径
因作者水平有限,如有错误之处,请在下方评论区指正,谢谢!