0
点赞
收藏
分享

微信扫一扫

【操作系统】Project 2: UNIX Shell Programming & Linux Kernel Module for Task Information

西红柿上校 2022-04-13 阅读 131
linux

目录

Report

Project 2-1: UNIX Shell

Project 2-2: Linux Kernel Module for Task Information

Code

simple-shell.c

in.txt

out.txt

pid.c

Makefile

README


Report

Project 2-1: UNIX Shell

Ⅰ. Overview

注意:目录下并没有书上所说的文件prog.c,故这里使用cat命令显示目录下的pid.c文件内容。

1、cat pid.c

直接显示等待下一个指令

2、cat pid.c &

不显示等待下一个指令,需要按回车键之后才显示等待下一个指令

Ⅱ. Executing Command in a Child Process

一、实现细节

1、预处理

(1)初始化

(2)获取输入。若输入为“\n”则直接进入下一次循环。

2、输入处理

(3)使用一个for循环处理输入,进行分词。

(4)若最后一个分词为“&”,则更改进程并行条件parent_wait后将最后一个分词置空。

(5)若第一个分词为exit,程序退出。

3、在子进程中执行命令

(6)创建子进程,在子进程中调用execvp()执行命令,父进程根据并行条件执行。

4、释放空间

(7)循环末尾对存储输入的空间进行释放。

二、试运行结果

尝试指令:

ls

ls &

exit

Ⅲ. Creating a History Feature

一、实现细节

1、优先于输入处理,在输入处理之前添加代码,先判断输入是否为!!,否则进入输入处理

2、若输入是!!,判断是否有历史输入history_exist,没有则提示错误信息

3、若有历史输入则将历史指令复制到输入,进入输入处理

4、输入处理后将输入保存到历史history【保证了history里存的绝对是一条指令,而不会是!!】

二、试运行结果

尝试指令:

!!

ls

!!

exit

Ⅳ. Redirecting Input and Output

一、实现细节

1、在子进程中,判断处理后的输入中是否有“<”“>”;因为这种情况下最后一个args肯定是文件名,所以只需要看倒数第二个args是否是“<”“>”。

2、如果有,则用open函数获取文件描述file_input/file_output,使用该文件描述和函数dup2()对输入输出进行重定位,这一步需要在execvp()之前完成。

3、指令执行后,需要用close()函数关闭打开的文件。

4、最后需要将输入输出定位恢复到命令行,使用全局变量shell_input/shell_output中存下的命令行输入输出的定位,用dup()获取。

二、试运行结果

尝试指令:

ls > out.txt

sort < in.txt

Ⅴ. Communication via a Pipe

一、实现细节

1、在输入处理中增加代码,确定是否存在pipe(pipe_exist),并记下pipe符号在args中的位置(pipe_pos)。

2、如果存在pipe,处理完输入之后,需要将|前后的两个指令分开,后面的指令放入pipe_args中。

3、在子进程中创建pipe。

4、在子进程中再创建子进程,在子进程的子进程中运行第一个指令,将输出重定位到pipe的写端,在当前进程中运行第二个指令,将pipe的读端读到的数据作为输入。

二、试运行结果

尝试指令:

ls -l | less

Project 2-2: Linux Kernel Module for Task Information

Ⅰ. Writing to the /proc File System

实现细节:

1、在proc_ops中添加.proc_write = proc_write

2、需修改proc_write()函数。因为无法保证从usr_buf复制(copy_from_user())的字符串str(用户通过echo输入的pid)以null结尾,所以需要用sscanf()将str再复制到buffer,未被覆盖的部分已被初始化为null。这样才能适用于kstrtol()的第一个参数。

3、kstrtol()的第二个参数为进制,最后一个参数为第一个字符串参数转化为的整数,即需要查询其信息的进程的pid。

4、实例代码中用kmalloc()为字符串声明的空间,最后用kfree()释放。

Ⅱ. Reading form the /proc File System

实现细节:

1、查看<linux/sched.h>文档中关于结构体task_struct的声明,可知需要的信息对应的变量名:command为comm,字符串;state为state,长整型。

