1. A Makefile that compiles the shell

CC      = gcc
CCFLAGS = -g 
LD      = gcc 
LDFLAGS = 
CLEAN   = rm -f *.o cse451sh 
  

%.o: %.c 
	$(CC) $(CCFLAGS) -c $< -o $@ 

all: 451sh 

clobber: clean all 

451sh: shell.o 
	${LD} ${LDFLAGS} $^ -o $@ 

clean: 
	$(CLEAN) 

 

2. Source for the shell

/***********************************************************
 * A skeletal Linux shell that handles the execution of user 
 * programs and three system calls: exit, physusage, clear_physusage
 **********************************************************/ 

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <linux/kernel.h> 
#include <linux/sys.h> 
#include <sys/syscall.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/wait.h> 

#define __NR_physusage  223
#define __NR_clearphysusage  224

#define MAX_INPUT 1024 
#define DELIMITER " \t\n" 

/* parses user input by whitespace and returns an array of words */ 
char **parse_input(char *instr); 

/* frees any dynamic memory we use in the token buffer */ 
void freeinput(char **input); 

int main(int argc, char *argv[]) 
{ 
    /* forked process id */ 
    int procid; 

    /* variables for calculating page requests */
    int total;
    int usageArray[MAX_ORDER];

    /* input buffer to hold user input */ 
    char inputbuffer[MAX_INPUT]; 

    /* Just one character for flushing stdin*/ 
    char ch; 

    /* this will be the array of the file path and arguments */ 
    char **tok_input = NULL; 

    /* this structure holds various information about the system */ 
    struct sysinfo ansr; 
    int count = -1; 
    int errtrap = 0; 

    /* A loop variable */
    int i;

    /* loop until we get the exit command */ 
    while(1) { 
        printf("CSE451shell%% "); 

        /* grab a line of user input */ 
        if (!(fgets(inputbuffer, MAX_INPUT, stdin))) { 
            fprintf(stderr, "\nINPUT ERROR: error with fgets\n"); 
            continue; 
        } 

        /* Did we exceed the input buffer? */ 
        if (strlen(inputbuffer)==MAX_INPUT-1 && inputbuffer[MAX_INPUT-2]!='\n') { 
            fprintf(stderr,"\nSorry, but the input is limited to %d chars. \n\"%s\" not accepted.\nTry again.\n",MAX_INPUT,inputbuffer); 
            
            /* Discard rest of input line */ 
            while ((ch = getchar()) != '\n' && ch != EOF); 
            continue; 
        } 
  
        /* our input should be split by ' ', '\t' and '\n' after this call */ 
        tok_input = parse_input(inputbuffer); 

        /* If empty input start over */ 
        if (tok_input == NULL) continue; 
  
        /* "exit" syscall just ends this program */ 
        if (strcmp("exit", tok_input[0]) == 0) { 
            exit(0);

        } else if (strcmp("chdir", tok_input[0]) == 0) {
            if (tok_input[1] == NULL) {
                fprintf(stderr, "\nchdir: Not enough arguments\n");
            } else if (!chdir(tok_input[1])) {
                fprintf(stderr, "\nchdir: errno %d\n",errno);
            }

        } else if (strcmp("physusage", tok_input[0]) == 0) {
            if (syscall(__NR_physusage, usageArray)) {
                fprintf(stderr, "\nSyscall %d returned errno: %d\n",__NR_physusage,errno);

            } else {
                /* get total */
                for (i = 0, total = 0; i < MAX_ORDER; i++) {
                    total += usageArray[i];
                }

                /* print statistics */
                printf("\nTotal requests to page_alloc: %d\n\n", total);
                for (i = 0; i < MAX_ORDER; i++) {
                    if (usageArray[i]) {
                        printf("Requests for order %d pages: %d (%f)\n", 
                           i, usageArray[i], usageArray[i]/(float)total);
                    }
                }
            }

        } else if (strcmp("clear_physusage", tok_input[0]) == 0) {
            if (syscall(__NR_clearphysusage)) {
                fprintf(stderr, "\nSyscall %d returned errno: %d\n",__NR_physusage,errno);
            }

        } else { /* the user is running a user program */ 
            procid = fork(); 

            if (procid < 0) { 
                fprintf(stderr, "\nERROR: Could not fork child, Errno: %d\n",errno); 

            } else if (procid == 0) { 
                /* if execvp returns then something went wrong */ 
                errtrap = execvp(tok_input[0], tok_input); 
                if (errtrap) { 
                    /* this is a switch in case I want to add more error handling */ 
                    switch (errno) { 
                        case ENOENT: 
                                  fprintf(stderr, "\n%s: command not found\n", tok_input[0]); 
                                  break; 
                        case ENOMEM: 
                                  fprintf(stderr, "\nInsufficient memory for process\n"); 
                                  break; 
                        default: 
                                  fprintf(stderr, "\nERROR: execvp not successful. Errno: %d\n",errno); 
                    } 
                    exit(errno); 
                } 
            } else { 
                wait(NULL); 
            } 
        } 
        freeinput(tok_input); 
    } 

    printf("Exited loop for some reason\n"); 
    freeinput(tok_input); 
    return 0; 
} 

