Command, System Call, and Enhancement
Command, System Call, and Enhancement
NOVEMBER 2020
Department of Computer Science and Engineering
K L Deemed to be University,
Green Fields, Vaddeswaram,
Guntur District, A.P., 522 502.
TABLE OF CONTENTS
CHAPTER NO. TITLE
1 What is XV6?
2 SRS
3 Data Flow Diagrams
4 Modules Description
5 Codes with output
6 Conclusion
WHAT IS XV6?
Xv6 is a re-implementation of the Unix sixth edition in order to use as a learning tool.
xv6 was developed by MIT as a teaching operating system for their “6.828” course. A
vital fact about xv6 is that it contains all the core Unix concepts and has a similar
structure to Unix even though it lacks some functionality that you would expect from
a modern operating system. This is a lightweight operating system where the time to
compile is very low and it also allow remote debugging.
1.2 Scope
With the decrease in the number of people actually learning to work with the base OS
like XV6 due to its lack of functionality even for educational purposes. We took it upon
ourselves to create a Shell in XV6 with all the functionalities which we think is
absolutely necessary for us someone to use it properly without any problem.
1.3 Overview of the system
The system focuses on improving the already existing open source XV6-public OS
distribution by MIT on GitHub and use create the basic shell functionalities like
Copying, Moving and Editing files and also to display all running process. This
means we create a basic working Editor and add extra functionalities into it while
at the same time implementing all missing common Linux commands.
2 General Requirements
Basic XV6 – use the MIT XV6 as a base code and make it run
Copy – Implement a copy function to copy files from one location to
another
Move – enable moving a file from one location to another using
the function
Head – display first 10 lines of any file
Tail – Display last 10 lines of any file
Editor – Create a basic editor to create and modify files
Process Display – display all running process
3 Functional Requirements
3.1 Necessary requirements
The user should have general computer knowledge
The users should have a popular Linux Distribution
User should have a virtualization command like Qemu or Qemu-KVD
User should be comfortable with working on a sole Command-
Line-Interface without any mouse usage
3.2 Technical requirements
Linux Distro with QEMU or any other Virtualization support must be
installed
4 Interface requirements
4.1 Software Requirements
Visual Studio Code – A basic editor for modifying the code
4.2 Hardware Requirements
Intel core i3 processor at 2.0 GHz or higher
256 MB RAM or higher
256 GB Hard disk
6 Performance Requirements
Response time of the system should be as quick as possible.
In case of technical issues, The system should try to handle it
without entering Panic State
7 References
XV6 MIT PDOS
COL331/COL633 Operating Systems Course Lecture Videos
XV6 Survival Guide
Data Flow Models
Level 0 DFD
TABLE DESCRIPTIONS
Main Memory
The RAM and HDD/SSD parts of an OS where all data is finally stored. It does
not lose any data even when the OS enters a panic state or is shut down. It has a
logical memory address or physical memory address. The RAM houses all files
which are for immediate access while the HDD/SSD houses the rest.
Buffers
The buffers are streams or intermediate storages that house all data for
display or modification. The stream 2 is connected straight to the output terminal
and is used for displaying in the Terminal. The other streams are used to carry
around information and commands from all devices and the CPU.
Structures
This Data Store stores all necessary structures required for functioning of a
CPU. This table has predefined structures and cannot be modified unless the
change is done directly to the source code. This data store houses the structures of
Process Statistics or File Structures and is used for initiation of all core
functionalities of a system.
Level 1 DFD
Level 2 DFD
MODULES DESCRIPTION
Copy
Syntax: cp file1 file2
Mandatory Parameters: file1, file2
This module is invoked with help of the command cp. this command
copies the
contents of file1 to file2. but internally what happens is that it reads the
contents
of file1 in a buffer and writes the content in file 2 from the buffer
continuously until
the end of file1, here the file2 is created if it doesn’t exist else it is
overwritten on
the existing file specified. The contents of the file1 is unharmed. Since a
copy of
file1 is created, more space will be occupied in the memory Here all
parameters are
mandatory for the invocation of the module. This module is based on
command
line arguments where the inputs are passed as arguments to the module
when it is
invoked.
Editor
This module is used to open a basic editor that can be used to create a
new file or
view and modify an existing file. The editor can be used to insert, modify
or delete
a particular line. It can also be used to insert a huge block of text. The
editor can
also be used to add lines at end of the file. The editor displays the
number of lines
at each line and that can be used to specify after which line you need to
insert or
modify. When invoked, the editor goes to fetch the filename and if its
nonexistent, it then goes on to create a file of the given name. It then
prints the
whole text along with line numbers and then shows all possible options
to choose
from and execute. At the end, you can choose to exit with or without
saving all
changes.
TASK:1
Code for shell.c:
// Shell.
#include "types.h"
#include "user.h"
#include "fcntl.h"
#define MAXARGS 10
struct cmd {
int type;
};
struct execcmd {
int type;
char *argv[MAXARGS];
char *eargv[MAXARGS];
};
struct redircmd {
int type;
struct cmd *cmd;
char *file;
char *efile;
int mode;
int fd;
};
struct pipecmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct listcmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct backcmd {
int type;
struct cmd *cmd;
};
int fork1(void); // Fork but panics on failure.
void panic(char*);
struct cmd *parsecmd(char*);
if(cmd == 0)
exit();
switch(cmd->type){
default:
panic("runcmd");
case EXEC:
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
exit();
exec(ecmd->argv[0], ecmd->argv);
printf(2, "exec %s failed\n", ecmd->argv[0]);
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode) < 0){
printf(2, "open %s failed\n", rcmd->file);
exit();
}
runcmd(rcmd->cmd);
break;
case LIST:
lcmd = (struct listcmd*)cmd;
if(fork1() == 0)
runcmd(lcmd->left);
wait();
runcmd(lcmd->right);
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
if(pipe(p) < 0)
panic("pipe");
if(fork1() == 0){
close(1);
dup(p[1]);
close(p[0]);
close(p[1]);
runcmd(pcmd->left);
}
if(fork1() == 0){
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
runcmd(pcmd->right);
}
close(p[0]);
close(p[1]);
wait();
wait();
break;
case BACK:
bcmd = (struct backcmd*)cmd;
if(fork1() == 0)
runcmd(bcmd->cmd);
break;
}
exit();
}
int
getcmd(char *buf, int nbuf)
{
printf(2, "$ ");
memset(buf, 0, nbuf);
gets(buf, nbuf);
if(buf[0] == 0) // EOF
return -1;
return 0;
}
int
main(void)
{
static char buf[100],bufx[100];
int fd;
void
panic(char *s)
{
printf(2, "%s\n", s);
exit();
}
int
fork1(void)
{
int pid;
pid = fork();
if(pid == -1)
panic("fork");
return pid;
}
//PAGEBREAK!
// Constructors
struct cmd*
execcmd(void)
{
struct execcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = EXEC;
return (struct cmd*)cmd;
}
struct cmd*
redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
{
struct redircmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = REDIR;
cmd->cmd = subcmd;
cmd->file = file;
cmd->efile = efile;
cmd->mode = mode;
cmd->fd = fd;
return (struct cmd*)cmd;
}
struct cmd*
pipecmd(struct cmd *left, struct cmd *right)
{
struct pipecmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = PIPE;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
struct cmd*
listcmd(struct cmd *left, struct cmd *right)
{
struct listcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = LIST;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
struct cmd*
backcmd(struct cmd *subcmd)
{
struct backcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = BACK;
cmd->cmd = subcmd;
return (struct cmd*)cmd;
}
//PAGEBREAK!
// Parsing
int
gettoken(char **ps, char *es, char **q, char **eq)
{
char *s;
int ret;
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
if(q)
*q = s;
ret = *s;
switch(*s){
case 0:
break;
case '|':
case '(':
case ')':
case ';':
case '&':
case '<':
s++;
break;
case '>':
s++;
if(*s == '>'){
ret = '+';
s++;
}
break;
default:
ret = 'a';
while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
s++;
break;
}
if(eq)
*eq = s;
int
peek(char **ps, char *es, char *toks)
{
char *s;
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
*ps = s;
return *s && strchr(toks, *s);
}
struct cmd*
parsecmd(char *s)
{
char *es;
struct cmd *cmd;
es = s + strlen(s);
cmd = parseline(&s, es);
peek(&s, es, "");
if(s != es){
printf(2, "leftovers: %s\n", s);
panic("syntax");
}
nulterminate(cmd);
return cmd;
}
struct cmd*
parseline(char **ps, char *es)
{
struct cmd *cmd;
struct cmd*
parsepipe(char **ps, char *es)
{
struct cmd *cmd;
struct cmd*
parseredirs(struct cmd *cmd, char **ps, char *es)
{
int tok;
char *q, *eq;
struct cmd*
parseblock(char **ps, char *es)
{
struct cmd *cmd;
struct cmd*
parseexec(char **ps, char *es)
{
char *q, *eq;
int tok, argc;
struct execcmd *cmd;
struct cmd *ret;
ret = execcmd();
cmd = (struct execcmd*)ret;
argc = 0;
ret = parseredirs(ret, ps, es);
while(!peek(ps, es, "|)&;")){
if((tok=gettoken(ps, es, &q, &eq)) == 0)
break;
if(tok != 'a')
panic("syntax");
cmd->argv[argc] = q;
cmd->eargv[argc] = eq;
argc++;
if(argc >= MAXARGS)
panic("too many args");
ret = parseredirs(ret, ps, es);
}
cmd->argv[argc] = 0;
cmd->eargv[argc] = 0;
return ret;
}
if(cmd == 0)
return 0;
switch(cmd->type){
case EXEC:
ecmd = (struct execcmd*)cmd;
for(i=0; ecmd->argv[i]; i++)
*ecmd->eargv[i] = 0;
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
nulterminate(rcmd->cmd);
*rcmd->efile = 0;
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
nulterminate(pcmd->left);
nulterminate(pcmd->right);
break;
case LIST:
lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left);
nulterminate(lcmd->right);
break;
case BACK:
bcmd = (struct backcmd*)cmd;
nulterminate(bcmd->cmd);
break;
}
return cmd;
}
Screenshot of code:
OUTPUT:
TASK-2:editor
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
#include "fs.h"
//???????
int changed = 0;
int auto_show = 1;
//??????
show_text(text);
//????
com_help(text);
//????
char input[MAX_LINE_LENGTH] = {};
while (1)
{
printf(1, "\nplease input command:\n");
memset(input, 0, MAX_LINE_LENGTH);
gets(input, MAX_LINE_LENGTH);
int len = strlen(input);
input[len-1] = '\0';
len --;
//??????????
int pos = MAX_LINE_LENGTH - 1;
int j = 0;
for (; j < 8; j++)
{
if (input[j] == ' ')
{
pos = j + 1;
break;
}
}
//ins
if (input[0] == 'i' && input[1] == 'n' && input[2] == 's')
{
if (input[3] == '-'&&stringtonumber(&input[4])>=0)
{
com_ins(text, stringtonumber(&input[4]), &input[pos]);
//??????????
line_number = get_line_number(text);
}
else if(input[3] == ' '||input[3] == '\0')
{
com_ins(text, line_number+1, &input[pos]);
line_number = get_line_number(text);
}
else
{
printf(1, "invalid command.\n");
com_help(text);
}
}
//mod
else if (input[0] == 'm' && input[1] == 'o' && input[2] == 'd')
{
if (input[3] == '-'&&stringtonumber(&input[4])>=0)
com_mod(text, atoi(&input[4]), &input[pos]);
else if(input[3] == ' '||input[3] == '\0')
com_mod(text, line_number + 1, &input[pos]);
else
{
printf(1, "invalid command.\n");
com_help(text);
}
}
//del
else if (input[0] == 'd' && input[1] == 'e' && input[2] == 'l')
{
if (input[3] == '-'&&stringtonumber(&input[4])>=0)
{
com_del(text, atoi(&input[4]));
//??????????
line_number = get_line_number(text);
}
else if(input[3]=='\0')
{
com_del(text, line_number + 1);
line_number = get_line_number(text);
}
else
{
printf(1, "invalid command.\n");
com_help(text);
}
}
else if (strcmp(input, "show") == 0)
{
auto_show = 1;
printf(1, "enable show current contents after text changed.\n");
}
else if (strcmp(input, "hide") == 0)
{
auto_show = 0;
printf(1, "disable show current contents after text changed.\n");
}
else if (strcmp(input, "help") == 0)
com_help(text);
else if (strcmp(input, "save") == 0 || strcmp(input, "CTRL+S\n") == 0)
com_save(text, argv[1]);
else if (strcmp(input, "exit") == 0)
com_exit(text, argv[1]);
else
{
printf(1, "invalid command.\n");
com_help(text);
}
}
//setProgramStatus(SHELL);
exit();
}
//??src??n????dest
char* strcat_n(char* dest, char* src, int len)
{
if (len <= 0)
return dest;
int pos = strlen(dest);
if (len + pos >= MAX_LINE_LENGTH)
return dest;
int i = 0;
for (; i < len; i++)
dest[i+pos] = src[i];
dest[len+pos] = '\0';
return dest;
}
//?????????,?0??,?return x??text[0]?text[x]??
int get_line_number(char *text[])
{
int i = 0;
for (; i < MAX_LINE_NUMBER; i++)
if (text[i] == NULL)
return i - 1;
return i - 1;
}
//????,n????????,?1??
//extra:??????????,????????
void com_ins(char *text[], int n, char *extra)
{
if (n < 0 || n > get_line_number(text) + 1)
{
printf(1, "invalid line number\n");
return;
}
char input[MAX_LINE_LENGTH] = {};
if (*extra == '\0')
{
printf(1, "please input content:\n");
gets(input, MAX_LINE_LENGTH);
input[strlen(input)-1] = '\0';
}
else
strcpy(input, extra);
int i = MAX_LINE_NUMBER - 1;
for (; i > n; i--)
{
if (text[i-1] == NULL)
continue;
else if (text[i] == NULL && text[i-1] != NULL)
{
text[i] = malloc(MAX_LINE_LENGTH);
memset(text[i], 0, MAX_LINE_LENGTH);
strcpy(text[i], text[i-1]);
}
else if (text[i] != NULL && text[i-1] != NULL)
{
memset(text[i], 0, MAX_LINE_LENGTH);
strcpy(text[i], text[i-1]);
}
}
if (text[n] == NULL)
{
text[n] = malloc(MAX_LINE_LENGTH);
if (text[n-1][0] == '\0')
{
memset(text[n], 0, MAX_LINE_LENGTH);
strcpy(text[n-1], input);
changed = 1;
if (auto_show == 1)
show_text(text);
return;
}
}
memset(text[n], 0, MAX_LINE_LENGTH);
strcpy(text[n], input);
changed = 1;
if (auto_show == 1)
show_text(text);
}
//????,n????????,?1??
//extra:??????????,?????????
void com_mod(char *text[], int n, char *extra)
{
if (n <= 0 || n > get_line_number(text) + 1)
{
printf(1, "invalid line number\n");
return;
}
char input[MAX_LINE_LENGTH] = {};
if (*extra == '\0')
{
printf(1, "please input content:\n");
gets(input, MAX_LINE_LENGTH);
input[strlen(input)-1] = '\0';
}
else
strcpy(input, extra);
memset(text[n-1], 0, MAX_LINE_LENGTH);
strcpy(text[n-1], input);
changed = 1;
if (auto_show == 1)
show_text(text);
}
//????,n????????,?1??
void com_del(char *text[], int n)
{
if (n <= 0 || n > get_line_number(text) + 1)
{
printf(1, "invalid line number\n");
return;
}
memset(text[n-1], 0, MAX_LINE_LENGTH);
int i = n - 1;
for (; text[i+1] != NULL; i++)
{
strcpy(text[i], text[i+1]);
memset(text[i+1], 0, MAX_LINE_LENGTH);
}
if (i != 0)
{
free(text[i]);
text[i] = 0;
}
changed = 1;
if (auto_show == 1)
show_text(text);
}
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
int
main()
{
char buf[512];
int fd, i, sectors;
sectors = 0;
while(1){
*(int*)buf = sectors;
int cc = write(fd, buf, sizeof(buf));
if(cc <= 0)
break;
sectors++;
if (sectors % 100 == 0)
printf(2, ".");
}
close(fd);
fd = open("big.file", O_RDONLY);
if(fd < 0){
printf(2, "big: cannot re-open big.file for reading\n");
exit();
}
for(i = 0; i < sectors; i++){
int cc = read(fd, buf, sizeof(buf));
if(cc <= 0){
printf(2, "big: read error at sector %d\n", i);
exit();
}
if(*(int*)buf != i){
printf(2, "big: read the wrong data (%d) for sector %d\n",
*(int*)buf, i);
exit();
}
}
exit();
}
Output:-
--Currently xv6 files are limited to 140 sectors, or 71,680 bytes. This
limit comes from the fact that an xv6 inode contains 12 "direct" block
numbers and one "singly-indirect" block number, which refers to a block
that holds up to 128 more block numbers, for a total of 12+128=140.
You'll change the xv6 file system code to support a "triply-indirect" block
in each inode, containing 128 addresses of doubly-indirect blocks, each
of which can contain up to 16384 addresses of data blocks.
TASK-5
Code :
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int SRCFD,TRGTFD,Flag,RDflag;
char Data[100];
SRCFD=open("F2.txt",O_RDONLY);
Flag = dup2(SRCFD,TRGTFD);
RDflag = read(TRGTFD,Data,sizeof(Data));
write(1,Data,sizeof(Data));
close(SRCFD);
}
Output:-
CONCLUSION
We successfully created a basic XV6 shell with what our team believes to
be necessary for a common usage. We learnt a lot from working with a
basic Operating System and would like to thank everyone for this
opportunity. The journey to modifying the XV6 and implementing our own
shell was a very interesting and eventful one and even though sometimes,
our code was like a shot in the dark, we believe that we achieved what we
wanted to in the end.