Compare commits

...

10 Commits

Author SHA1 Message Date
de6e8570fe refactor code 2025-04-24 23:59:44 +08:00
ffa95bc8fc remove unused files 2025-04-24 23:57:56 +08:00
201fc5cc93 lab6 report 2025-04-24 23:50:26 +08:00
c4e4d69b8d exp complete 2025-04-24 23:37:58 +08:00
35e60a8b6d add testing text 2025-04-24 23:36:08 +08:00
2722ab3feb add new tests 2025-04-24 23:32:10 +08:00
7da2a7bd58 avoid read before write 2025-04-24 22:33:24 +08:00
2f794d04b7 only allow one writer 2025-04-24 22:31:06 +08:00
86c6bb3f55 add exp 2025-04-23 23:56:54 +08:00
db88c4381a add tester 2025-04-23 23:54:09 +08:00
18 changed files with 27800 additions and 53 deletions

View File

@@ -36,7 +36,6 @@ std::deque<Customer> customer_q;
std::counting_semaphore<100> customer_num_sem{0}; std::counting_semaphore<100> customer_num_sem{0};
// END_LST_COUNT_SEM // END_LST_COUNT_SEM
void signal_handler(int signum) { void signal_handler(int signum) {
// std::cout << "\nReceived signal " << signum << ", stopping...\n"; // std::cout << "\nReceived signal " << signum << ", stopping...\n";
write(STDOUT_FILENO, "\nCaught SIGINT, shutting down...\n", 33); write(STDOUT_FILENO, "\nCaught SIGINT, shutting down...\n", 33);
@@ -59,11 +58,17 @@ void teller_thread(
(curr_tick - start_serving_time) / TIME_GRAN_MSEC)) (curr_tick - start_serving_time) / TIME_GRAN_MSEC))
.count() <= curr_customer.service_time) { .count() <= curr_customer.service_time) {
// We are still serving, do nothing // 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; goto next_tick;
} else { } else {
// We have just finished serving. // 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; is_serving = false;
} }
} }
@@ -86,7 +91,8 @@ void teller_thread(
} }
} else { } else {
std::osyncstream(std::cout) 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: next_tick:
@@ -108,7 +114,9 @@ void customer_thread(
std::chrono::time_point curr_tick = start_tick; std::chrono::time_point curr_tick = start_tick;
unsigned int tick_count = 0; 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) { while (curr_tick < target_tick) {
if (!bank_open) { if (!bank_open) {
return; return;
@@ -117,9 +125,11 @@ void customer_thread(
curr_tick += std::chrono::milliseconds(TIME_GRAN_MSEC); curr_tick += std::chrono::milliseconds(TIME_GRAN_MSEC);
std::this_thread::sleep_until(curr_tick); std::this_thread::sleep_until(curr_tick);
} }
// Wake up and push self into queue, then V(customer_num_sem) // 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); std::lock_guard<std::mutex> lock(customer_q_mtx);
customer_q.push_back(cus); customer_q.push_back(cus);
@@ -180,14 +190,15 @@ int main(int argc, char *argv[]) {
for (auto cu : customer_infos) { for (auto cu : customer_infos) {
try { try {
customers.emplace_back(customer_thread, cu, start_tick); customers.emplace_back(customer_thread, cu, start_tick);
} catch (const std::system_error &e) { }
std::cerr << "Failed to create customer thread, id = " << cu.id << ": " catch (const std::system_error &e) {
<< e.what() << std::endl; std::cerr << "Failed to create customer thread, id = " << cu.id
<< ": " << e.what() << std::endl;
} }
} }
// Wait for all children to shutdown (by ctrl-c handler) // Wait for all children to shutdown (by ctrl-c handler)
for (auto &t: customers) { for (auto &t : customers) {
if (t.joinable()) { if (t.joinable()) {
t.join(); t.join();
} }

Binary file not shown.

View File

@@ -17,7 +17,7 @@ struct ThreadArgs {
int high; int high;
}; };
void swap(int *a, int* b) { void swap(int *a, int *b) {
int temp = *b; int temp = *b;
*b = *a; *b = *a;
*a = temp; *a = temp;
@@ -25,7 +25,7 @@ void swap(int *a, int* b) {
// Returns the index of pivot in arr[]. // Returns the index of pivot in arr[].
int partition(int low, int high) { int partition(int low, int high) {
int pivot = numbers[high]; // Pivot element int pivot = numbers[high]; // Pivot element
int i = low - 1; int i = low - 1;
for (int j = low; j < high; j++) { for (int j = low; j < high; j++) {
@@ -51,7 +51,7 @@ void qsort_nothread(int low, int high) {
} }
void *qsort_thread(void *arg) { 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); // printf("Low: %d, high: %d\n", args->low, args->high);
if (args->high - args->low <= SINGLE_THREAD_SRTH) { if (args->high - args->low <= SINGLE_THREAD_SRTH) {
qsort_nothread(args->low, args->high); qsort_nothread(args->low, args->high);
@@ -59,14 +59,9 @@ void *qsort_thread(void *arg) {
} }
int pivot_idx = partition(args->low, args->high); int pivot_idx = partition(args->low, args->high);
struct ThreadArgs left_thd_arg = { struct ThreadArgs left_thd_arg = {.low = args->low, .high = pivot_idx - 1};
.low = args->low, struct ThreadArgs right_thd_arg = {.low = pivot_idx + 1,
.high = pivot_idx - 1 .high = args->high};
};
struct ThreadArgs right_thd_arg = {
.low = pivot_idx + 1,
.high = args->high
};
pthread_t left_handle, right_handle; pthread_t left_handle, right_handle;
pthread_create(&left_handle, NULL, qsort_thread, (void *)&left_thd_arg); pthread_create(&left_handle, NULL, qsort_thread, (void *)&left_thd_arg);
pthread_create(&right_handle, NULL, qsort_thread, (void *)&right_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 // LST_MAIN_FUNC
void do_qsort() { void do_qsort() {
struct ThreadArgs arg = { struct ThreadArgs arg = {.low = 0, .high = NUM_LENGTH - 1};
.low = 0,
.high = NUM_LENGTH - 1
};
qsort_thread(&arg); qsort_thread(&arg);
} }
int main() { int main() {
printf("Start reading file...\n"); printf("Start reading file...\n");
numbers = malloc(sizeof(int) * NUM_LENGTH); numbers = malloc(sizeof(int) * NUM_LENGTH);

View File

@@ -1,9 +1,78 @@
{ {
"files.associations": { "files.associations": {
"cdev.h": "c", "any": "cpp",
"module.h": "c", "array": "cpp",
"types.h": "c", "atomic": "cpp",
"init.h": "c" "bit": "cpp",
}, "*.tcc": "cpp",
"C_Cpp.default.compilerPath": "/usr/bin/clang++" "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"
}
} }

View File

@@ -1,29 +1,32 @@
obj-m += wendy.o obj-m += wendy.o
OUT_DIR := build 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 all: kern_mod reader writer tester
@true
kern_mod: wendy.c kern_mod:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) MO=$(PWD)/build/kern make -C wendy_mod
install: install: kern_mod
sudo insmod ./build/kern/wendy.ko make -C wendy_mod install
uninstall: rm:
sudo rmmod wendy make -C wendy_mod uninstall
reader: reader: dirprepare
mkdir -p $(OUT_DIR)
gcc pipe_read.c -o $(OUT_DIR)/pread gcc pipe_read.c -o $(OUT_DIR)/pread
writer: writer: dirprepare
mkdir -p $(OUT_DIR)
gcc pipe_write.c -o $(OUT_DIR)/pwrite 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: clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean make -C wendy_mod clean
rm -rf $(OUT_DIR) rm -rf $(OUT_DIR)

BIN
lab6/exps/longread_end.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

13709
lab6/make.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,42 @@
#include <chrono>
#include <fstream>
#include <iostream> #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() { 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
View File

@@ -0,0 +1,4 @@
*.aux
*.log
*.synctex.gz
*.synctex(busy)

BIN
lab6/report/report.pdf Normal file

Binary file not shown.

128
lab6/report/report.tex Normal file
View 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}

View File

@@ -1,7 +1,7 @@
{ {
"configurations": [ "configurations": [
{ {
"name": "Linux Kernel Module", "name": "Linux",
"includePath": [ "includePath": [
"/usr/lib/modules/6.14.3-arch1-1/build/include", "/usr/lib/modules/6.14.3-arch1-1/build/include",
"/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include", "/usr/lib/modules/6.14.3-arch1-1/build/arch/x86/include",

8
lab6/wendy_mod/.vscode/settings.json vendored Normal file
View 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
View 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

View File

@@ -7,6 +7,7 @@
// https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html // https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
// https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/ // https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/
// BEGIN_LST_DEF
#define MYPIPE_BUFFER_SIZE 4096 #define MYPIPE_BUFFER_SIZE 4096
#define MYPIPE_DEVICE_NAME "wendy" #define MYPIPE_DEVICE_NAME "wendy"
@@ -20,6 +21,8 @@ enum MyPipeMinors {
struct mypipe_data { struct mypipe_data {
struct cdev cdev; struct cdev cdev;
// Whether opened by some writer
bool is_active;
u8 buffer[MYPIPE_BUFFER_SIZE]; u8 buffer[MYPIPE_BUFFER_SIZE];
size_t write_pos; size_t write_pos;
size_t read_pos; size_t read_pos;
@@ -33,21 +36,50 @@ static struct class *mypipedev_class = NULL;
// Protects the buffer // Protects the buffer
static DEFINE_MUTEX(mypipe_lock); 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 // These are two wait_queue_head_t for storing tasks that are waiting for
// writing/ reading // writing/ reading
static DECLARE_WAIT_QUEUE_HEAD(read_queue); static DECLARE_WAIT_QUEUE_HEAD(read_queue);
static DECLARE_WAIT_QUEUE_HEAD(write_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) { 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; return 0;
} }
static int mypipe_release(struct inode *inode, struct file *file) { 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; return 0;
} }
// END_LST_OPEN_RELEASE
// BEGIN_LST_READ_WRITE
static ssize_t mypipe_read(struct file *filep, char __user *buf, size_t size, static ssize_t mypipe_read(struct file *filep, char __user *buf, size_t size,
loff_t *offset) { loff_t *offset) {
ssize_t ret = 0; 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 // We currently have no data, but we can wait for possible new to write in
while (pdata.data_len == 0) { while (pdata.data_len == 0) {
mutex_unlock(&mypipe_lock); 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) { if (filep->f_flags & O_NONBLOCK) {
// The caller says no waiting, so we don't wait
return -EAGAIN; return -EAGAIN;
} }
if (wait_event_interruptible(read_queue, pdata.data_len > 0)) { if (wait_event_interruptible(read_queue, pdata.data_len > 0)) {
@@ -158,11 +204,15 @@ out:
return ret; return ret;
} }
// END_LST_READ_WRITE
// BEGIN_LST_FOP
const struct file_operations mypipe_fops = {.owner = THIS_MODULE, const struct file_operations mypipe_fops = {.owner = THIS_MODULE,
.open = mypipe_open, .open = mypipe_open,
.read = mypipe_read, .read = mypipe_read,
.write = mypipe_write, .write = mypipe_write,
.release = mypipe_release}; .release = mypipe_release};
// END_LST_FOP
// Used to set the permission to allow any user to r/w // Used to set the permission to allow any user to r/w
static int mypipe_uevent(const struct device *dev, static int mypipe_uevent(const struct device *dev,
@@ -171,6 +221,7 @@ static int mypipe_uevent(const struct device *dev,
return 0; return 0;
} }
// BEGIN_LST_INIT
static int __init mypipe_init(void) { static int __init mypipe_init(void) {
int ret; int ret;
dev_t dev; dev_t dev;
@@ -211,7 +262,9 @@ static int __init mypipe_init(void) {
device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_OUT), device_create(mypipedev_class, NULL, MKDEV(dev_major, MYPIPE_MINOR_OUT),
NULL, "%s_out", MYPIPE_DEVICE_NAME); 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; return 0;
del_cdev: del_cdev:
@@ -229,10 +282,11 @@ static void __exit mypipe_destroy(void) {
class_destroy(mypipedev_class); class_destroy(mypipedev_class);
cdev_del(&pdata.cdev); cdev_del(&pdata.cdev);
unregister_chrdev_region(MKDEV(dev_major, 0), MYPIPE_MINOR_COUNT); 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_init(mypipe_init);
module_exit(mypipe_destroy); module_exit(mypipe_destroy);
// END_LST_INIT
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");