2、修改proc_write()。如果pid_task()返回空,说明没有传给它合法的pid,用sprintf()函数将错误信息写到buffer,并返回0。如果返回正常,则用sprintf()将要查询的信息写到buffer,buffer会被复制(copy_to_user())到usr_buffer,用户能在shell看到查询的信息。

运行结果:

1、装载内核模块,用ps指令查看当前有哪些进程,选择一个pid准备写入

2、写入pid并读取查询结果

3、卸载内核模块

Code

simple-shell.c

/**
 * Simple shell interface program.
 *
 * Operating System Concepts - Tenth Edition
 * Copyright John Wiley & Sons - 2018
 */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>


#define MAX_LINE 80 /* 80 chars per line, per command */
#define EXIT "exit"
#define HISTORY "!!\n"
#define READ_END 0
#define WRITE_END 1


int main(void)
{
	char *args[MAX_LINE/2 + 1];	/* command line (of 80) has max of 40 arguments */
	for(int k = 0; k < MAX_LINE/2 + 1; k++) args[k] = NULL; //all init to NULL
    	
    	char input[MAX_LINE + 1]; //input[81]
    	
    	char history[MAX_LINE + 1]; //history[81]
    	int history_exist = 0; //for history
    	
    	int shell_input = dup(STDIN_FILENO); //for file input
    	int shell_output = dup(STDOUT_FILENO); //for file output
    	
    	int pipe_exist = 0; //for pipe
    	int pipe_pos;
    	int fd[2];
    	
    	int should_run = 1;
    	
    	int parent_wait; //for &
		
    	while (should_run)
    	{   
        	printf("osh>");
        	fflush(stdout);
        	parent_wait = 1;
        
        	//get input
        	fgets(input, MAX_LINE + 1, stdin);
        	
        	if(input[0] == '\n')
        	{
        		continue;
        	}
        	
        	//history command
        	if(strcmp(input,HISTORY) == 0)
        	{
        		if(history_exist == 0)
        		{
        			printf("No commands in history.\n");
        			continue;
        		}
        		else
        		{
        			if(strcmp(history,HISTORY) == 0)
        			{
        				printf("No commands in history.\n");
        				continue;
        			}
        			printf("%s\n", history);
        			strcpy(input, history);
        		}
        	}
        	
        	
        	//input is not !!
        	//process input
        	int args_ = 0;
        	args[args_] = (char*)malloc(sizeof(char)*20); //allocate space for each arg
        	int j = 0;
        	for(int i = 0; input[i] != '\n'; i++)
        	{
        		
        		if(input[i] != ' ' && input[i] != '\t' && input[i] != '\n')
        		{
        			args[args_][j] = input[i];
        			j++;
        			if(input[i] == '|') //there is a pipe
        			{
        				pipe_exist = 1;
        				pipe_pos = args_;
        			}
        		}
        		if(input[i] == ' ' || input[i] == '\t') 
        		{
        			if(input[i+1] != ' ' && input[i+1] != '\t' && input[i+1] != '\n')
        			{
					args[args_][j] = '\0';
					args_++;
					args[args_] = (char*)malloc(sizeof(char)*20); //allocate space for each arg
					j = 0;
        			}
        		}
        	}
        	args[args_][j] = '\0';
        	
        	//store history
        	strcpy(history, input);
        	history_exist = 1;
        	
        	//exit command
        	if(strcmp(args[0], EXIT) == 0) break;
        	
        	//if parent need to wait      	
        	if(strcmp(args[args_],"&") == 0)
 	 	{
 	 		args[args_] = NULL;
 	 		parent_wait = 0;
 	 	}
 	 	
 	 	//if there is a pipe
 	 	char *pipe_args[MAX_LINE/2+1];
 	 	for(int k = 0; k < MAX_LINE/2 + 1; k++) pipe_args[k] = NULL;
 	 	if(pipe_exist)
 	 	{	
 	 		for(int i = pipe_pos + 1, j = 0; i <=args_; i++, j++)
 	 		{
 	 			pipe_args[j] = (char*)malloc(sizeof(char)*20);
 	 			strcpy(pipe_args[j], args[i]); //put the command behind "|" into pipe_args
 	 			args[i] = NULL;
 	 			args_--;
 	 		}
 	 		args[pipe_pos] = NULL;
 	 	}
 	 	
 	 	
        	 
        	/**
         	 * After reading user input, the steps are:
         	 * (1) fork a child process
         	 * (2) the child process will invoke execvp()
         	 * (3) if command includes &, parent and child will run concurrently
         	 */
         	 pid_t pid;
         	 pid = fork();
         	 
         	 if(pid < 0) //error occurred
         	 {
         	 	fprintf(stderr, "Fork Failed");
         	 	return 1;
         	 }
         	 else if(pid == 0) //child process
         	 {
         	 	//redirect input and output
         	 	if(args_ >=1)
         	 	{
         	 		if(strcmp(args[args_-1], ">") == 0) //file output
         	 		{
         	 			int file_output = open(args[args_], O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
         	 			if(file_output == -1)
         	 			{
         	 				printf("Can't open the output file!");
         	 				continue;
         	 			}
         	 			args[args_-1] = NULL;
         	 			args[args_] = NULL;
         	 			dup2(file_output, STDOUT_FILENO); //redirect
         	 			execvp(args[0], args);
         	 			close(file_output);
         	 			dup2(shell_output, STDOUT_FILENO); //recover direct
         	 			exit(0);
         	 		}
         	 		else if(strcmp(args[args_-1], "<") == 0) //file input
         	 		{
         	 			int file_input = open(args[args_], O_RDONLY);
         	 			if(file_input == -1)
         	 			{
         	 				printf("Can't open the input file!");
         	 				continue;
         	 			}
         	 			args[args_-1] = NULL;
         	 			args[args_] = NULL;
         	 			dup2(file_input, STDIN_FILENO); //redirect
         	 			execvp(args[0], args);
         	 			close(file_input);
         	 			dup2(shell_input, STDIN_FILENO); //recover direct
         	 			exit(0);
         	 		}
         	 		
         	 		if(pipe_exist) //pipe communication
         	 		{
         	 			pid_t pid2;
         	 			if(pipe(fd) == -1) //create the pipe
         	 			{
         	 				fprintf(stderr, "Pipe failed");
         	 				return 1;
         	 			}
         	 			
         	 			pid2 = fork();
         	 			if(pid2 < 0) //error occurred
         	 			{
         	 				fprintf(stderr, "Fork Failed");
         	 				return 1;
         	 			}
         	 			else if(pid2 == 0) //child process
         	 			{
         	 				close(fd[READ_END]); //close the unused end of the pipe
         	 				dup2(fd[WRITE_END], STDOUT_FILENO); //output redirect to pipe's WRIRE_END
         	 				execvp(args[0], args); //first command output write into the pipe
         	 				close(fd[WRITE_END]);
         	 				dup2(shell_output, STDOUT_FILENO);
         	 				exit(0);
         	 			}
         	 			else // parent process
         	 			{
         	 				wait(NULL);
         	 				close(fd[WRITE_END]);
         	 				dup2(fd[READ_END], STDIN_FILENO); //input redirect to pipe's READ_END
         	 				execvp(pipe_args[0],pipe_args); //second command input read from pipe
         	 				close(fd[READ_END]);
         	 				dup2(shell_input, STDIN_FILENO);
         	 			}
         	 		}
         	 		else
         	 		{
         	 			execvp(args[0], args);
         	 		}
         	 	}
         	 	else
         	 	{
         	 		execvp(args[0], args);
         	 	}
         	 }
         	 else //parent process
         	 {
         	 	if(parent_wait)
         	 	{
         	 		wait(NULL);
         	 		printf("Child Complete\n");
         	 	}	
         	 }
         	 
         	 
         	 //clear
         	 for(int i = 0; i < MAX_LINE/2 + 1; i++) free(args[i]);
         	 
         	 
    	}
    
	return 0;
}

in.txt

opq
acb
abc
abcc

out.txt

DateClient.java
DateServer.java
fig3-30.c
fig3-31.c
fig3-32.c
fig3-33.c
fig3-34.c
fig3-35.c
Makefile
multi-fork
multi-fork.c
newproc-posix.c
newproc-win32.c
out.txt
pid.c
shell
shm-posix-consumer.c
shm-posix-producer.c
simple-shell.c
unix_pipe.c
win32-pipe-child.c
win32-pipe-parent.c

pid.c

/**
 * Kernel module that communicates with /proc file system.
 *
 * This provides the base logic for Project 2 - displaying task information
 */

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

#define BUFFER_SIZE 128
#define PROC_NAME "pid"

/* the current pid */
static long l_pid;

/**
 * Function prototypes
 */
static ssize_t proc_read(struct file *file, char *buf, size_t count, loff_t *pos);
static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos);

