Compare commits
10 Commits
f012b549d2
...
de6e8570fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
de6e8570fe
|
|||
|
ffa95bc8fc
|
|||
|
201fc5cc93
|
|||
|
c4e4d69b8d
|
|||
|
35e60a8b6d
|
|||
|
2722ab3feb
|
|||
|
7da2a7bd58
|
|||
|
2f794d04b7
|
|||
|
86c6bb3f55
|
|||
|
db88c4381a
|
@@ -36,7 +36,6 @@ std::deque<Customer> customer_q;
|
||||
std::counting_semaphore<100> customer_num_sem{0};
|
||||
// END_LST_COUNT_SEM
|
||||
|
||||
|
||||
void signal_handler(int signum) {
|
||||
// std::cout << "\nReceived signal " << signum << ", stopping...\n";
|
||||
write(STDOUT_FILENO, "\nCaught SIGINT, shutting down...\n", 33);
|
||||
@@ -59,11 +58,17 @@ void teller_thread(
|
||||
(curr_tick - start_serving_time) / TIME_GRAN_MSEC))
|
||||
.count() <= curr_customer.service_time) {
|
||||
// We are still serving, do nothing
|
||||
std::osyncstream(std::cout) << "Teller id " << id << " is serving customer " << curr_customer.id << " at tick " << tick_count << std::endl;
|
||||
std::osyncstream(std::cout)
|
||||
<< "Teller id " << id << " is serving customer "
|
||||
<< curr_customer.id << " at tick " << tick_count
|
||||
<< std::endl;
|
||||
goto next_tick;
|
||||
} else {
|
||||
// We have just finished serving.
|
||||
std::osyncstream(std::cout) << "Teller id " << id << " finishied serving customer " << curr_customer.id << " at tick " << tick_count << std::endl;
|
||||
std::osyncstream(std::cout)
|
||||
<< "Teller id " << id << " finishied serving customer "
|
||||
<< curr_customer.id << " at tick " << tick_count
|
||||
<< std::endl;
|
||||
is_serving = false;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +91,8 @@ void teller_thread(
|
||||
}
|
||||
} else {
|
||||
std::osyncstream(std::cout)
|
||||
<< "Teller id " << id << " is idle at tick " << tick_count << std::endl;
|
||||
<< "Teller id " << id << " is idle at tick " << tick_count
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
next_tick:
|
||||
@@ -108,7 +114,9 @@ void customer_thread(
|
||||
|
||||
std::chrono::time_point curr_tick = start_tick;
|
||||
unsigned int tick_count = 0;
|
||||
std::chrono::time_point target_tick = start_tick + std::chrono::milliseconds(TIME_GRAN_MSEC) * info.arrival_time;
|
||||
std::chrono::time_point target_tick =
|
||||
start_tick +
|
||||
std::chrono::milliseconds(TIME_GRAN_MSEC) * info.arrival_time;
|
||||
while (curr_tick < target_tick) {
|
||||
if (!bank_open) {
|
||||
return;
|
||||
@@ -119,7 +127,9 @@ void customer_thread(
|
||||
}
|
||||
|
||||
// Wake up and push self into queue, then V(customer_num_sem)
|
||||
std::osyncstream(std::cout) << "Customer id " << info.id << " arrived at tick " << tick_count << std::endl;
|
||||
std::osyncstream(std::cout)
|
||||
<< "Customer id " << info.id << " arrived at tick " << tick_count
|
||||
<< std::endl;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(customer_q_mtx);
|
||||
customer_q.push_back(cus);
|
||||
@@ -180,14 +190,15 @@ int main(int argc, char *argv[]) {
|
||||
for (auto cu : customer_infos) {
|
||||
try {
|
||||
customers.emplace_back(customer_thread, cu, start_tick);
|
||||
} catch (const std::system_error &e) {
|
||||
std::cerr << "Failed to create customer thread, id = " << cu.id << ": "
|
||||
<< e.what() << std::endl;
|
||||
}
|
||||
catch (const std::system_error &e) {
|
||||
std::cerr << "Failed to create customer thread, id = " << cu.id
|
||||
<< ": " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all children to shutdown (by ctrl-c handler)
|
||||
for (auto &t: customers) {
|
||||
for (auto &t : customers) {
|
||||
if (t.joinable()) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
BIN
lab1/report.pdf
BIN
lab1/report.pdf
Binary file not shown.
23
lab2/qsort.c
23
lab2/qsort.c
@@ -17,7 +17,7 @@ struct ThreadArgs {
|
||||
int high;
|
||||
};
|
||||
|
||||
void swap(int *a, int* b) {
|
||||
void swap(int *a, int *b) {
|
||||
int temp = *b;
|
||||
*b = *a;
|
||||
*a = temp;
|
||||
@@ -25,7 +25,7 @@ void swap(int *a, int* b) {
|
||||
|
||||
// Returns the index of pivot in arr[].
|
||||
int partition(int low, int high) {
|
||||
int pivot = numbers[high]; // Pivot element
|
||||
int pivot = numbers[high]; // Pivot element
|
||||
int i = low - 1;
|
||||
|
||||
for (int j = low; j < high; j++) {
|
||||
@@ -51,7 +51,7 @@ void qsort_nothread(int low, int high) {
|
||||
}
|
||||
|
||||
void *qsort_thread(void *arg) {
|
||||
struct ThreadArgs* args = (struct ThreadArgs*) arg;
|
||||
struct ThreadArgs *args = (struct ThreadArgs *)arg;
|
||||
// printf("Low: %d, high: %d\n", args->low, args->high);
|
||||
if (args->high - args->low <= SINGLE_THREAD_SRTH) {
|
||||
qsort_nothread(args->low, args->high);
|
||||
@@ -59,14 +59,9 @@ void *qsort_thread(void *arg) {
|
||||
}
|
||||
|
||||
int pivot_idx = partition(args->low, args->high);
|
||||
struct ThreadArgs left_thd_arg = {
|
||||
.low = args->low,
|
||||
.high = pivot_idx - 1
|
||||
};
|
||||
struct ThreadArgs right_thd_arg = {
|
||||
.low = pivot_idx + 1,
|
||||
.high = args->high
|
||||
};
|
||||
struct ThreadArgs left_thd_arg = {.low = args->low, .high = pivot_idx - 1};
|
||||
struct ThreadArgs right_thd_arg = {.low = pivot_idx + 1,
|
||||
.high = args->high};
|
||||
pthread_t left_handle, right_handle;
|
||||
pthread_create(&left_handle, NULL, qsort_thread, (void *)&left_thd_arg);
|
||||
pthread_create(&right_handle, NULL, qsort_thread, (void *)&right_thd_arg);
|
||||
@@ -77,14 +72,10 @@ void *qsort_thread(void *arg) {
|
||||
|
||||
// LST_MAIN_FUNC
|
||||
void do_qsort() {
|
||||
struct ThreadArgs arg = {
|
||||
.low = 0,
|
||||
.high = NUM_LENGTH - 1
|
||||
};
|
||||
struct ThreadArgs arg = {.low = 0, .high = NUM_LENGTH - 1};
|
||||
qsort_thread(&arg);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
printf("Start reading file...\n");
|
||||
numbers = malloc(sizeof(int) * NUM_LENGTH);
|
||||
|
||||
81
lab6/.vscode/settings.json
vendored
81
lab6/.vscode/settings.json
vendored
@@ -1,9 +1,78 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"cdev.h": "c",
|
||||
"module.h": "c",
|
||||
"types.h": "c",
|
||||
"init.h": "c"
|
||||
},
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/clang++"
|
||||
"any": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"ratio": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"format": "cpp",
|
||||
"fstream": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ranges": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"span": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stdfloat": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"syncstream": "cpp",
|
||||
"text_encoding": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"variant": "cpp"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,32 @@
|
||||
obj-m += wendy.o
|
||||
OUT_DIR := build
|
||||
|
||||
.PHONY: all kern_mod install uninstall clean reader writer
|
||||
.PHONY: all kern_mod install rm clean reader writer tester dirprepare
|
||||
|
||||
all: kern_mod
|
||||
@true
|
||||
all: kern_mod reader writer tester
|
||||
|
||||
kern_mod: wendy.c
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) MO=$(PWD)/build/kern
|
||||
kern_mod:
|
||||
make -C wendy_mod
|
||||
|
||||
install:
|
||||
sudo insmod ./build/kern/wendy.ko
|
||||
install: kern_mod
|
||||
make -C wendy_mod install
|
||||
|
||||
uninstall:
|
||||
sudo rmmod wendy
|
||||
rm:
|
||||
make -C wendy_mod uninstall
|
||||
|
||||
reader:
|
||||
mkdir -p $(OUT_DIR)
|
||||
reader: dirprepare
|
||||
gcc pipe_read.c -o $(OUT_DIR)/pread
|
||||
|
||||
writer:
|
||||
mkdir -p $(OUT_DIR)
|
||||
writer: dirprepare
|
||||
gcc pipe_write.c -o $(OUT_DIR)/pwrite
|
||||
|
||||
tester: dirprepare
|
||||
g++ --std=c++20 pipe_tester.cpp -o $(OUT_DIR)/tester
|
||||
|
||||
dirprepare:
|
||||
mkdir -p $(OUT_DIR)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
make -C wendy_mod clean
|
||||
rm -rf $(OUT_DIR)
|
||||
|
||||
|
||||
BIN
lab6/exps/longread_end.png
Normal file
BIN
lab6/exps/longread_end.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
lab6/exps/longread_start.png
Normal file
BIN
lab6/exps/longread_start.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
13713
lab6/exps/mirror_file.txt
Normal file
13713
lab6/exps/mirror_file.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
lab6/exps/read_write.png
Normal file
BIN
lab6/exps/read_write.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
13709
lab6/make.txt
Normal file
13709
lab6/make.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,42 @@
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <syncstream>
|
||||
#include <thread>
|
||||
|
||||
void writer() {
|
||||
std::osyncstream(std::cout) << "Writer started" << std::endl;
|
||||
|
||||
std::ofstream pipe_write("/dev/wendy_in");
|
||||
if (!pipe_write.is_open()) {
|
||||
std::cerr << "Cannot open pipe in" << std::endl;
|
||||
}
|
||||
|
||||
std::ifstream txt("./make.txt");
|
||||
if (!txt.is_open()) {
|
||||
std::cerr << "Canno open make.txt" << std::endl;
|
||||
}
|
||||
// pipe_write << "Hello from the other side\n";
|
||||
pipe_write << txt.rdbuf();
|
||||
std::osyncstream(std::cout) << "Writer ended" << std::endl;
|
||||
}
|
||||
|
||||
void reader() {
|
||||
std::osyncstream(std::cout) << "Reader started" << std::endl;
|
||||
std::ifstream pipe_read("/dev/wendy_out");
|
||||
std::osyncstream(std::cout) << "Reader read content: " << pipe_read.rdbuf();
|
||||
std::osyncstream(std::cout) << "Reader ended" << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
std::thread writer_thread(writer);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::thread reader_thread(reader);
|
||||
if (reader_thread.joinable()) {
|
||||
reader_thread.join();
|
||||
}
|
||||
if (writer_thread.joinable()) {
|
||||
writer_thread.join();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
4
lab6/report/.gitignore
vendored
Normal file
4
lab6/report/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.aux
|
||||
*.log
|
||||
*.synctex.gz
|
||||
*.synctex(busy)
|
||||
BIN
lab6/report/report.pdf
Normal file
BIN
lab6/report/report.pdf
Normal file
Binary file not shown.
128
lab6/report/report.tex
Normal file
128
lab6/report/report.tex
Normal file
@@ -0,0 +1,128 @@
|
||||
\documentclass[12pt, a4paper]{article}
|
||||
\usepackage[UTF8]{ctex}
|
||||
\usepackage{geometry}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{float}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{extarrows}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{diagbox}
|
||||
\usepackage{siunitx}
|
||||
\usepackage{subcaption}
|
||||
\usepackage{fontspec}
|
||||
\usepackage{listings}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{siunitx}
|
||||
\usepackage[ruled,vlined]{algorithm2e}
|
||||
|
||||
\sisetup{
|
||||
group-separator={,},
|
||||
}
|
||||
|
||||
\definecolor{vscodeBlue}{RGB}{0,0,255} % 关键字/语句
|
||||
\definecolor{vscodeRed}{RGB}{163,21,21} % 字符串
|
||||
\definecolor{vscodeGreen}{RGB}{0,128,0} % 注释
|
||||
|
||||
\newfontfamily\codefont{Fira Code}
|
||||
\lstset{
|
||||
basicstyle = \footnotesize\codefont,
|
||||
% ---
|
||||
tabsize = 4,
|
||||
showstringspaces = false,
|
||||
numbers = left,
|
||||
numberstyle = \codefont,
|
||||
% ---
|
||||
breaklines = true,
|
||||
captionpos = t,
|
||||
% ---
|
||||
frame = l,
|
||||
flexiblecolumns,
|
||||
commentstyle=\color{vscodeGreen}, % 注释样式
|
||||
keywordstyle=\color{vscodeBlue}, % 关键字样式
|
||||
stringstyle=\color{vscodeRed}, % 字符串样式
|
||||
rangebeginprefix = //\ BEGIN_LST_,
|
||||
rangeendprefix = //\ END_LST_
|
||||
}
|
||||
|
||||
\renewcommand{\qedsymbol}{}
|
||||
|
||||
\lstMakeShortInline|
|
||||
|
||||
\geometry{a4paper,scale=0.8}
|
||||
|
||||
\allowdisplaybreaks[4] %允许公式跨页
|
||||
|
||||
\setenumerate[1]{label=\arabic{*}. }
|
||||
\setenumerate[2]{label=(\arabic{*})}
|
||||
|
||||
\title{\Huge{{\bf 操作系统大作业实验6报告}}}
|
||||
\author{高艺轩 2022010639}
|
||||
\date{}
|
||||
|
||||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
\section{实验题目}
|
||||
选择的题目为“管道驱动程序开发”。
|
||||
|
||||
\section{问题描述}
|
||||
管道是现代操作系统中重要的进程间通信(IPC)机制之一,Linux和Windows操作系统都支持管道。
|
||||
|
||||
管道在本质上就是在进程之间以字节流方式传送信息的通信通道,每个管道具有两个端,一端用于输入,一端用于输出,如下图所示。在相互通信的两个进程中,一个进程将信息送入管道的输入端,另一个进程就可以从管道的输出端读取该信息。显然,管道独立于用户进程,所以只能在内核态下实现。
|
||||
|
||||
在本实验中,请通过编写设备驱动程序mypipe实现自己的管道,并通过该管道实现进程间通信。
|
||||
|
||||
你需要编写一个设备驱动程序mypipe实现管道,该驱动程序创建两个设备实例,一个针对管道的输入端,另一个针对管道的输出端。另外,你还需要编写两个测试程序,一个程序向管道的输入端写入数据,另一个程序从管道的输出端读出数据,从而实现两个进程间通过你自己实现的管道进行数据通信。
|
||||
|
||||
\section{思路与程序结构}
|
||||
实现Linux管道驱动的核心是编写一个character device驱动,我们将它通过模块的形式挂载到内核中。实验内核版本为|6.14.3-arch1-1|。作为一个管道程序,我们需要向系统注册两个设备,一个作为输出,一个作为输入;同时,需要开辟一片缓冲区,用于暂时接收从管道入口发来的信息,在这里我选择使用了环形缓存(ring buffer)。
|
||||
|
||||
首先,定义一些变量用于存储状态:
|
||||
\lstinputlisting[language=c, linerange=DEF]{../wendy_mod/wendy.c}
|
||||
|
||||
作为一个设备驱动模块,我们要做的最主要的事情就是提供|module_init|和|module_exit|两个回调函数。我们的驱动程序将在这两个|module_init|中初始化自身,向操作系统注册我们创建的设备以及注册另一个重要的回调函数结构体|mypipe_fops|,同时在|module_exit|中做相反的操作。模块初始化与销毁的代码如下:
|
||||
\lstinputlisting[language=c, linerange=INIT]{../wendy_mod/wendy.c}
|
||||
|
||||
在|module_init|中我们注册了回调函数结构体|cdev_init(&pdata.cdev, &mypipe_fops);|这其中的|mypipe_fops|定义为:
|
||||
\lstinputlisting[language=c, linerange=FOP]{../wendy_mod/wendy.c}
|
||||
|open|,|release|,|read|,|write|可以粗略地看作对应了用户态下的|fopen|,|fclose|,|fread|,|fwrite|四个操作。我们需要实现这四个回调函数才能正确地完成目标。对于|open|与|release|,由于我们实现的是一个虚拟设备,无需与物理硬件交互,所以不需要进行特别的操作;为了保证只有一个程序可以向管道写入,同时保证在写入程序关闭管道时向读取端提示已经写入端已经关闭,需要处理|pdata.is_active|量来标识当前是否有程序正在占用管道的写入端。
|
||||
\lstinputlisting[language=c, linerange=OPEN_RELEASE]{../wendy_mod/wendy.c}
|
||||
对于|read|与|write|函数,首先需要判断|minor|号是否匹配:我们不允许在入口调用|read|,也不允许在出口调用|write|。之后的操作是类似的,以|read|为例。首先尝试获取缓冲区的锁,如果当前的数据长度为0,那么要么是对面端已经关闭了管道而且我们已经读取了所有的数据,要么是管道对面还有等着写入的数据暂时没有写入。对于前者,我们可以直接返回|0|代表已经读取完毕,对于后者,我们可以将自己放入等待区,等待|pdata.data_len > 0|的事件发生时重启此系统调用。如果当前的数据长度不为0,那么我们就可以从环形缓存中依次读取数据放入用户提供的缓冲区中,并且提醒可能的正在等待的写入端我们已经腾出了空间。|write|的操作是完全类似的。
|
||||
\lstinputlisting[language=c, linerange=READ_WRITE]{../wendy_mod/wendy.c}
|
||||
|
||||
\section{程序运行情况}
|
||||
首先,编写简单的写入(Listings \ref{code:pwrite})与读出程序(Listings \ref{code:pread})进行测试:
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{../exps/read_write.png}
|
||||
\caption{简单读入读出测试}
|
||||
\end{figure}
|
||||
|
||||
之后,尝试利用管道传输超出缓冲区容量的数据(Listings \ref{code:tester}),读取大约\SI{620}{KB}:
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{../exps/longread_start.png}
|
||||
\caption{程序输出的开头}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{../exps/longread_end.png}
|
||||
\caption{程序输出的结尾}
|
||||
\end{figure}
|
||||
|
||||
\section{实验总结}
|
||||
在本次实验中,我第一次实现了一个简单的Linux驱动。在阅读了一些教程后,对代码结构有了基本的认识,实际写起来也并不觉得过于困难,这次时间很大地提升了我的编程水平。
|
||||
|
||||
|
||||
\appendix
|
||||
\section{完整源代码}
|
||||
\lstinputlisting[language=c, title=管道驱动程序代码, caption={内核驱动模块代码}]{../wendy_mod/wendy.c}
|
||||
\lstinputlisting[language=c, title=pipe\_write.c, caption={简单写入程序代码}, label={code:pwrite}]{../pipe_write.c}
|
||||
\lstinputlisting[language=c, title=pipe\_read.c, caption={简单读出程序代码}, label={code:pread}]{../pipe_read.c}
|
||||
\lstinputlisting[language=c++, title=pipe\_tester.cpp, caption={测试程序代码}, label={code:tester}]{../pipe_tester.cpp}
|
||||
|
||||
\end{document}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux Kernel Module",
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/include",
|
||||
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include",
|
||||
8
lab6/wendy_mod/.vscode/settings.json
vendored
Normal file
8
lab6/wendy_mod/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"cdev.h": "c",
|
||||
"module.h": "c",
|
||||
"types.h": "c",
|
||||
"init.h": "c",
|
||||
}
|
||||
}
|
||||
20
lab6/wendy_mod/Makefile
Normal file
20
lab6/wendy_mod/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
obj-m += wendy.o
|
||||
OUT_DIR := build
|
||||
CUR_DIR := $(shell pwd)
|
||||
|
||||
.PHONY: kern_mod install uninstall dirprepare clean
|
||||
|
||||
kern_mod: wendy.c dirprepare
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(CUR_DIR) MO=$(CUR_DIR)/$(OUT_DIR)
|
||||
|
||||
install:
|
||||
sudo insmod build/wendy.ko
|
||||
|
||||
uninstall:
|
||||
sudo rmmod wendy
|
||||
|
||||
dirprepare:
|
||||
mkdir -p $(CUR_DIR)/$(OUT_DIR)
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(CUR_DIR) clean
|
||||
@@ -7,6 +7,7 @@
|
||||
// https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
|
||||
// https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/
|
||||
|
||||
// BEGIN_LST_DEF
|
||||
#define MYPIPE_BUFFER_SIZE 4096
|
||||
#define MYPIPE_DEVICE_NAME "wendy"
|
||||
|
||||
@@ -20,6 +21,8 @@ enum MyPipeMinors {
|
||||
|
||||
struct mypipe_data {
|
||||
struct cdev cdev;
|
||||
// Whether opened by some writer
|
||||
bool is_active;
|
||||
u8 buffer[MYPIPE_BUFFER_SIZE];
|
||||
size_t write_pos;
|
||||
size_t read_pos;
|
||||
@@ -33,21 +36,50 @@ static struct class *mypipedev_class = NULL;
|
||||
|
||||
// Protects the buffer
|
||||
static DEFINE_MUTEX(mypipe_lock);
|
||||
// Protects the is_active value
|
||||
static DEFINE_MUTEX(isactive_lock);
|
||||
// These are two wait_queue_head_t for storing tasks that are waiting for
|
||||
// writing/ reading
|
||||
static DECLARE_WAIT_QUEUE_HEAD(read_queue);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(write_queue);
|
||||
// END_LST_DEF
|
||||
|
||||
// BEGIN_LST_OPEN_RELASE
|
||||
static int mypipe_open(struct inode *inode, struct file *file) {
|
||||
printk(KERN_INFO "Wendy opened.");
|
||||
int minor = iminor(inode);
|
||||
printk(KERN_INFO "%s opened, minor = %d", MYPIPE_DEVICE_NAME, minor);
|
||||
|
||||
// If its opening the writing end, we limit it to only one writer
|
||||
if (minor == MYPIPE_MINOR_IN) {
|
||||
if (mutex_lock_interruptible(&isactive_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
if (pdata.is_active) {
|
||||
return -EBUSY;
|
||||
}
|
||||
pdata.is_active = true;
|
||||
mutex_unlock(&isactive_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mypipe_release(struct inode *inode, struct file *file) {
|
||||
printk(KERN_INFO "Wendy released.");
|
||||
int minor = iminor(inode);
|
||||
printk(KERN_INFO "%s released, minor = %d", MYPIPE_DEVICE_NAME, minor);
|
||||
|
||||
if (minor == MYPIPE_MINOR_IN) {
|
||||
mutex_lock(&isactive_lock);
|
||||
pdata.is_active = false;
|
||||
mutex_unlock(&isactive_lock);
|
||||
wake_up_interruptible(&read_queue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// END_LST_OPEN_RELEASE
|
||||
|
||||
// BEGIN_LST_READ_WRITE
|
||||
static ssize_t mypipe_read(struct file *filep, char __user *buf, size_t size,
|
||||
loff_t *offset) {
|
||||
ssize_t ret = 0;
|
||||
@@ -65,7 +97,21 @@ static ssize_t mypipe_read(struct file *filep, char __user *buf, size_t size,
|
||||
// We currently have no data, but we can wait for possible new to write in
|
||||
while (pdata.data_len == 0) {
|
||||
mutex_unlock(&mypipe_lock);
|
||||
|
||||
// Check if the writer is still active
|
||||
if (mutex_lock_interruptible(&isactive_lock)) {
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
bool writer_is_active = pdata.is_active;
|
||||
mutex_unlock(&isactive_lock);
|
||||
|
||||
if (!writer_is_active) {
|
||||
return 0; // No data left and the writer is gone -> EOF for reader
|
||||
}
|
||||
|
||||
// There is still writer, wait for data
|
||||
if (filep->f_flags & O_NONBLOCK) {
|
||||
// The caller says no waiting, so we don't wait
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (wait_event_interruptible(read_queue, pdata.data_len > 0)) {
|
||||
@@ -158,11 +204,15 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
// END_LST_READ_WRITE
|
||||
|
||||
// BEGIN_LST_FOP
|
||||
const struct file_operations mypipe_fops = {.owner = THIS_MODULE,
|
||||
.open = mypipe_open,
|
||||
.read = mypipe_read,
|
||||
.write = mypipe_write,
|
||||
.release = mypipe_release};
|
||||
// END_LST_FOP
|
||||
|
||||
// Used to set the permission to allow any user to r/w
|
||||
static int mypipe_uevent(const struct device *dev,
|
||||
@@ -171,6 +221,7 @@ static int mypipe_uevent(const struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// BEGIN_LST_INIT
|
||||
static int __init mypipe_init(void) {
|
||||
int ret;
|
||||
dev_t dev;
|
||||
@@ -211,7 +262,9 @@ static int __init mypipe_init(void) {
|
||||
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_OUT),
|
||||
NULL, "%s_out", MYPIPE_DEVICE_NAME);
|
||||
|
||||
printk(KERN_INFO "Wendy: module loaded\n");
|
||||
pdata.is_active = false;
|
||||
|
||||
printk(KERN_INFO "%s: module loaded\n", MYPIPE_DEVICE_NAME);
|
||||
return 0;
|
||||
|
||||
del_cdev:
|
||||
@@ -229,10 +282,11 @@ static void __exit mypipe_destroy(void) {
|
||||
class_destroy(mypipedev_class);
|
||||
cdev_del(&pdata.cdev);
|
||||
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT);
|
||||
printk(KERN_INFO "Wendy: module unloaded");
|
||||
printk(KERN_INFO "%s: module unloaded", MYPIPE_DEVICE_NAME);
|
||||
}
|
||||
|
||||
module_init(mypipe_init);
|
||||
module_exit(mypipe_destroy);
|
||||
// END_LST_INIT
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user