操作系统实践报告
Job6/sh4
题目要求:
重新编写makefile;
实现连接多条命令;
编写测试用例。
解决思路:
重新编写makefile:
根据教学视频所讲述模板进行编写,得到自动生成依赖关系makefile文件。在 Makefile.dep 中定义依赖关系,然后在 Makefile 文件中引用依赖关系进行编 译等操作。
实现连接多条命令:
创建管道,前一个命令的输出作为下一个命令的输入,管道的创建和关闭依靠函数对命令数组进行遍历,将命令的输入输出连接起来,在子进程中,函数执行当前命令,并将输出写入管道;在父进程中,函数等待子进程执行完毕,并将管道的输入端设置为下一个命令的输入。
编写测试用例:
对几种测试用例进行实现。
关键代码:
重写makefile:
exec_cmd函数实现
运行结果:
test_parse_cmd_1 pass
test_parse_cmd_2 pass
test_parse_pipe_cmd_1 pass
test_parse_pipe_cmd_1 pass
test_exec_cmd pass
test_exec_pipe_cmd pass
All test have passed!
>pwd
current working directory:/root/os/job6/sh4
>ls
Makefile Makefile.dep cmd.c cmd.h cmd.o main.c main.o parse.c
parse.h parse.o readme.md sh4 test test.in test.out
>exit
Job8/pc1.c
题目要求:
系统中有3个线程:生产者、计算者、消费者
系统中有2个容量为4的缓冲区:buffer1、buffer2
生产者
生产'a'、'b'、'c'、‘d'、'e'、'f'、'g'、'h'八个字符
放入到buffer1
打印生产的字符
计算者
从buffer1取出字符
将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印
放入到buffer2
消费者
从buffer2取出字符
打印取出的字符
程序输出结果(实际输出结果是交织的) a b c ... a:A b:B c:C ... A B C ...
解决思路:
定义一个buffer结构体,用来存储buffer1与buffer2状态,定义buffer_is_empty(struct array a)
、buffer_is_full(struct array a)
、get_item(struct array *a)
、put_item(struct array *a, char item)
函数。
总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者。
1.生产者产生字符后,查看buffer1的锁状态,加锁,之后判断buffer1是否为满,满则阻塞等待,反之放入缓冲区buffer1,之后唤醒等待的计算者线程,解开锁。
2.计算者被唤醒后,获取buffer1的锁,加锁,判断buffer1是否为空,如果空则阻塞等待,反之取出一个字符,转化为大写形式。之后再唤醒生产者,释放锁。接下来,它作为消费者的生产者,查看buffer2的锁状态,加锁,之后判断buffer2是否为满,满则阻塞等待,反之将字符放入缓冲区buffer2,之后唤醒等待的消费者线程,解开锁。
3.消费者被唤醒后,获取buffer2的锁,加锁,判断buffer2是否为空,如果空则阻塞等待,反之取出一个字符,输出到屏幕上,之后唤醒消费者,解开锁。
关键代码:
运行结果:
produce item: a
produce item: b
a:A
produce item: c
consume item: A
b:B
produce item: d
consume item: B
c:C
produce item: e
consume item: C
d:D
produce item: f
consume item: D
e:E
produce item: g
consume item: E
f:F
produce item: h
consume item: F
g:G
consume item: G
h:H
consume item: H
Job8/pc2.c
题目要求:
使用信号量解决生产者、计算者、消费者问题 功能与 job8/pc1.c
相同
解决思路:
引入信号量结构体,并且定义多个信号量,通过sema_init()
对信号量进行初始化,sema_wait()
进行等待,sema_signal()
进行释放。
总共有三个角色,他们的关系是:计算者相当于生产者的消费者,“消费者”相当于计算者的消费者,按一定的逻辑执行PV
操作。
生产者首先执行 sema_wait(&empty_buffer1_sema)
函数,如果buffer1
存在空闲位置, empty_buffer1_sema
减1,对buffer1
进行加锁,加入字符后进行解锁。之后执行sema_signal(&full_buffer1_sema);
进行加1操作后,唤起计算者进程。
唤起计算者进程后,首先执行sema_wait(&full_buffer1_sema);
函数,如果buffer1
存在值,则full_buffer1_sema
减1,加buffer1
的锁,取字符转换为大写形式,解开锁,之后执行sema_signal(&empty_buffer1_sema);
此时empty_buffer1_sema
加1,唤醒等待的生产者线程。之后作为生产者,向buffer2
写字符,首先执行sema_wait(&full_buffer2_sema)
函数,判断是否buffer2
有值,如果等到有值的情况,则full_buffer2_sema
减1,加buffer1
的锁,取字符转换为大写形式,解开锁,再执行ema_signal(&empty_buffer2_sema)
,此时empty_buffer2_sema
加1,唤醒等待的消费者线程。
消费者首先执行sema_wait(&full_buffer2_sema);
对buffer2
进行判断,如果有值,则full_buffer2_sema
减1,加buffer2
的锁,取字符打印输出,解开锁,再执行sema_signal(&empty_buffer2_sema)
,此时empty__buffer2_sema
加1,唤醒等待的计算者线程。
关键代码:
运行结果:
produce item: a
produce item: b
produce item: c
compute item before: a
compute item after : A
produce item: d
consume item: A
compute item before: b
compute item after : B
produce item: e
consume item: B
compute item before: c
compute item after : C
produce item: f
consume item: C
compute item before: d
compute item after : D
produce item: g
consume item: D
compute item before: e
compute item after : E
produce item: h
consume item: E
compute item before: f
compute item after : F
compute item before: g
compute item after : G
consume item: F
compute item before: h
compute item after : H
consume item: G
consume item: H
Job9/pfind.c
题目要求:
功能与 sfind 相同
要求使用多线程完成
主线程创建若干个子线程
主线程负责遍历目录中的文件
遍历到目录中的叶子节点时
将叶子节点发送给子线程进行处理
两者之间使用生产者消费者模型通信
主线程生成数据
子线程读取数据
解决思路:
分不同情况,如果路径为文件,则对文件进行查找是否有包含指定的字符串,输出行。如果路径为目录,则遍历目录与其子目录,将得到的文件路径与搜索的字符串添加到任务队列中。
创建WORKER_NUMBER
个子进程,不同子进程从任务队列中获取任务并执行,即对指定文件进行查找。当任务队列为空时,等待新的任务加入。
主线程完成遍历后,根据WORKER_NUMBER
添加标记,表示所有任务完成,子线程得到标记后,自动退出线程。
关键代码:
运行结果:
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# cc pfind.c -o pfind -lpthread
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test main
test/hello/hello.c: int main() {
test/world/world.c: int main()
root@lisongqi-virtual-machine:/home/lisongqi/桌面/operating-system-experiment-ma
ster/job10# ./pfind test int
test/hello/hello.c: int main() {
test/hello/hello.c: printf("hello");
test/world/world.c: int main()
test/world/world.c: printf("world");
Job11/http服务器
题目要求:
完成多进程版本的 httpd
放置在 job10/http/concurrent 目录下
解决思路:
主函数首先解析命令行参数,获取要监听的 IP 地址和端口号。然后使用 tcp_listen 函数创 建一个 TCP 服务器,该服务器会监听指定的 IP 地址和端口号。 在服务器开始循环等待客户端连接时,使用 tcp_accept 函数接受一个新的连接,然后使用 fork 函数创建一个子进程来处理客户端连接。在子进程中,调用 http_handler 函数来处 理客户端的 HTTP 请求,并在处理完毕后关闭连接。
通过 http_parse_req 函数解析客户端发送的请求,获取请求的路径。
判断路径是否以 "/app/" 开头,如果是则认为客户端请求的是一个 CGI 脚本,需要执行该脚本 并将输出发送给客户端。
如果路径不是以 "/app/" 开头,则判断路径对应的是一个文件还是目录,如果是目录则调用 generate_directory_listing 函数生成一个 HTML 目录列表并发送给客户端,如果是文 件则直接将文件内容发送给客户端。
在向客户端发送响应时,使用分块传输编码(Chunked Transfer Encoding)进行传输,这是 一种将消息分块传输的方式,可以在发送大量数据时提高传输效率。
关键代码
http.c
main.c
运行结果:
http://localhost:8080/
Hello World!
/a.html
/b.html
/c.html
/source
/app/now
/app/show_env?name=tom&age=10
/app/list_student
/add_student.html
/remove_student.html