//static struct file_operations proc_ops = {
//        .owner = THIS_MODULE,
//        .read = proc_read,
//};

static struct proc_ops proc_ops = {
        //.owner = THIS_MODULE,
        .proc_read = proc_read,
        .proc_write = proc_write
};

/* This function is called when the module is loaded. */
static int proc_init(void)
{
        // creates the /proc/procfs entry
        proc_create(PROC_NAME, 0666, NULL, &proc_ops);

        printk(KERN_INFO "/proc/%s created\n", PROC_NAME);

	return 0;
}

/* This function is called when the module is removed. */
static void proc_exit(void) 
{
        // removes the /proc/procfs entry
        remove_proc_entry(PROC_NAME, NULL);

        printk( KERN_INFO "/proc/%s removed\n", PROC_NAME);
}

/**
 * This function is called each time the /proc/pid is read.
 * 
 * This function is called repeatedly until it returns 0, so
 * there must be logic that ensures it ultimately returns 0
 * once it has collected the data that is to go into the 
 * corresponding /proc file.
 */
static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos)
{
        int rv = 0;
        char buffer[BUFFER_SIZE];
        static int completed = 0;
        struct task_struct *tsk = NULL;

        if (completed) {
                completed = 0;
                return 0;
        }

        tsk = pid_task(find_vpid(l_pid), PIDTYPE_PID);
        
        if(tsk == NULL)
        {
        	rv = sprintf(buffer, "Not pass a valid pid!\n");
        	return 0;
        }
        rv = sprintf(buffer, "command = [%s] pid = [%ld] state = [%ld]\n", tsk->comm, l_pid, tsk->state);

        completed = 1;

        // copies the contents of kernel buffer to userspace usr_buf 
        if (copy_to_user(usr_buf, buffer, rv)) {
                rv = -1;
        }

        return rv;
}

