diff --git a/lab2/qsort.c b/lab2/qsort.c index 3d28f84..ea02d23 100644 --- a/lab2/qsort.c +++ b/lab2/qsort.c @@ -11,6 +11,7 @@ int *numbers; +// LST_QSORT_IMPL struct ThreadArgs { int low; int high; @@ -72,7 +73,9 @@ void *qsort_thread(void *arg) { pthread_join(left_handle, NULL); pthread_join(right_handle, NULL); } +// LST_QSORT_IMPL +// LST_MAIN_FUNC void do_qsort() { struct ThreadArgs arg = { .low = 0, @@ -122,4 +125,5 @@ int main() { } fclose(file); return 0; -} \ No newline at end of file +} +// LST_MAIN_FUNC \ No newline at end of file diff --git a/lab2/report/.gitignore b/lab2/report/.gitignore new file mode 100644 index 0000000..579d7de --- /dev/null +++ b/lab2/report/.gitignore @@ -0,0 +1,4 @@ +*.aux +*.log +*.synctex.gz +*.synctex(busy) \ No newline at end of file diff --git a/lab2/report/img/Screenshot 2025-04-20 234440.png b/lab2/report/img/Screenshot 2025-04-20 234440.png new file mode 100644 index 0000000..0908c8e Binary files /dev/null and b/lab2/report/img/Screenshot 2025-04-20 234440.png differ diff --git a/lab2/report/img/Screenshot 2025-04-20 234647.png b/lab2/report/img/Screenshot 2025-04-20 234647.png new file mode 100644 index 0000000..6c33ed7 Binary files /dev/null and b/lab2/report/img/Screenshot 2025-04-20 234647.png differ diff --git a/lab2/report/report.pdf b/lab2/report/report.pdf new file mode 100644 index 0000000..3a12179 Binary files /dev/null and b/lab2/report/report.pdf differ diff --git a/lab2/report/report.tex b/lab2/report/report.tex new file mode 100644 index 0000000..7302846 --- /dev/null +++ b/lab2/report/report.tex @@ -0,0 +1,141 @@ +\documentclass[12pt, a4paper]{article} +\usepackage[UTF8]{ctex} +% \usepackage[UTF8]{ctex} +\usepackage{geometry} +\usepackage{amsmath} +\usepackage{amsthm} +\usepackage{amssymb} +\usepackage{enumitem} +% \usepackage{subfig} +\usepackage{float} +\usepackage{graphicx} +\usepackage{extarrows} +\usepackage{booktabs} +\usepackage{diagbox} +\usepackage{siunitx} +\usepackage{subcaption} +\usepackage{fontspec} +\usepackage{listings} +\usepackage{xcolor} +\usepackage{siunitx} + +\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 = //\ LST_, + rangeendprefix = //\ LST_ +} + +\renewcommand{\qedsymbol}{} + +\lstMakeShortInline| + +\geometry{a4paper,scale=0.8} + +\allowdisplaybreaks[4] %允许公式跨页 + +\setenumerate[1]{label=\arabic{*}. } +\setenumerate[2]{label=(\arabic{*})} + +\title{\Huge{{\bf 操作系统大作业实验2报告}}} +\author{高艺轩 2022010639} +\date{} + +\begin{document} +\maketitle +\section{实验目的} +\begin{enumerate} + \item 通过对进程间高级通信问题的片成实现,加深理解进程间高级通信的原理; + \item 对Windows或Linux涉及的几种高级进程间通信机制有更进一步的了解; + \item 熟悉Windows或Linux中定义的与高级进程间通信有关的函数。 +\end{enumerate} + +\section{实验题目} +本次实验选择的题目是“快速排序问题”。 + +\section{问题描述} +对有\num{1000000}个乱序数据的数据文件执行快速排序。 + +\begin{enumerate} + \item 首先产生包含\num{1000000}个随机数(数据类型可选整型或者浮点型)的数据文件; + \item 每次数据分割后产生两个新的进程(或线程)处理分割后的数据,每个进程(线程)处理的数据小于1000以后不再分割(控制产生的进程在20个左右); + \item 线程(或进程)之间的通信可以选择下述机制之一进行: + \begin{itemize} + \item 管道(无名管道或命名管道) + \item 消息队列 + \item 共享内存 + \end{itemize} + \item 通过适当的函数调用创建函数IPC对象,通过调用适当的函数调用实现数据的读出与写入; + \item 需要考虑线程(或进程)间的同步; + \item 线程(或进程)运行结束,通过适当的系统调用结束线程(或进程)。 +\end{enumerate} + +\section{思路与程序结构} +快速排序作为一种经典的分治算法,天然地适合多线程并行执行。在快速排序的各个分支调用中,调用的列表部分完全不重合,线程之间在逻辑上完全不可能出现数据竞争的情况,因此在实现算法时自然地想到可以直接使用共享内存实现,它们甚至不需要Mutex就可以保证进程间同步。 + +具体细节上,采用多线程来实现并行,因为相比起使用多进程,多线程中共享内存只需要使用全局变量即可,极大地减少了编程的复杂度。为了防止分治导致的产生的线程数过多,当递归调用的范围小于\num{1000}时,改为调用单线程版本的|qsort|,不再产生更多子线程。 + +实验中,直接用全局变量|int numbers[]|实现待排序数组的存储。快速排序算法主要分为两个步骤:从待排区间中选取一个作为基准,将区间中所有小于基准的都放在基准左边,所有大于基准的都放在基准右边;在基准左与基准右侧都递归调用快速排序。具体的实现如下: +\lstinputlisting[language=C, linerange=QSORT_IMPL]{../qsort.c} + +最后,在|main|中,只需要从文件中读入数据、存入|numbers[]|,调用排序,并将排序好的数据重新写入文件中即可: +\lstinputlisting[language=C, linerange=MAIN_FUNC]{../qsort.c} + + +\section{程序运行情况} +利用|gen_num.sh|在|numbers.txt|生成\num{1000000}个随机数(将1-\num{1000000})随机排列;之后调用|make run|运行程序,运行的结果存储在|sorted.txt|中。 + +运行效果: + +\begin{figure}[H] + \centering + \begin{subfigure}[t]{0.45\linewidth} + \includegraphics[width=\textwidth]{img/Screenshot 2025-04-20 234440.png} + \caption{生成的随机数} + \end{subfigure} + \hspace{3em} + \begin{subfigure}[t]{0.45\linewidth} + \includegraphics[width=\textwidth]{img/Screenshot 2025-04-20 234647.png} + \caption{排序后的随机数} + \end{subfigure} + \caption{程序测试结果} +\end{figure} + +\section{思考题解答} +\begin{enumerate} + \item 你采用了你选择的机制而不是另外的两种机制解决该问题,请解释你做出这种选择的理由。 + \begin{proof}[答] + 主要是考虑到本次要解决的的问题中,父线程要交给子进程大量的数据,而子线程排序完毕后还需要返回同样数量的数据,使用消息队列较为低效,而管道通常是单向传输的,因此不利于在本次问题中的双向信息传输。同时,由于快排算法本身逻辑上就不会造成数据竞争,因此也不需要信号量或者其它方法来对内存施加更多保护。另外,使用其它消息传递机制通常会导致不必要的内存拷贝,效率比共享内存更低。 + \end{proof} + \item 你认为另外的两种机制是否同样可以解决该问题?如果可以请给出你的思路;如果不能,请解释理由。 + \begin{proof}[答] + 我认为也可以解决。在本问题中,需要解决的核心问题是要使用单向传输的消息队列和管道机制解决双向通信。基本的思路应当是,在创建子线程前,父线程先创建两个消息队列/管道,其中一个是父进程写入的,一个是父进程读取的,之后再将它们的另一端交给子线程,再进行通信。 + \end{proof} +\end{enumerate} + +\section{实验总结} +本次实验本身并不复杂,在重新确认了快速排序的算法内容之后我很快就调试出了正确的程序。这是我第一次用C语言编写多线程函数,对多线程也有了更多的认识。 + +\end{document} \ No newline at end of file