目录
Project 2-2: Linux Kernel Module for Task Information
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