/**
 * This function is called each time we write to the /proc file system.
 */
static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos)
{
        char *k_mem;
        char buffer[BUFFER_SIZE];

        // allocate kernel memory
        k_mem = kmalloc(count, GFP_KERNEL);

        /* copies user space usr_buf to kernel buffer */
        if (copy_from_user(k_mem, usr_buf, count)) {
		printk( KERN_INFO "Error copying from user\n");
                return -1;
        }

	/**
 	 * kstrtol() will not work because the strings are not guaranteed
	 * to be null-terminated.
	 * 
	 * sscanf() must be used instead.
	 */
	
	sscanf(k_mem,"%s",buffer); //sscanf(): null-terminated
	kstrtol(buffer, 10, &l_pid); //base = 10, decimal
        kfree(k_mem);

        return count;
}

/* Macros for registering module entry and exit points. */
module_init( proc_init );
module_exit( proc_exit );

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Module");
MODULE_AUTHOR("SGG");

Makefile

obj-m += pid.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

README

TEST simple-shell.c: 
gcc -o shell simple-shell.c
./shell
ls
ls &
[enter]
exit
./shell
!!
ls
!!
exit
./shell
ls > out.txt
exit
./shell
sort < in.txt
exit
./shell
ls -l | less
[q]
exit

TEST pid.c: 
make
sudo dmesg -c
sudo insmod pid.ko
dmesg
ps 【记下bash的pid】
echo "_ _ _ _" > /proc/pid 【填入bash的pid】
cat /proc/pid
sudo rmmod pid
dmesg
举报

相关推荐

0 条评论