/* takes in a string of user input and places each word into an array 
 * returns a pointer to the array of string tokens 
 * the array returned will have a NULL pointer in its last index */ 
char **parse_input(char *instr) 
{ 
    char **token_list; 
    char *token = NULL; 
    char *instrCopy; 
    int token_count = 0; 
    int i = 0; 

    /* first we need to know how many tokens 
       exist so we can allocate the correct number of pointers */ 
    instrCopy = strdup(instr); 
    if (instrCopy == NULL) { 
        fprintf(stderr, "Insufficient memory\n"); 
        exit(errno); 
    } 
    token = strtok(instrCopy, DELIMITER); 
    while (token) { 
        token = strtok(NULL, DELIMITER); 
        token_count++; 
    } 
    free(instrCopy); 

    /* Do we have an empty input, then return and do not parse */ 
    if (token_count <= 0) return NULL; 
    token_list = (char **)malloc((token_count+1) * sizeof(char *)); 
    if (token_list == NULL) { 
        fprintf(stderr, "Insufficient memory\n"); 
		exit(errno); 
    } 

    /* tokenize the string and place each token in the array */ 
    token_count=0; 
    token = strtok(instr, DELIMITER); 
    while (token) { 
        token_list[token_count] = strdup(token); 
        if (token_list[token_count] == NULL) { 
            fprintf(stderr, "Insufficient memory\n"); 
            exit(errno); 
        } 
        token = strtok(NULL, DELIMITER); 
		token_count++;
    } 
    token_list[token_count] = NULL; 
    return token_list; 
} 
  

/* free the token buffer */ 
void freeinput(char **input) 
{ 
    int i; 

    for (i = 0; input[i]; i++) {
        free(input[i]);
    }
    free(input); 
} 

 

3. List of all kernel file changes

Three kernel source files were modified. None were added:

 

4. Changes to page_alloc.c

At the top of page_alloc.c:

static int pageUsage[MAX_ORDER+1];
In __alloc_pages():
struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) {
	/* variable declarations */

        if (order >= 0 && order <= MAX_ORDER) {
          pageUsage[order]++;
        }

	/* rest of function */
}
At the end of the file:
asmlinkage int sys_physusage(int *usageArray) {
	if (!access_ok(VERIFY_WRITE, usageArray, sizeof(int) * MAX_ORDER)) {
		return -EFAULT;
	} else if (!copy_to_user(usageArray, pageUsage, sizeof(int) * MAX_ORDER)) {
		return -EFAULT;
	} else {
		return 0;
	}
}

asmlinkage int sys_clearphysusage() {
	int i;

	for (i = 0; i < MAX_ORDER; i++) {
		pageUsage[i] = 0;
	}
}