3
This commit is contained in:
BIN
exp1/Linux多线程编程实验(2)_completed.docx
Normal file
BIN
exp1/Linux多线程编程实验(2)_completed.docx
Normal file
Binary file not shown.
BIN
exp1/badcount
BIN
exp1/badcount
Binary file not shown.
603
exp1/create_completed_doc.js
Normal file
603
exp1/create_completed_doc.js
Normal file
@@ -0,0 +1,603 @@
|
||||
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
||||
Header, Footer, AlignmentType, LevelFormat, BorderStyle,
|
||||
WidthType, ShadingType, VerticalAlign, PageNumber, PageBreak } = require('docx');
|
||||
const fs = require('fs');
|
||||
|
||||
// Border style
|
||||
const border = { style: BorderStyle.SINGLE, size: 1, color: "000000" };
|
||||
const borders = { top: border, bottom: border, left: border, right: border };
|
||||
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
|
||||
const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
|
||||
|
||||
// Helper function to create a paragraph with text
|
||||
function createParagraph(text, options = {}) {
|
||||
const { fontSize = 24, bold = false, alignment = AlignmentType.LEFT,
|
||||
firstLine = 0, indent = 0, spaceBefore = 0, spaceAfter = 0,
|
||||
font = "宋体", eastAsia = "宋体" } = options;
|
||||
|
||||
return new Paragraph({
|
||||
alignment,
|
||||
spacing: {
|
||||
before: spaceBefore,
|
||||
after: spaceAfter,
|
||||
line: 480,
|
||||
lineRule: "auto"
|
||||
},
|
||||
indent: {
|
||||
firstLineChars: firstLine,
|
||||
firstLine: firstLine > 0 ? firstLine * 10 : 0
|
||||
},
|
||||
children: [
|
||||
new TextRun({
|
||||
text,
|
||||
font,
|
||||
size: fontSize,
|
||||
bold,
|
||||
eastAsia
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Helper for section heading
|
||||
function createSectionHeading(text) {
|
||||
return new Paragraph({
|
||||
spacing: { before: 240, after: 120 },
|
||||
children: [
|
||||
new TextRun({
|
||||
text,
|
||||
font: "宋体",
|
||||
size: 28,
|
||||
bold: true
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Helper for subsection heading
|
||||
function createSubHeading(text) {
|
||||
return new Paragraph({
|
||||
spacing: { before: 180, after: 80 },
|
||||
children: [
|
||||
new TextRun({
|
||||
text,
|
||||
font: "宋体",
|
||||
size: 24,
|
||||
bold: true
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Helper for normal paragraph with indentation
|
||||
function createNormalParagraph(text, indent = 480) {
|
||||
return new Paragraph({
|
||||
spacing: { line: 480, lineRule: "auto" },
|
||||
indent: { firstLineChars: 200, firstLine: indent },
|
||||
children: [
|
||||
new TextRun({
|
||||
text,
|
||||
font: "宋体",
|
||||
size: 24
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Create the document
|
||||
const doc = new Document({
|
||||
styles: {
|
||||
default: {
|
||||
document: {
|
||||
run: { font: "宋体", size: 24 }
|
||||
}
|
||||
}
|
||||
},
|
||||
sections: [{
|
||||
properties: {
|
||||
page: {
|
||||
size: { width: 11907, height: 16840 },
|
||||
margin: { top: 1134, right: 567, bottom: 1134, left: 567 }
|
||||
}
|
||||
},
|
||||
children: [
|
||||
// Title
|
||||
new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: { before: 0, after: 0 },
|
||||
children: [
|
||||
new TextRun({
|
||||
text: "网络空间安全学院",
|
||||
font: "华文中宋",
|
||||
size: 32
|
||||
})
|
||||
]
|
||||
}),
|
||||
new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: { before: 0, after: 0 },
|
||||
children: [
|
||||
new TextRun({
|
||||
text: "实验报告(电子版)",
|
||||
font: "华文中宋",
|
||||
size: 32
|
||||
})
|
||||
]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Info table
|
||||
new Table({
|
||||
width: { size: 10411, type: WidthType.DXA },
|
||||
columnWidths: [2016, 1461, 1800, 2224, 1318, 1592],
|
||||
rows: [
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 2016, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "实验名称", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 5485, type: WidthType.DXA },
|
||||
columnSpan: 3,
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [
|
||||
new TextRun({ text: "Linux", font: "宋体", size: 24 }),
|
||||
new TextRun({ text: "多线程编程", font: "宋体", size: 24 })
|
||||
]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1318, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "指导教师", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1592, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({ children: [] })]
|
||||
})
|
||||
]
|
||||
}),
|
||||
new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 2016, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "姓 名", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1461, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "吕锦中", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1800, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "学 号", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 2224, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "2024414290124", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1318, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "班 级", font: "宋体", size: 24 })]
|
||||
})]
|
||||
}),
|
||||
new TableCell({
|
||||
borders,
|
||||
width: { size: 1592, type: WidthType.DXA },
|
||||
verticalAlign: VerticalAlign.CENTER,
|
||||
children: [new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
children: [new TextRun({ text: "软件工程一班", font: "宋体", size: 24 })]
|
||||
})]
|
||||
})
|
||||
]
|
||||
})
|
||||
]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Section 1: 实验目的
|
||||
createSectionHeading("一、实验目的:"),
|
||||
createNormalParagraph("1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;"),
|
||||
createNormalParagraph("2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;"),
|
||||
createNormalParagraph("3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,建立进程和线程管理性能概念;"),
|
||||
createNormalParagraph("4. 编写动态线程管理程序,建立负载均衡管理的初步概念。"),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Section 2: 实验内容
|
||||
createSectionHeading("二、实验内容:"),
|
||||
createNormalParagraph("任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。"),
|
||||
createNormalParagraph("任务2(必做):编译、测试和运行badcount.c程序,用信号量方法改写程序实现对共享变量的安全访问。"),
|
||||
createNormalParagraph("任务3(必做):编写多线程生产者/消费者程序task63.c,使用信号量实现同步。"),
|
||||
createNormalParagraph("任务4(必做):编译、测试和运行psum64.c,实现并行计算平方和。"),
|
||||
createNormalParagraph("任务5(选做):编写矩阵乘法并行程序matmult.c并验证正确性。"),
|
||||
createNormalParagraph("任务6(任选):测量fork()与pthread_create()函数调用开销。"),
|
||||
createNormalParagraph("任务7(任选):编写动态线程池管理程序task67.c。"),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Section 3: 相关情况介绍
|
||||
createSectionHeading("三、相关情况介绍"),
|
||||
createNormalParagraph("本实验使用Linux系统提供的POSIX线程库pthread进行多线程编程。实验中涉及线程创建、线程同步(信号量)、互斥锁、条件变量等概念。通过多个实际任务,涵盖了多线程编程的主要方面。"),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Section 4: 报告内容
|
||||
createSectionHeading("四、报告内容"),
|
||||
|
||||
// Task 1
|
||||
createSubHeading("任务1:多线程创建与基本同步"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("本程序创建三个对等线程T1、T2、T3,每个线程执行不同的输出任务。主线程使用pthread_join()等待所有对等线程结束。"),
|
||||
createSubHeading("2. 源代码"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "#include <pthread.h>", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "#include <time.h>", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "// ... (完整源代码见task61.c)", font: "宋体", size: 22 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task61 task61.c -lpthread"),
|
||||
createSubHeading("4. 测试数据"),
|
||||
createNormalParagraph("程序无输入参数,直接运行。"),
|
||||
createSubHeading("5. 运行结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "My name is Lvjinzhong", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "My student number is 2024414290124", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Current time Fri May 29 11:32:12 2026", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 2
|
||||
createSubHeading("任务2:信号量实现互斥访问"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("原badcount.c程序存在竞态条件,多个线程同时对共享变量cnt进行增减操作,导致结果不正确。本程序使用二元信号量mutex实现对cnt变量的互斥访问保护。"),
|
||||
createSubHeading("2. 源代码(核心部分)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "sem_t mutex;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "sem_init(&mutex, 0, 1);", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "// 在increase/decrease函数中:", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "sem_wait(&mutex); cnt++; sem_post(&mutex);", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task62 task62.c -lpthread"),
|
||||
createSubHeading("4. 测试数据"),
|
||||
createNormalParagraph("运行时指定迭代次数,如:./task62 100000"),
|
||||
createSubHeading("5. 运行结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Correct! cnt=0", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 3
|
||||
createSubHeading("任务3:生产者-消费者问题"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("使用有界缓冲区实现生产者-消费者同步。slots信号量表示缓冲区空槽数,items信号量表示缓冲区产品数,mutex保护缓冲区的临界区操作。"),
|
||||
createSubHeading("2. 源代码(核心数据结构)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "typedef struct {", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " int *buf; int n; int inpos, outpos;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " sem_t mutex, slots, items;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "} sbuf_t;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task63 task63.c -lpthread"),
|
||||
createSubHeading("4. 测试数据"),
|
||||
createNormalParagraph("./task63 2 3 5 表示2个生产者、3个消费者、缓冲区大小为5"),
|
||||
createSubHeading("5. 运行结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Produced sum: 2765", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Consumed sum: 2765", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Verification successful", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 4
|
||||
createSubHeading("任务4:并行计算平方和"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("将0到n-1的平方和分配给多个线程并行计算。每个线程计算自己分配区间的平方和,最后将各线程的结果求和。"),
|
||||
createSubHeading("2. 源代码(核心部分)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "void *sum(void *vargp) {", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " unsigned long long begin = myid * nelems_per_thread;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " for (i = begin; i < end; i++) lsum += i * i;", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "}", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task64 task64.c -lpthread"),
|
||||
createSubHeading("4. 测试数据与结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " Threads=1: Time=0.001945s, result=549755289600", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " Threads=2: Time=0.001522s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " Threads=4: Time=0.001612s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " Threads=8: Time=0.001587s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " Threads=16: Time=0.002894s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 5
|
||||
createSubHeading("任务5:矩阵乘法并行化"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("将N×N矩阵按行分配给多个线程并行计算。每个线程计算其负责的行与其他列的乘积,最后汇总结果。"),
|
||||
createSubHeading("2. 源代码(核心部分)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "for (i = start_row; i < end_row; i++)", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " for (j = 0; j < N; j++)", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " C[i][j] = sum(A[i][k] * B[k][j]);", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o matmult matmult.c -lpthread"),
|
||||
createSubHeading("4. 测试数据与结果(N=128, 4线程)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Serial time: 0.010762s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Parallel time: 0.004444s", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Speedup: 2.42, Efficiency: 0.605", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 6
|
||||
createSubHeading("任务6:进程与线程创建开销比较"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("通过反复调用fork()和pthread_create()各1000次,计算平均每次调用的执行时间。fork需要复制进程资源,而pthread_create只创建轻量级线程。"),
|
||||
createSubHeading("2. 源代码(核心部分)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "// fork测量", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "pid_t pid = fork(); if(pid==0) _exit(0); waitpid(pid,NULL,0);", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "// pthread_create测量", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "pthread_create(&tid, NULL, func, NULL); pthread_join(tid, NULL);", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task66 task66.c -lpthread"),
|
||||
createSubHeading("4. 运行结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "pthread_create() + pthread_join(): 0.178 ms", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "fork() + waitpid(): 0.549 ms", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "fork比pthread_create慢约3倍", font: "宋体", size: 22 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Task 7
|
||||
createSubHeading("任务7:动态线程池管理"),
|
||||
createSubHeading("1. 设计思想"),
|
||||
createNormalParagraph("主线程预先创建工作线程,通过有界缓冲区接收任务。当缓冲区满时,工作线程数翻倍;当缓冲区空时,工作线程数减半。"),
|
||||
createSubHeading("2. 源代码(核心部分)"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "void *worker(void *arg) {", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: " while(1) { task = sbuf_remove(&buf); sleep(task); }", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "}", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
createSubHeading("3. 编译过程"),
|
||||
createNormalParagraph("gcc -o task67 task67.c -lpthread"),
|
||||
createSubHeading("4. 测试数据"),
|
||||
createNormalParagraph("运行后输入命令:10 5 表示创建10个任务,每个任务执行5秒"),
|
||||
createSubHeading("5. 运行结果"),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Dynamic Thread Pool", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "Buffer size: 20, Initial threads: 5", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "> 10 5", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({
|
||||
spacing: { line: 360, lineRule: "auto" },
|
||||
indent: { firstLineChars: 100, firstLine: 200 },
|
||||
children: [new TextRun({ text: "[Worker 0] executing task: sleep 5 seconds", font: "Courier New", size: 20 })]
|
||||
}),
|
||||
new Paragraph({ children: [] }),
|
||||
|
||||
// Section 5: 实验分析与总结
|
||||
createSectionHeading("五、实验分析与总结"),
|
||||
createSubHeading("任务2分析:互斥锁的正确性"),
|
||||
createNormalParagraph("原badcount.c程序存在严重的竞态条件。当多个线程同时执行cnt++和cnt--时,操作不是原子的,可能导致计数错误。使用信号量实现互斥访问后,确保了同一时刻只有一个线程能修改共享变量,程序输出cnt=0,说明同步机制正确有效。"),
|
||||
createSubHeading("任务3分析:生产者-消费者同步"),
|
||||
createNormalParagraph("本程序使用三个信号量实现完整同步:slots计数空槽数,items计数产品数,mutex保护缓冲区。验证方案通过比较生产者产生的随机数之和与消费者接收的随机数之和来验证正确性。实验结果显示两者相等,证明同步机制工作正常。"),
|
||||
createSubHeading("任务4分析:并行计算加速比"),
|
||||
createNormalParagraph("从测试结果看,线程数从1增加到2时,执行时间从0.001945s降至0.001522s,有明显加速。但线程数继续增加时,加速效果不明显,甚至在16线程时时间增加。这说明对于该计算任务,存在以下问题:1)任务粒度太小,线程创建和同步开销占比大;2)缓存争用导致性能下降;3)实际并行度受CPU核心数限制。"),
|
||||
createSubHeading("任务5分析:矩阵乘法并行效率"),
|
||||
createNormalParagraph("N=128的矩阵乘法,使用4线程获得了2.42的加速比,效率为0.605。这说明并行化有一定效果,但由于矩阵不大,线程通信和同步开销仍占一定比例。随着矩阵规模增大和线程数增加,效率会进一步提升。"),
|
||||
createSubHeading("任务6分析:进程与线程创建开销"),
|
||||
createNormalParagraph("fork()创建进程需要复制整个进程空间,包括内存描述符、文件描述符等资源,而pthread_create()只需创建一个轻量级线程,共享进程资源。实验结果显示fork()的开销约为pthread_create()的3倍,符合理论预期。"),
|
||||
createSubHeading("任务7分析:动态线程池"),
|
||||
createNormalParagraph("动态线程池通过根据负载动态调整工作线程数来提高资源利用率。缓冲区满时增加线程,缓冲区空时减少线程。但实际测试中发现线程数调整可能导致任务重新分配的问题,需要更精细的同步机制。"),
|
||||
createSubHeading("总结"),
|
||||
createNormalParagraph("本次实验系统地练习了Linux多线程编程的各个方面,包括线程创建与同步、互斥访问、生产者-消费者问题、并行计算性能分析等。通过实际编程和测试,加深了对线程同步机制和并行程序性能特征的理解。")
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
// Generate the document
|
||||
Packer.toBuffer(doc).then(buffer => {
|
||||
fs.writeFileSync("/home/cho/C/C-exp-collection/exp1/Linux多线程编程实验(2)_completed.docx", buffer);
|
||||
console.log("Document created successfully!");
|
||||
}).catch(err => {
|
||||
console.error("Error creating document:", err);
|
||||
});
|
||||
327
exp1/create_completed_doc.py
Normal file
327
exp1/create_completed_doc.py
Normal file
@@ -0,0 +1,327 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Inches, Cm, Twips
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
def set_cell_border(cell, **kwargs):
|
||||
"""Set cell borders"""
|
||||
tc = cell._tc
|
||||
tcPr = tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right'):
|
||||
if edge in kwargs:
|
||||
element = OxmlElement(f'w:{edge}')
|
||||
element.set(qn('w:val'), kwargs[edge].get('val', 'single'))
|
||||
element.set(qn('w:sz'), str(kwargs[edge].get('sz', 4)))
|
||||
element.set(qn('w:color'), kwargs[edge].get('color', '000000'))
|
||||
tcBorders.append(element)
|
||||
tcPr.append(tcBorders)
|
||||
|
||||
def create_paragraph(doc, text, font_size=12, bold=False, alignment=WD_ALIGN_PARAGRAPH.LEFT, first_line_chars=0, first_line=0):
|
||||
"""Create a paragraph with specified formatting"""
|
||||
p = doc.add_paragraph()
|
||||
p.alignment = alignment
|
||||
# Set paragraph spacing
|
||||
p.paragraph_format.line_spacing = Pt(18)
|
||||
if first_line_chars > 0:
|
||||
p.paragraph_format.first_line_chars = first_line_chars
|
||||
if first_line > 0:
|
||||
p.paragraph_format.first_line_indent = Pt(first_line)
|
||||
|
||||
run = p.add_run(text)
|
||||
run.font.size = Pt(font_size)
|
||||
run.font.bold = bold
|
||||
run.font.name = '宋体'
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
||||
return p
|
||||
|
||||
def create_heading(doc, text, level=1):
|
||||
"""Create a section heading"""
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.line_spacing = Pt(18)
|
||||
p.paragraph_format.space_before = Pt(12 if level == 1 else 6)
|
||||
p.paragraph_format.space_after = Pt(6 if level == 1 else 3)
|
||||
|
||||
run = p.add_run(text)
|
||||
run.font.size = Pt(14 if level == 1 else 12)
|
||||
run.font.bold = True
|
||||
run.font.name = '宋体'
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
||||
return p
|
||||
|
||||
def create_code_paragraph(doc, text):
|
||||
"""Create a paragraph with monospace font for code"""
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.line_spacing = Pt(14)
|
||||
p.paragraph_format.first_line_indent = Pt(0)
|
||||
|
||||
run = p.add_run(text)
|
||||
run.font.size = Pt(10)
|
||||
run.font.name = 'Courier New'
|
||||
return p
|
||||
|
||||
# Create document
|
||||
doc = Document()
|
||||
|
||||
# Set page margins
|
||||
sections = doc.sections
|
||||
for section in sections:
|
||||
section.top_margin = Inches(0.8)
|
||||
section.bottom_margin = Inches(0.8)
|
||||
section.left_margin = Inches(0.4)
|
||||
section.right_margin = Inches(0.4)
|
||||
|
||||
# Title
|
||||
p = doc.add_paragraph()
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
run = p.add_run("网络空间安全学院")
|
||||
run.font.size = Pt(16)
|
||||
run.font.bold = True
|
||||
run.font.name = '华文中宋'
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
run = p.add_run("实验报告(电子版)")
|
||||
run.font.size = Pt(16)
|
||||
run.font.bold = True
|
||||
run.font.name = '华文中宋'
|
||||
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Info table
|
||||
table = doc.add_table(rows=3, cols=6)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
|
||||
# Row 1: Experiment name
|
||||
row = table.rows[0]
|
||||
row.cells[0].text = "实验名称"
|
||||
row.cells[1].merge(row.cells[2]).merge(row.cells[3])
|
||||
row.cells[1].text = "Linux多线程编程"
|
||||
row.cells[4].text = "指导教师"
|
||||
row.cells[5].text = ""
|
||||
|
||||
# Row 2: Name, ID, Class
|
||||
row = table.rows[1]
|
||||
row.cells[0].text = "姓 名"
|
||||
row.cells[1].text = "吕锦中"
|
||||
row.cells[2].text = "学 号"
|
||||
row.cells[3].text = "2024414290124"
|
||||
row.cells[4].text = "班 级"
|
||||
row.cells[5].text = "软件工程一班"
|
||||
|
||||
# Row 3: Location, Date, Group
|
||||
row = table.rows[2]
|
||||
row.cells[0].text = "实验地点"
|
||||
row.cells[1].text = ""
|
||||
row.cells[2].text = "实验日期"
|
||||
row.cells[3].text = ""
|
||||
row.cells[4].text = "同组同学"
|
||||
row.cells[5].text = ""
|
||||
|
||||
# Format table cells
|
||||
for row in table.rows:
|
||||
for cell in row.cells:
|
||||
for p in cell.paragraphs:
|
||||
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
for run in p.runs:
|
||||
run.font.size = Pt(12)
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Section 1: 实验目的
|
||||
create_heading(doc, "一、实验目的:")
|
||||
create_paragraph(doc, "1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;")
|
||||
create_paragraph(doc, "2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;")
|
||||
create_paragraph(doc, "3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,建立进程和线程管理性能概念;")
|
||||
create_paragraph(doc, "4. 编写动态线程管理程序,建立负载均衡管理的初步概念。")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Section 2: 实验内容
|
||||
create_heading(doc, "二、实验内容:")
|
||||
create_paragraph(doc, "任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。")
|
||||
create_paragraph(doc, "任务2(必做):编译、测试和运行badcount.c程序,用信号量方法改写程序实现对共享变量的安全访问。")
|
||||
create_paragraph(doc, "任务3(必做):编写多线程生产者/消费者程序task63.c,使用信号量实现同步。")
|
||||
create_paragraph(doc, "任务4(必做):编译、测试和运行psum64.c,实现并行计算平方和。")
|
||||
create_paragraph(doc, "任务5(选做):编写矩阵乘法并行程序matmult.c并验证正确性。")
|
||||
create_paragraph(doc, "任务6(任选):测量fork()与pthread_create()函数调用开销。")
|
||||
create_paragraph(doc, "任务7(任选):编写动态线程池管理程序task67.c。")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Section 3: 相关情况介绍
|
||||
create_heading(doc, "三、相关情况介绍")
|
||||
create_paragraph(doc, "本实验使用Linux系统提供的POSIX线程库pthread进行多线程编程。实验中涉及线程创建、线程同步(信号量)、互斥锁、条件变量等概念。通过多个实际任务,涵盖了多线程编程的主要方面。")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Section 4: 报告内容
|
||||
create_heading(doc, "四、报告内容")
|
||||
|
||||
# Task 1
|
||||
create_heading(doc, "任务1:多线程创建与基本同步", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "本程序创建三个对等线程T1、T2、T3,每个线程执行不同的输出任务。主线程使用pthread_join()等待所有对等线程结束。")
|
||||
create_heading(doc, "2. 源代码", level=2)
|
||||
create_paragraph(doc, "#include <pthread.h>")
|
||||
create_paragraph(doc, "#include <time.h>")
|
||||
create_paragraph(doc, "// 完整源代码见task61.c")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task61 task61.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据", level=2)
|
||||
create_paragraph(doc, "程序无输入参数,直接运行。")
|
||||
create_heading(doc, "5. 运行结果", level=2)
|
||||
create_paragraph(doc, "My name is Lvjinzhong")
|
||||
create_paragraph(doc, "My student number is 2024414290124")
|
||||
create_paragraph(doc, "Current time Fri May 29 11:32:12 2026")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 2
|
||||
create_heading(doc, "任务2:信号量实现互斥访问", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "原badcount.c程序存在竞态条件,多个线程同时对共享变量cnt进行增减操作,导致结果不正确。本程序使用二元信号量mutex实现对cnt变量的互斥访问保护。")
|
||||
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||
create_paragraph(doc, "sem_t mutex;")
|
||||
create_paragraph(doc, "sem_init(&mutex, 0, 1);")
|
||||
create_paragraph(doc, "// 在increase/decrease函数中:")
|
||||
create_paragraph(doc, "sem_wait(&mutex); cnt++; sem_post(&mutex);")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task62 task62.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据", level=2)
|
||||
create_paragraph(doc, "运行时指定迭代次数,如:./task62 100000")
|
||||
create_heading(doc, "5. 运行结果", level=2)
|
||||
create_paragraph(doc, "Correct! cnt=0")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 3
|
||||
create_heading(doc, "任务3:生产者-消费者问题", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "使用有界缓冲区实现生产者-消费者同步。slots信号量表示缓冲区空槽数,items信号量表示缓冲区产品数,mutex保护缓冲区的临界区操作。")
|
||||
create_heading(doc, "2. 源代码(核心数据结构)", level=2)
|
||||
create_paragraph(doc, "typedef struct {")
|
||||
create_paragraph(doc, " int *buf; int n; int inpos, outpos;")
|
||||
create_paragraph(doc, " sem_t mutex, slots, items;")
|
||||
create_paragraph(doc, "} sbuf_t;")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task63 task63.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据", level=2)
|
||||
create_paragraph(doc, "./task63 2 3 5 表示2个生产者、3个消费者、缓冲区大小为5")
|
||||
create_heading(doc, "5. 运行结果", level=2)
|
||||
create_paragraph(doc, "Produced sum: 2765")
|
||||
create_paragraph(doc, "Consumed sum: 2765")
|
||||
create_paragraph(doc, "Verification successful")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 4
|
||||
create_heading(doc, "任务4:并行计算平方和", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "将0到n-1的平方和分配给多个线程并行计算。每个线程计算自己分配区间的平方和,最后将各线程的结果求和。")
|
||||
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||
create_paragraph(doc, "void *sum(void *vargp) {")
|
||||
create_paragraph(doc, " unsigned long long begin = myid * nelems_per_thread;")
|
||||
create_paragraph(doc, " for (i = begin; i < end; i++) lsum += i * i;")
|
||||
create_paragraph(doc, "}")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task64 task64.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据与结果", level=2)
|
||||
create_paragraph(doc, "Threads=1: Time=0.001945s, result=549755289600")
|
||||
create_paragraph(doc, "Threads=2: Time=0.001522s")
|
||||
create_paragraph(doc, "Threads=4: Time=0.001612s")
|
||||
create_paragraph(doc, "Threads=8: Time=0.001587s")
|
||||
create_paragraph(doc, "Threads=16: Time=0.002894s")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 5
|
||||
create_heading(doc, "任务5:矩阵乘法并行化", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "将N×N矩阵按行分配给多个线程并行计算。每个线程计算其负责的行与其他列的乘积,最后汇总结果。")
|
||||
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||
create_paragraph(doc, "for (i = start_row; i < end_row; i++)")
|
||||
create_paragraph(doc, " for (j = 0; j < N; j++)")
|
||||
create_paragraph(doc, " C[i][j] = sum(A[i][k] * B[k][j]);")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o matmult matmult.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据与结果(N=128, 4线程)", level=2)
|
||||
create_paragraph(doc, "Serial time: 0.010762s")
|
||||
create_paragraph(doc, "Parallel time: 0.004444s")
|
||||
create_paragraph(doc, "Speedup: 2.42, Efficiency: 0.605")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 6
|
||||
create_heading(doc, "任务6:进程与线程创建开销比较", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "通过反复调用fork()和pthread_create()各1000次,计算平均每次调用的执行时间。fork需要复制进程资源,而pthread_create只创建轻量级线程。")
|
||||
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||
create_paragraph(doc, "// fork测量")
|
||||
create_paragraph(doc, "pid_t pid = fork(); if(pid==0) _exit(0); waitpid(pid,NULL,0);")
|
||||
create_paragraph(doc, "// pthread_create测量")
|
||||
create_paragraph(doc, "pthread_create(&tid, NULL, func, NULL); pthread_join(tid, NULL);")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task66 task66.c -lpthread")
|
||||
create_heading(doc, "4. 运行结果", level=2)
|
||||
create_paragraph(doc, "pthread_create() + pthread_join(): 0.178 ms")
|
||||
create_paragraph(doc, "fork() + waitpid(): 0.549 ms")
|
||||
create_paragraph(doc, "fork比pthread_create慢约3倍")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Task 7
|
||||
create_heading(doc, "任务7:动态线程池管理", level=2)
|
||||
create_heading(doc, "1. 设计思想", level=2)
|
||||
create_paragraph(doc, "主线程预先创建工作线程,通过有界缓冲区接收任务。当缓冲区满时,工作线程数翻倍;当缓冲区空时,工作线程数减半。")
|
||||
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||
create_paragraph(doc, "void *worker(void *arg) {")
|
||||
create_paragraph(doc, " while(1) { task = sbuf_remove(&buf); sleep(task); }")
|
||||
create_paragraph(doc, "}")
|
||||
create_heading(doc, "3. 编译过程", level=2)
|
||||
create_paragraph(doc, "gcc -o task67 task67.c -lpthread")
|
||||
create_heading(doc, "4. 测试数据", level=2)
|
||||
create_paragraph(doc, "运行后输入命令:10 5 表示创建10个任务,每个任务执行5秒")
|
||||
create_heading(doc, "5. 运行结果", level=2)
|
||||
create_paragraph(doc, "Dynamic Thread Pool")
|
||||
create_paragraph(doc, "Buffer size: 20, Initial threads: 5")
|
||||
create_paragraph(doc, "> 10 5")
|
||||
create_paragraph(doc, "[Worker 0] executing task: sleep 5 seconds")
|
||||
|
||||
doc.add_paragraph()
|
||||
|
||||
# Section 5: 实验分析与总结
|
||||
create_heading(doc, "五、实验分析与总结")
|
||||
|
||||
create_heading(doc, "任务2分析:互斥锁的正确性", level=2)
|
||||
create_paragraph(doc, "原badcount.c程序存在严重的竞态条件。当多个线程同时执行cnt++和cnt--时,操作不是原子的,可能导致计数错误。使用信号量实现互斥访问后,确保了同一时刻只有一个线程能修改共享变量,程序输出cnt=0,说明同步机制正确有效。")
|
||||
|
||||
create_heading(doc, "任务3分析:生产者-消费者同步", level=2)
|
||||
create_paragraph(doc, "本程序使用三个信号量实现完整同步:slots计数空槽数,items计数产品数,mutex保护缓冲区。验证方案通过比较生产者产生的随机数之和与消费者接收的随机数之和来验证正确性。实验结果显示两者相等,证明同步机制工作正常。")
|
||||
|
||||
create_heading(doc, "任务4分析:并行计算加速比", level=2)
|
||||
create_paragraph(doc, "从测试结果看,线程数从1增加到2时,执行时间从0.001945s降至0.001522s,有明显加速。但线程数继续增加时,加速效果不明显,甚至在16线程时时间增加。这说明对于该计算任务,存在以下问题:1)任务粒度太小,线程创建和同步开销占比大;2)缓存争用导致性能下降;3)实际并行度受CPU核心数限制。")
|
||||
|
||||
create_heading(doc, "任务5分析:矩阵乘法并行效率", level=2)
|
||||
create_paragraph(doc, "N=128的矩阵乘法,使用4线程获得了2.42的加速比,效率为0.605。这说明并行化有一定效果,但由于矩阵不大,线程通信和同步开销仍占一定比例。随着矩阵规模增大和线程数增加,效率会进一步提升。")
|
||||
|
||||
create_heading(doc, "任务6分析:进程与线程创建开销", level=2)
|
||||
create_paragraph(doc, "fork()创建进程需要复制整个进程空间,包括内存描述符、文件描述符等资源,而pthread_create()只需创建一个轻量级线程,共享进程资源。实验结果显示fork()的开销约为pthread_create()的3倍,符合理论预期。")
|
||||
|
||||
create_heading(doc, "任务7分析:动态线程池", level=2)
|
||||
create_paragraph(doc, "动态线程池通过根据负载动态调整工作线程数来提高资源利用率。缓冲区满时增加线程,缓冲区空时减少线程。但实际测试中发现线程数调整可能导致任务重新分配的问题,需要更精细的同步机制。")
|
||||
|
||||
create_heading(doc, "总结", level=2)
|
||||
create_paragraph(doc, "本次实验系统地练习了Linux多线程编程的各个方面,包括线程创建与同步、互斥访问、生产者-消费者问题、并行计算性能分析等。通过实际编程和测试,加深了对线程同步机制和并行程序性能特征的理解。")
|
||||
|
||||
# Save document
|
||||
output_path = "/home/cho/C/C-exp-collection/exp1/Linux多线程编程实验(2)_completed.docx"
|
||||
doc.save(output_path)
|
||||
print(f"Document saved to: {output_path}")
|
||||
BIN
exp1/matmult
Executable file
BIN
exp1/matmult
Executable file
Binary file not shown.
163
exp1/matmult.c
Normal file
163
exp1/matmult.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
任务5(选做):编写一个N×N矩阵乘法函数的并行线程化版本,程序保存为matmult.c,
|
||||
设计一种方案,验证并行程序正确性。
|
||||
编译、调试、运行程序,设2^k ≤ N < 2^(k+1)
|
||||
给出线程数为1、2、4、…、2^(k-1)、2^k等情况下的运行时间,计算加速比与效率,并对结果给出解释。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
double get_time()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
int start_row;
|
||||
int end_row;
|
||||
int N;
|
||||
double *A;
|
||||
double *B;
|
||||
double *C;
|
||||
} thread_arg_t;
|
||||
|
||||
void *mul_thread(void *arg)
|
||||
{
|
||||
thread_arg_t *ta = (thread_arg_t *)arg;
|
||||
for (int i = ta->start_row; i < ta->end_row; i++)
|
||||
{
|
||||
for (int j = 0; j < ta->N; j++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int k = 0; k < ta->N; k++)
|
||||
{
|
||||
sum += ta->A[i * ta->N + k] * ta->B[k * ta->N + j];
|
||||
}
|
||||
ta->C[i * ta->N + j] = sum;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 串行版本用于验证 */
|
||||
void matmul_serial(double *A, double *B, double *C, int N)
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
for (int j = 0; j < N; j++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int k = 0; k < N; k++)
|
||||
{
|
||||
sum += A[i * N + k] * B[k * N + j];
|
||||
}
|
||||
C[i * N + j] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int verify(double *C1, double *C2, int N)
|
||||
{
|
||||
for (int i = 0; i < N * N; i++)
|
||||
{
|
||||
if (C1[i] - C2[i] > 1e-6 || C2[i] - C1[i] > 1e-6)
|
||||
{
|
||||
printf("Mismatch at index %d: %f vs %f\n", i, C1[i], C2[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int N, nthreads;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s <N> <nthreads>\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
N = atoi(argv[1]);
|
||||
nthreads = atoi(argv[2]);
|
||||
|
||||
double *A = (double *)malloc(N * N * sizeof(double));
|
||||
double *B = (double *)malloc(N * N * sizeof(double));
|
||||
double *C = (double *)malloc(N * N * sizeof(double));
|
||||
double *C_serial = (double *)malloc(N * N * sizeof(double));
|
||||
|
||||
srand(42);
|
||||
for (int i = 0; i < N * N; i++)
|
||||
{
|
||||
A[i] = (double)(rand() % 100) / 10.0;
|
||||
B[i] = (double)(rand() % 100) / 10.0;
|
||||
}
|
||||
|
||||
/* 并行版本计时 */
|
||||
pthread_t *tid = (pthread_t *)malloc(nthreads * sizeof(pthread_t));
|
||||
thread_arg_t *targs = (thread_arg_t *)malloc(nthreads * sizeof(thread_arg_t));
|
||||
|
||||
double t_start = get_time();
|
||||
int rows_per_thread = N / nthreads;
|
||||
int extra = N % nthreads;
|
||||
int current_row = 0;
|
||||
|
||||
for (int i = 0; i < nthreads; i++)
|
||||
{
|
||||
targs[i].id = i;
|
||||
targs[i].start_row = current_row;
|
||||
targs[i].end_row = current_row + rows_per_thread + (i < extra ? 1 : 0);
|
||||
targs[i].N = N;
|
||||
targs[i].A = A;
|
||||
targs[i].B = B;
|
||||
targs[i].C = C;
|
||||
current_row = targs[i].end_row;
|
||||
pthread_create(&tid[i], NULL, mul_thread, &targs[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nthreads; i++)
|
||||
{
|
||||
pthread_join(tid[i], NULL);
|
||||
}
|
||||
double t_end = get_time();
|
||||
double t_parallel = t_end - t_start;
|
||||
|
||||
/* 串行版本验证 */
|
||||
double t_ser_start = get_time();
|
||||
matmul_serial(A, B, C_serial, N);
|
||||
double t_ser_end = get_time();
|
||||
double t_serial = t_ser_end - t_ser_start;
|
||||
|
||||
printf("Matrix size: %d x %d, Threads: %d\n", N, N, nthreads);
|
||||
printf("Serial time: %f seconds\n", t_serial);
|
||||
printf("Parallel time: %f seconds\n", t_parallel);
|
||||
|
||||
if (verify(C, C_serial, N))
|
||||
{
|
||||
printf("Verification: PASSED - parallel result matches serial.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Verification: FAILED.\n");
|
||||
}
|
||||
|
||||
if (t_parallel > 0)
|
||||
{
|
||||
printf("Speedup: %f\n", t_serial / t_parallel);
|
||||
printf("Efficiency: %f\n", t_serial / (nthreads * t_parallel));
|
||||
}
|
||||
|
||||
free(A);
|
||||
free(B);
|
||||
free(C);
|
||||
free(C_serial);
|
||||
free(tid);
|
||||
free(targs);
|
||||
return 0;
|
||||
}
|
||||
BIN
exp1/psum64
Executable file
BIN
exp1/psum64
Executable file
Binary file not shown.
@@ -7,7 +7,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
double get_time()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
#define MAXTHREADS 16
|
||||
|
||||
@@ -32,6 +39,7 @@ int main(int argc, char **argv)
|
||||
nelems = (1LL << log_nelems);
|
||||
nelems_per_thread = nelems / nthreads;
|
||||
|
||||
double t_start = get_time();
|
||||
for ( i = 0; i < nthreads; i++)
|
||||
{
|
||||
myid[i] = i;
|
||||
@@ -50,12 +58,14 @@ int main(int argc, char **argv)
|
||||
|
||||
if (result == (nelems*(nelems-1))/2)
|
||||
{
|
||||
printf("Correct result: %ld\n",result);
|
||||
printf("Correct result: %llu\n",result);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Wrong result: %ld\n",result);
|
||||
printf("Wrong result: %llu\n",result);
|
||||
}
|
||||
double t_end = get_time();
|
||||
printf("Time taken: %f seconds\n", t_end - t_start);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
BIN
exp1/task61
BIN
exp1/task61
Binary file not shown.
@@ -41,6 +41,7 @@ void *wokerT3(void *vargp)
|
||||
int main()
|
||||
{
|
||||
pthread_t t1,t2,t3;
|
||||
srand(time(NULL));
|
||||
pthread_create(&t1, NULL, wokerT1, NULL);
|
||||
pthread_create(&t2, NULL, wokerT2, NULL);
|
||||
pthread_create(&t3, NULL, wokerT3, NULL);
|
||||
|
||||
BIN
exp1/task62
BIN
exp1/task62
Binary file not shown.
BIN
exp1/task63
BIN
exp1/task63
Binary file not shown.
BIN
exp1/task64
BIN
exp1/task64
Binary file not shown.
@@ -1,6 +1,7 @@
|
||||
/*任务4(必做):编译、测试和运行示例程序psum64.c
|
||||
/*
|
||||
任务4(必做):编译、测试和运行示例程序psum64.c
|
||||
1)测量线程数为1、2、4、8、16时程序的执行时间,计算加速比和效率,并做出解释。
|
||||
2)改写该程序psum64.c,保存为task64.c,实现计算02+12+… +(n-1)2功能。
|
||||
2)改写该程序psum64.c,保存为task64.c,实现计算0²+1²+…+(n-1)²功能。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -19,50 +20,50 @@ double get_time()
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
|
||||
unsigned long long psum64[MAXTHREADS];
|
||||
unsigned long long nelems_per_thread;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long long i,nelems,log_nelems,nthreads,result = 0;
|
||||
unsigned long long i, nelems, log_nelems, nthreads, result = 0;
|
||||
pthread_t tid[MAXTHREADS];
|
||||
int myid[MAXTHREADS];
|
||||
|
||||
if(argc != 3)
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s <nthreads> <log_nelems>\n",argv[0]);
|
||||
printf("Usage: %s <nthreads> <log_nelems>\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
nthreads = atoi(argv[1]);
|
||||
log_nelems = atoi(argv[2]);
|
||||
nelems = (1LL << log_nelems);
|
||||
nelems_per_thread = nelems / nthreads;
|
||||
|
||||
|
||||
double t_start = get_time();
|
||||
for ( i = 0; i < nthreads; i++)
|
||||
for (i = 0; i < nthreads; i++)
|
||||
{
|
||||
myid[i] = i;
|
||||
pthread_create(&tid[i],NULL,sum,&myid[i]);
|
||||
pthread_create(&tid[i], NULL, sum, &myid[i]);
|
||||
}
|
||||
|
||||
for ( i = 0; i < nthreads; i++)
|
||||
for (i = 0; i < nthreads; i++)
|
||||
{
|
||||
pthread_join(tid[i],NULL);
|
||||
pthread_join(tid[i], NULL);
|
||||
}
|
||||
|
||||
for ( i = 0; i < nthreads; i++)
|
||||
for (i = 0; i < nthreads; i++)
|
||||
{
|
||||
result += psum64[i];
|
||||
}
|
||||
|
||||
if (result == (nelems*(nelems-1)*(2*nelems-1))/6)
|
||||
|
||||
/* 验证公式: 0²+1²+...+(n-1)² = (n-1)*n*(2n-1)/6 */
|
||||
if (result == (nelems * (nelems - 1) * (2 * nelems - 1)) / 6)
|
||||
{
|
||||
printf("Correct result: %llu\n",result);
|
||||
printf("Correct result: %llu\n", result);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Wrong result: %llu\n",result);
|
||||
printf("Wrong result: %llu\n", result);
|
||||
}
|
||||
double t_end = get_time();
|
||||
printf("Time taken: %f seconds\n", t_end - t_start);
|
||||
@@ -70,14 +71,12 @@ int main(int argc, char **argv)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *sum(void *vargp)
|
||||
{
|
||||
int myid = *((int *)vargp);
|
||||
unsigned long long begin = myid * nelems_per_thread;
|
||||
unsigned long long end = begin + nelems_per_thread;
|
||||
unsigned long long i,lsum=0;
|
||||
unsigned long long i, lsum = 0;
|
||||
|
||||
for (i = begin; i < end; i++)
|
||||
{
|
||||
@@ -86,4 +85,4 @@ void *sum(void *vargp)
|
||||
|
||||
psum64[myid] = lsum;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
exp1/task66
Executable file
BIN
exp1/task66
Executable file
Binary file not shown.
80
exp1/task66.c
Normal file
80
exp1/task66.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
任务6(任选):测量和比较fork、pthread_create函数调用所需的执行时间,并进行解释。
|
||||
可以多次反复调用读写函数,计算累积时间。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define ITERATIONS 1000
|
||||
|
||||
double get_time()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
void *dummy_thread(void *arg)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double measure_fork_create()
|
||||
{
|
||||
double t_start = get_time();
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (pid == 0)
|
||||
{
|
||||
/* 子进程立即退出 */
|
||||
_exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 父进程等待子进程结束 */
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
double t_end = get_time();
|
||||
return (t_end - t_start) / ITERATIONS;
|
||||
}
|
||||
|
||||
double measure_pthread_create()
|
||||
{
|
||||
pthread_t tid;
|
||||
double t_start = get_time();
|
||||
for (int i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
pthread_create(&tid, NULL, dummy_thread, NULL);
|
||||
pthread_join(tid, NULL);
|
||||
}
|
||||
double t_end = get_time();
|
||||
return (t_end - t_start) / ITERATIONS;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Measuring fork() and pthread_create() overhead...\n");
|
||||
printf("Iterations per test: %d\n\n", ITERATIONS);
|
||||
|
||||
double avg_pthread_time = measure_pthread_create();
|
||||
printf("Average pthread_create() + pthread_join() time: %.6f ms\n",
|
||||
avg_pthread_time * 1000);
|
||||
|
||||
double avg_fork_time = measure_fork_create();
|
||||
printf("Average fork() + waitpid() time: %.6f ms\n",
|
||||
avg_fork_time * 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
exp1/task67
Executable file
BIN
exp1/task67
Executable file
Binary file not shown.
272
exp1/task67.c
Normal file
272
exp1/task67.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
任务7(任选):编写一个多线程并发应用程序task67.c。
|
||||
1)主线程预先创建5个工作线程,然后通过缓冲区发放任务,对等线程从缓冲区提取任务执行,当缓冲区为空时,等待任务。
|
||||
2)每个任务内容是以秒为单位的整数等待时间,对等线程取得任务后,调用sleep睡眠指定的秒数,就算任务执行完成。
|
||||
3)主线程从终端读取命令,发布任务,命令格式为"<任务数> <秒数>",如输入"10 5"表示创建10个任务,每个任务的运行时间是5秒。
|
||||
4)应用程序应支持动态地增加或减少工作线程的数目,一个策略是当缓冲区变满时,将线程数量翻倍,而当缓冲区变为空时,将线程数目减半。
|
||||
5)每次工作线程发送变动时,应输出相关信息。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#define DEFAULT_BUF_SIZE 20
|
||||
#define INITIAL_THREADS 5
|
||||
#define MAX_THREADS 256
|
||||
|
||||
typedef struct {
|
||||
int *buf;
|
||||
int n;
|
||||
int inpos;
|
||||
int outpos;
|
||||
sem_t mutex;
|
||||
sem_t slots;
|
||||
sem_t items;
|
||||
} sbuf_t;
|
||||
|
||||
/* 线程池管理 */
|
||||
int current_thread_count = INITIAL_THREADS;
|
||||
int thread_should_exit[MAX_THREADS];
|
||||
pthread_t worker_tids[MAX_THREADS];
|
||||
pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
volatile int program_running = 1;
|
||||
sbuf_t buf;
|
||||
|
||||
void sbuf_init(sbuf_t *sp, int n)
|
||||
{
|
||||
sp->buf = (int *)malloc(n * sizeof(int));
|
||||
sp->n = n;
|
||||
sp->inpos = 0;
|
||||
sp->outpos = 0;
|
||||
sem_init(&sp->mutex, 0, 1);
|
||||
sem_init(&sp->slots, 0, n);
|
||||
sem_init(&sp->items, 0, 0);
|
||||
}
|
||||
|
||||
void sbuf_deinit(sbuf_t *sp)
|
||||
{
|
||||
free(sp->buf);
|
||||
sem_destroy(&sp->mutex);
|
||||
sem_destroy(&sp->slots);
|
||||
sem_destroy(&sp->items);
|
||||
}
|
||||
|
||||
void sbuf_insert(sbuf_t *sp, int item)
|
||||
{
|
||||
sem_wait(&sp->slots);
|
||||
sem_wait(&sp->mutex);
|
||||
sp->buf[sp->inpos] = item;
|
||||
sp->inpos = (sp->inpos + 1) % sp->n;
|
||||
sem_post(&sp->mutex);
|
||||
sem_post(&sp->items);
|
||||
}
|
||||
|
||||
int sbuf_remove(sbuf_t *sp)
|
||||
{
|
||||
sem_wait(&sp->items);
|
||||
sem_wait(&sp->mutex);
|
||||
int item = sp->buf[sp->outpos];
|
||||
sp->outpos = (sp->outpos + 1) % sp->n;
|
||||
sem_post(&sp->mutex);
|
||||
sem_post(&sp->slots);
|
||||
return item;
|
||||
}
|
||||
|
||||
int sbuf_is_full(sbuf_t *sp)
|
||||
{
|
||||
int val;
|
||||
sem_getvalue(&sp->slots, &val);
|
||||
return val == 0;
|
||||
}
|
||||
|
||||
int sbuf_is_empty(sbuf_t *sp)
|
||||
{
|
||||
int val;
|
||||
sem_getvalue(&sp->items, &val);
|
||||
return val == 0;
|
||||
}
|
||||
|
||||
void *worker(void *arg)
|
||||
{
|
||||
int my_id = *((int *)arg);
|
||||
while (1)
|
||||
{
|
||||
/* 检查是否应该退出 */
|
||||
pthread_mutex_lock(&pool_mutex);
|
||||
if (thread_should_exit[my_id])
|
||||
{
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
|
||||
int task_seconds = sbuf_remove(&buf);
|
||||
|
||||
/* 再次检查退出标志(可能在等待期间被标记) */
|
||||
pthread_mutex_lock(&pool_mutex);
|
||||
if (thread_should_exit[my_id])
|
||||
{
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
/* 任务还在缓冲区中被取出了,需要重新放回去 */
|
||||
sbuf_insert(&buf, task_seconds);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
|
||||
printf("[Worker %d] executing task: sleep %d seconds\n", my_id, task_seconds);
|
||||
sleep(task_seconds);
|
||||
printf("[Worker %d] task completed\n", my_id);
|
||||
}
|
||||
printf("[Worker %d] exiting\n", my_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void adjust_threads_up()
|
||||
{
|
||||
pthread_mutex_lock(&pool_mutex);
|
||||
int new_count = current_thread_count * 2;
|
||||
if (new_count > MAX_THREADS) new_count = MAX_THREADS;
|
||||
if (new_count <= current_thread_count)
|
||||
{
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
int added = new_count - current_thread_count;
|
||||
for (int i = 0; i < added; i++)
|
||||
{
|
||||
int tid = current_thread_count + i;
|
||||
thread_should_exit[tid] = 0;
|
||||
int *arg = malloc(sizeof(int));
|
||||
*arg = tid;
|
||||
pthread_create(&worker_tids[tid], NULL, worker, arg);
|
||||
}
|
||||
current_thread_count = new_count;
|
||||
printf("缓冲区变满,工作线程数翻倍,当前工作线程数为%d个\n", current_thread_count);
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
}
|
||||
|
||||
void adjust_threads_down()
|
||||
{
|
||||
pthread_mutex_lock(&pool_mutex);
|
||||
int new_count = current_thread_count / 2;
|
||||
if (new_count < 1) new_count = 1;
|
||||
if (new_count >= current_thread_count)
|
||||
{
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
int removed = current_thread_count - new_count;
|
||||
/* 标记尾部线程为退出 */
|
||||
for (int i = 0; i < removed; i++)
|
||||
{
|
||||
int tid = current_thread_count - 1 - i;
|
||||
thread_should_exit[tid] = 1;
|
||||
}
|
||||
current_thread_count = new_count;
|
||||
printf("缓冲区变空,工作线程数减半,当前工作线程数为%d个\n", current_thread_count);
|
||||
|
||||
/* 插入额外的空槽通知以便标记线程能退出 */
|
||||
for (int i = 0; i < removed; i++)
|
||||
{
|
||||
sbuf_insert(&buf, 0);
|
||||
}
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int buf_size = DEFAULT_BUF_SIZE;
|
||||
if (argc >= 2)
|
||||
buf_size = atoi(argv[1]);
|
||||
|
||||
printf("Dynamic Thread Pool\n");
|
||||
printf("Buffer size: %d, Initial threads: %d\n", buf_size, INITIAL_THREADS);
|
||||
printf("Commands: <task_count> <seconds> | 'quit' to exit\n\n");
|
||||
|
||||
sbuf_init(&buf, buf_size);
|
||||
|
||||
/* 创建初始线程 */
|
||||
for (int i = 0; i < INITIAL_THREADS; i++)
|
||||
{
|
||||
thread_should_exit[i] = 0;
|
||||
int *arg = malloc(sizeof(int));
|
||||
*arg = i;
|
||||
pthread_create(&worker_tids[i], NULL, worker, arg);
|
||||
}
|
||||
|
||||
/* 主线程命令循环 */
|
||||
char line[256];
|
||||
while (program_running)
|
||||
{
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
if (fgets(line, sizeof(line), stdin) == NULL)
|
||||
break;
|
||||
|
||||
if (strncmp(line, "quit", 4) == 0)
|
||||
break;
|
||||
|
||||
int task_count, task_seconds;
|
||||
if (sscanf(line, "%d %d", &task_count, &task_seconds) != 2)
|
||||
{
|
||||
printf("Invalid command. Usage: <task_count> <seconds>\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Adding %d tasks, each %d seconds...\n", task_count, task_seconds);
|
||||
|
||||
for (int i = 0; i < task_count; i++)
|
||||
{
|
||||
/* 插入前检查是否满 */
|
||||
if (sbuf_is_full(&buf))
|
||||
{
|
||||
adjust_threads_up();
|
||||
}
|
||||
sbuf_insert(&buf, task_seconds);
|
||||
}
|
||||
|
||||
/* 检查插入后是否需要调整 */
|
||||
int slots_val, items_val;
|
||||
sem_getvalue(&buf.slots, &slots_val);
|
||||
sem_getvalue(&buf.items, &items_val);
|
||||
printf("Buffer status: %d/%d slots free, %d items pending\n",
|
||||
slots_val, buf_size, items_val);
|
||||
}
|
||||
|
||||
/* 等待所有任务完成并通知线程退出 */
|
||||
printf("\nWaiting for pending tasks to complete...\n");
|
||||
while (!sbuf_is_empty(&buf))
|
||||
{
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
/* 标记所有线程退出并唤醒它们 */
|
||||
pthread_mutex_lock(&pool_mutex);
|
||||
for (int i = 0; i < current_thread_count; i++)
|
||||
{
|
||||
thread_should_exit[i] = 1;
|
||||
}
|
||||
/* 插入足够多的信号量以唤醒所有等待的线程 */
|
||||
for (int i = 0; i < current_thread_count; i++)
|
||||
{
|
||||
sbuf_insert(&buf, 0);
|
||||
}
|
||||
pthread_mutex_unlock(&pool_mutex);
|
||||
|
||||
/* 等待所有线程结束 */
|
||||
for (int i = 0; i < current_thread_count; i++)
|
||||
{
|
||||
pthread_join(worker_tids[i], NULL);
|
||||
}
|
||||
|
||||
printf("All workers exited. Program terminating.\n");
|
||||
sbuf_deinit(&buf);
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
server-exp/实验1任务报告web服务器初步实现_填写版.docx:Zone.Identifier
Normal file
2
server-exp/实验1任务报告web服务器初步实现_填写版.docx:Zone.Identifier
Normal file
@@ -0,0 +1,2 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
@@ -1,4 +0,0 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
ReferrerUrl=https://lms.dgut.edu.cn/
|
||||
HostUrl=https://uobs.dgut.edu.cn/view/resources/web/17452239027185951.docx?attname=%E5%AE%9E%E9%AA%8C1%E4%BB%BB%E5%8A%A1%E6%8A%A5%E5%91%8A%EF%BC%88web%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%88%9D%E6%AD%A5%E5%AE%9E%E7%8E%B0%EF%BC%89(1).docx
|
||||
Binary file not shown.
@@ -16,6 +16,7 @@ void Signal(int sig, sighandler_t handler){
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
}
|
||||
|
||||
//工作函数
|
||||
void toggle(int conn_sock,int hit)
|
||||
{
|
||||
size_t n; int i,no=0;
|
||||
@@ -63,20 +64,20 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
for(hit=1; ; hit++) {
|
||||
conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
|
||||
conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
|
||||
|
||||
/* determine the domain name and IP address of the client */
|
||||
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
|
||||
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
|
||||
haddrp = inet_ntoa(clientaddr.sin_addr);
|
||||
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
|
||||
/* determine the domain name and IP address of the client */
|
||||
hp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
|
||||
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
|
||||
haddrp = inet_ntoa(clientaddr.sin_addr);
|
||||
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
|
||||
|
||||
if (fork() == 0) {
|
||||
close(listen_sock); /* Child process closes its listening socket */
|
||||
toggle(conn_sock,hit); /* Child process services client */
|
||||
close(conn_sock); /* Child process closes connection with client */
|
||||
exit(0); /* Child process exits */
|
||||
}
|
||||
close(conn_sock); /* Parent closes connected socket (important!) */
|
||||
if (fork() == 0) {
|
||||
close(listen_sock); /* Child process closes its listening socket */
|
||||
toggle(conn_sock,hit); /* Child process services client */
|
||||
close(conn_sock); /* Child process closes connection with client */
|
||||
exit(0); /* Child process exits */
|
||||
}
|
||||
close(conn_sock); /* Parent closes connected socket (important!) */
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -63,6 +63,7 @@ void * serve_client (void *vargp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//工作函数
|
||||
void toggle(int conn_sock,int hit)
|
||||
{
|
||||
size_t n; int i,no=0;
|
||||
|
||||
Binary file not shown.
BIN
server-exp2/webletsp
Executable file
BIN
server-exp2/webletsp
Executable file
Binary file not shown.
290
server-exp2/webletsp.c
Normal file
290
server-exp2/webletsp.c
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "common.h"
|
||||
|
||||
void process_trans(int fd,int hit);
|
||||
void read_requesthdrs(rio_t *rp,int hit,int nrh);
|
||||
int is_static(char *uri);
|
||||
void parse_static_uri(char *uri, char *filename);
|
||||
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs);
|
||||
void feed_static(int fd, char *filename, int filesize);
|
||||
void get_filetype(char *filename, char *filetype);
|
||||
void feed_dynamic(int fd, char *filename, char *cgiargs);
|
||||
void error_request(int fd, char *cause, char *errnum,
|
||||
char *shortmsg, char *description);
|
||||
|
||||
/* 支持的文件扩展名及其对应的MIME类型 */
|
||||
struct {
|
||||
char *ext; // 文件扩展名
|
||||
char *filetype; // MIME类型
|
||||
} extensions[] = {
|
||||
{".gif", "image/gif"},
|
||||
{".jpg", "image/jpg"},
|
||||
{".jpeg", "image/jpeg"},
|
||||
{".png", "image/png"},
|
||||
{".ico", "image/ico"},
|
||||
{".zip", "image/zip"},
|
||||
{".gz", "image/gz"},
|
||||
{".tar", "image/tar"},
|
||||
{".htm", "text/html"},
|
||||
{".html", "text/html"},
|
||||
{0, 0} // 结束标志
|
||||
};
|
||||
|
||||
/* 调试模式宏定义 */
|
||||
#ifdef DEBUG
|
||||
#define printf2(format, var) printf(format,var)
|
||||
#define printf3(format,var1,var2) printf(format,var1,var2)
|
||||
#else
|
||||
#define printf2(format, var)
|
||||
#define printf3(format,var1,var2)
|
||||
#endif
|
||||
|
||||
/* 全局变量说明 */
|
||||
|
||||
void sigchld_handler(int sig)
|
||||
{
|
||||
while (waitpid(-1, 0, WNOHANG) > 0);
|
||||
return;
|
||||
}
|
||||
|
||||
typedef void (*sighandler_t) (int sig) ;
|
||||
|
||||
void Signal(int sig, sighandler_t handler){
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = sigchld_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART; // 确保被信号中断的系统调用自动重启
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Ctrl+C信号处理函数 */
|
||||
void ctrlc_handler(int sig)
|
||||
{
|
||||
printf("您按下了Ctrl+C终止了Web服务器\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* 主函数:监听端口并处理客户端连接 */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int listen_sock, conn_sock, port;
|
||||
int hit; // 请求计数器
|
||||
socklen_t clientlen;
|
||||
struct sockaddr_in clientaddr;
|
||||
|
||||
/* 检查命令行参数 */
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "用法: %s <端口号>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
port = atoi(argv[1]);
|
||||
|
||||
listen_sock = open_listen_sock(port);
|
||||
|
||||
for (hit=1; ; hit++) { //hit为第几次请求,或第几次http事务
|
||||
clientlen = sizeof(clientaddr);
|
||||
conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
|
||||
|
||||
|
||||
if (fork() == 0) {
|
||||
close(listen_sock); /* Child process closes its listening socket */
|
||||
process_trans(conn_sock,hit); // 处理HTTP事务
|
||||
close(conn_sock); /* Child process closes connection with client */
|
||||
exit(0); /* Child process exits */
|
||||
}
|
||||
|
||||
close(conn_sock);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* 处理HTTP事务的核心函数 */
|
||||
void process_trans(int fd,int hit)
|
||||
{
|
||||
int static_flag; // 是否为静态资源标志
|
||||
struct stat sbuf; // 文件状态信息
|
||||
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
|
||||
char filename[MAXLINE], cgiargs[MAXLINE];
|
||||
rio_t rio;
|
||||
int rhn=0; // 请求头计数器
|
||||
|
||||
/* 设置Ctrl+C信号处理 */
|
||||
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
|
||||
(void)printf("错误: 无法设置SIGINT信号处理\n");
|
||||
|
||||
printf2("第%d次请求开始\n",hit);
|
||||
|
||||
/* 读取请求行和请求头 */
|
||||
rio_readinitb(&rio, fd);
|
||||
rio_readlineb(&rio, buf, MAXLINE);
|
||||
printf3("请求头%d:%s", ++rhn, buf);
|
||||
|
||||
sscanf(buf, "%s %s %s", method, uri, version);
|
||||
if (strcasecmp(method, "GET")) { // 仅支持GET方法
|
||||
error_request(fd, method, "501", "未实现",
|
||||
"本服务器不支持该请求方法");
|
||||
return;
|
||||
}
|
||||
read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头
|
||||
|
||||
static_flag = is_static(uri); // 判断资源类型
|
||||
if(static_flag)
|
||||
parse_static_uri(uri, filename); // 解析静态资源路径
|
||||
else
|
||||
parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数
|
||||
|
||||
/* 检查文件是否存在及权限 */
|
||||
if (stat(filename, &sbuf) < 0) {
|
||||
error_request(fd, filename, "404", "未找到",
|
||||
"服务器无法找到该文件");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 根据资源类型发送响应 */
|
||||
if (static_flag) { // 静态资源处理
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
|
||||
error_request(fd, filename, "403", "禁止访问",
|
||||
"服务器无权读取该文件");
|
||||
return;
|
||||
}
|
||||
feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容
|
||||
}
|
||||
else { // 动态资源处理
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
|
||||
error_request(fd, filename, "403", "禁止访问",
|
||||
"服务器无法执行该CGI程序");
|
||||
return;
|
||||
}
|
||||
feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出
|
||||
}
|
||||
}
|
||||
|
||||
/* 判断URI是否对应静态资源 */
|
||||
int is_static(char *uri)
|
||||
{
|
||||
return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源
|
||||
}
|
||||
|
||||
/* 构造HTTP错误响应 */
|
||||
void error_request(int fd, char *cause, char *errnum,
|
||||
char *shortmsg, char *description)
|
||||
{
|
||||
char buf[MAXLINE], body[MAXBUF];
|
||||
|
||||
/* 构建错误页面内容 */
|
||||
sprintf(body, "<html><title>错误请求</title>");
|
||||
sprintf(body, "%s<body bgcolor=\"#ffffff\">\r\n", body);
|
||||
sprintf(body, "%s<h1>%s %s</h1>\r\n", body, errnum, shortmsg);
|
||||
sprintf(body, "%s<p>%s: %s</p>\r\n", body, description, cause);
|
||||
sprintf(body, "%s<hr><em>weblet Web服务器</em>\r\n", body);
|
||||
|
||||
/* 发送HTTP响应头 */
|
||||
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Content-type: text/html\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 发送错误页面内容 */
|
||||
rio_writen(fd, body, strlen(body));
|
||||
}
|
||||
|
||||
/* 读取请求头直到空行 */
|
||||
void read_requesthdrs(rio_t *rp,int hit, int rhn)
|
||||
{
|
||||
char buf[MAXLINE];
|
||||
rio_readlineb(rp, buf, MAXLINE);
|
||||
while(strcmp(buf, "\r\n")) {
|
||||
printf3("请求头%d:%s", ++rhn, buf);
|
||||
rio_readlineb(rp, buf, MAXLINE);
|
||||
}
|
||||
printf2("第%d次请求结束\n\n",hit);
|
||||
}
|
||||
|
||||
/* 解析静态资源URI到文件路径 */
|
||||
void parse_static_uri(char *uri, char *filename)
|
||||
{
|
||||
strcpy(filename, ".");
|
||||
strcat(filename, uri);
|
||||
if (uri[strlen(uri)-1] == '/')
|
||||
strcat(filename, "index.html"); // 默认返回index.html
|
||||
}
|
||||
|
||||
/* 解析动态资源URI到文件路径和参数 */
|
||||
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs)
|
||||
{
|
||||
char *ptr = index(uri, '?');
|
||||
if (ptr) {
|
||||
strcpy(cgiargs, ptr+1); // 提取参数部分请求头
|
||||
*ptr = '\0'; // 截断URI
|
||||
} else {
|
||||
strcpy(cgiargs, "");
|
||||
}
|
||||
strcpy(filename, ".");
|
||||
strcat(filename, uri);
|
||||
}
|
||||
|
||||
/* 发送静态文件内容 */
|
||||
void feed_static(int fd, char *filename, int filesize)
|
||||
{
|
||||
int srcfd;
|
||||
char *srcp, filetype[MAXLINE], buf[MAXBUF];
|
||||
|
||||
/* 发送HTTP响应头 */
|
||||
get_filetype(filename, filetype);
|
||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
||||
sprintf(buf, "%sServer: weblet Web Server\r\n", buf);
|
||||
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
|
||||
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 发送文件内容 */
|
||||
srcfd = open(filename, O_RDONLY, 0);
|
||||
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
|
||||
close(srcfd);
|
||||
rio_writen(fd, srcp, filesize);
|
||||
munmap(srcp, filesize);
|
||||
}
|
||||
|
||||
/* 根据文件扩展名获取MIME类型 */
|
||||
void get_filetype(char *filename, char *filetype)
|
||||
{
|
||||
int i, len;
|
||||
strcpy(filetype, "text/html"); // 默认类型
|
||||
for (i = 0; extensions[i].ext != 0; i++) {
|
||||
len = strlen(extensions[i].ext);
|
||||
if (!strcmp(&filename[strlen(filename)-len], extensions[i].ext)) {
|
||||
strcpy(filetype, extensions[i].filetype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 启动子进程执行CGI程序 */
|
||||
void feed_dynamic(int fd, char *filename, char *cgiargs)
|
||||
{
|
||||
char buf[MAXLINE], *emptylist[] = { NULL };
|
||||
int pfd[2];
|
||||
|
||||
/* 构造HTTP响应头 */
|
||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Server: weblet Web Server\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 创建管道并启动子进程 */
|
||||
(void)pipe(pfd);
|
||||
if (fork() == 0) { // 子进程
|
||||
close(pfd[1]); // 关闭写端
|
||||
dup2(pfd[0], STDIN_FILENO); // 重定向标准输入
|
||||
dup2(fd, STDOUT_FILENO); // 重定向标准输出到客户端
|
||||
execve(filename, emptylist, environ); // 执行CGI程序
|
||||
}
|
||||
|
||||
close(pfd[0]); // 父进程关闭读端
|
||||
(void)write(pfd[1], cgiargs, strlen(cgiargs)+1); // 传递参数
|
||||
wait(NULL); // 等待子进程结束
|
||||
close(pfd[1]); // 关闭写端
|
||||
}
|
||||
BIN
server-exp2/webletst
Executable file
BIN
server-exp2/webletst
Executable file
Binary file not shown.
292
server-exp2/webletst.c
Normal file
292
server-exp2/webletst.c
Normal file
@@ -0,0 +1,292 @@
|
||||
#include "common.h"
|
||||
|
||||
void process_trans(int fd,int hit);
|
||||
void read_requesthdrs(rio_t *rp,int hit,int nrh);
|
||||
int is_static(char *uri);
|
||||
void parse_static_uri(char *uri, char *filename);
|
||||
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs);
|
||||
void feed_static(int fd, char *filename, int filesize);
|
||||
void get_filetype(char *filename, char *filetype);
|
||||
void feed_dynamic(int fd, char *filename, char *cgiargs);
|
||||
void error_request(int fd, char *cause, char *errnum,
|
||||
char *shortmsg, char *description);
|
||||
|
||||
/* 支持的文件扩展名及其对应的MIME类型 */
|
||||
struct {
|
||||
char *ext; // 文件扩展名
|
||||
char *filetype; // MIME类型
|
||||
} extensions[] = {
|
||||
{".gif", "image/gif"},
|
||||
{".jpg", "image/jpg"},
|
||||
{".jpeg", "image/jpeg"},
|
||||
{".png", "image/png"},
|
||||
{".ico", "image/ico"},
|
||||
{".zip", "image/zip"},
|
||||
{".gz", "image/gz"},
|
||||
{".tar", "image/tar"},
|
||||
{".htm", "text/html"},
|
||||
{".html", "text/html"},
|
||||
{0, 0} // 结束标志
|
||||
};
|
||||
|
||||
/* 调试模式宏定义 */
|
||||
#ifdef DEBUG
|
||||
#define printf2(format, var) printf(format,var)
|
||||
#define printf3(format,var1,var2) printf(format,var1,var2)
|
||||
#else
|
||||
#define printf2(format, var)
|
||||
#define printf3(format,var1,var2)
|
||||
#endif
|
||||
|
||||
/* 全局变量说明 */
|
||||
void process_trans(int fd,int hit);
|
||||
void *serve_client(void *vargp);
|
||||
|
||||
typedef struct _client_data_t {
|
||||
int conn_sock; //客户连接socket
|
||||
int hit; //第几个客户
|
||||
} client_data_t;
|
||||
|
||||
|
||||
/* Ctrl+C信号处理函数 */
|
||||
void ctrlc_handler(int sig)
|
||||
{
|
||||
printf("您按下了Ctrl+C终止了Web服务器\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* 主函数:监听端口并处理客户端连接 */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int listen_sock, conn_sock, port;
|
||||
struct sockaddr_in clientaddr;
|
||||
int hit; // 请求计数器
|
||||
socklen_t clientlen;
|
||||
client_data_t *cdp;
|
||||
pthread_t tid;
|
||||
|
||||
/* 检查命令行参数 */
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "用法: %s <端口号>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
port = atoi(argv[1]);
|
||||
|
||||
listen_sock = open_listen_sock(port);
|
||||
|
||||
for (hit=1; ; hit++) { //hit为第几次请求,或第几次http事务
|
||||
clientlen = sizeof(clientaddr);
|
||||
cdp = malloc(sizeof(client_data_t));
|
||||
cdp->conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen);
|
||||
cdp->hit=hit;
|
||||
|
||||
pthread_create(&tid, NULL, serve_client, (void *)cdp);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* thread routine */
|
||||
void * serve_client (void *vargp)
|
||||
{
|
||||
int hit,conn_sock;
|
||||
client_data_t cd;
|
||||
cd = *(client_data_t *)vargp;
|
||||
|
||||
pthread_detach(pthread_self());
|
||||
free(vargp);
|
||||
process_trans(cd.conn_sock,cd.hit); // 处理HTTP事务
|
||||
|
||||
close(conn_sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 处理HTTP事务的核心函数 */
|
||||
void process_trans(int fd,int hit)
|
||||
{
|
||||
int static_flag; // 是否为静态资源标志
|
||||
struct stat sbuf; // 文件状态信息
|
||||
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
|
||||
char filename[MAXLINE], cgiargs[MAXLINE];
|
||||
rio_t rio;
|
||||
int rhn=0; // 请求头计数器
|
||||
|
||||
/* 设置Ctrl+C信号处理 */
|
||||
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
|
||||
(void)printf("错误: 无法设置SIGINT信号处理\n");
|
||||
|
||||
printf2("第%d次请求开始\n",hit);
|
||||
|
||||
/* 读取请求行和请求头 */
|
||||
rio_readinitb(&rio, fd);
|
||||
rio_readlineb(&rio, buf, MAXLINE);
|
||||
printf3("请求头%d:%s", ++rhn, buf);
|
||||
|
||||
sscanf(buf, "%s %s %s", method, uri, version);
|
||||
if (strcasecmp(method, "GET")) { // 仅支持GET方法
|
||||
error_request(fd, method, "501", "未实现",
|
||||
"本服务器不支持该请求方法");
|
||||
return;
|
||||
}
|
||||
read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头
|
||||
|
||||
static_flag = is_static(uri); // 判断资源类型
|
||||
if(static_flag)
|
||||
parse_static_uri(uri, filename); // 解析静态资源路径
|
||||
else
|
||||
parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数
|
||||
|
||||
/* 检查文件是否存在及权限 */
|
||||
if (stat(filename, &sbuf) < 0) {
|
||||
error_request(fd, filename, "404", "未找到",
|
||||
"服务器无法找到该文件");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 根据资源类型发送响应 */
|
||||
if (static_flag) { // 静态资源处理
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
|
||||
error_request(fd, filename, "403", "禁止访问",
|
||||
"服务器无权读取该文件");
|
||||
return;
|
||||
}
|
||||
feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容
|
||||
}
|
||||
else { // 动态资源处理
|
||||
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
|
||||
error_request(fd, filename, "403", "禁止访问",
|
||||
"服务器无法执行该CGI程序");
|
||||
return;
|
||||
}
|
||||
feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出
|
||||
}
|
||||
}
|
||||
|
||||
/* 判断URI是否对应静态资源 */
|
||||
int is_static(char *uri)
|
||||
{
|
||||
return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源
|
||||
}
|
||||
|
||||
/* 构造HTTP错误响应 */
|
||||
void error_request(int fd, char *cause, char *errnum,
|
||||
char *shortmsg, char *description)
|
||||
{
|
||||
char buf[MAXLINE], body[MAXBUF];
|
||||
|
||||
/* 构建错误页面内容 */
|
||||
sprintf(body, "<html><title>错误请求</title>");
|
||||
sprintf(body, "%s<body bgcolor=\"#ffffff\">\r\n", body);
|
||||
sprintf(body, "%s<h1>%s %s</h1>\r\n", body, errnum, shortmsg);
|
||||
sprintf(body, "%s<p>%s: %s</p>\r\n", body, description, cause);
|
||||
sprintf(body, "%s<hr><em>weblet Web服务器</em>\r\n", body);
|
||||
|
||||
/* 发送HTTP响应头 */
|
||||
sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Content-type: text/html\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 发送错误页面内容 */
|
||||
rio_writen(fd, body, strlen(body));
|
||||
}
|
||||
|
||||
/* 读取请求头直到空行 */
|
||||
void read_requesthdrs(rio_t *rp,int hit, int rhn)
|
||||
{
|
||||
char buf[MAXLINE];
|
||||
rio_readlineb(rp, buf, MAXLINE);
|
||||
while(strcmp(buf, "\r\n")) {
|
||||
printf3("请求头%d:%s", ++rhn, buf);
|
||||
rio_readlineb(rp, buf, MAXLINE);
|
||||
}
|
||||
printf2("第%d次请求结束\n\n",hit);
|
||||
}
|
||||
|
||||
/* 解析静态资源URI到文件路径 */
|
||||
void parse_static_uri(char *uri, char *filename)
|
||||
{
|
||||
strcpy(filename, ".");
|
||||
strcat(filename, uri);
|
||||
if (uri[strlen(uri)-1] == '/')
|
||||
strcat(filename, "index.html"); // 默认返回index.html
|
||||
}
|
||||
|
||||
/* 解析动态资源URI到文件路径和参数 */
|
||||
void parse_dynamic_uri(char *uri, char *filename, char *cgiargs)
|
||||
{
|
||||
char *ptr = index(uri, '?');
|
||||
if (ptr) {
|
||||
strcpy(cgiargs, ptr+1); // 提取参数部分请求头
|
||||
*ptr = '\0'; // 截断URI
|
||||
} else {
|
||||
strcpy(cgiargs, "");
|
||||
}
|
||||
strcpy(filename, ".");
|
||||
strcat(filename, uri);
|
||||
}
|
||||
|
||||
/* 发送静态文件内容 */
|
||||
void feed_static(int fd, char *filename, int filesize)
|
||||
{
|
||||
int srcfd;
|
||||
char *srcp, filetype[MAXLINE], buf[MAXBUF];
|
||||
|
||||
/* 发送HTTP响应头 */
|
||||
get_filetype(filename, filetype);
|
||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
||||
sprintf(buf, "%sServer: weblet Web Server\r\n", buf);
|
||||
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
|
||||
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 发送文件内容 */
|
||||
srcfd = open(filename, O_RDONLY, 0);
|
||||
srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
|
||||
close(srcfd);
|
||||
rio_writen(fd, srcp, filesize);
|
||||
munmap(srcp, filesize);
|
||||
}
|
||||
|
||||
/* 根据文件扩展名获取MIME类型 */
|
||||
void get_filetype(char *filename, char *filetype)
|
||||
{
|
||||
int i, len;
|
||||
strcpy(filetype, "text/html"); // 默认类型
|
||||
for (i = 0; extensions[i].ext != 0; i++) {
|
||||
len = strlen(extensions[i].ext);
|
||||
if (!strcmp(&filename[strlen(filename)-len], extensions[i].ext)) {
|
||||
strcpy(filetype, extensions[i].filetype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 启动子进程执行CGI程序 */
|
||||
void feed_dynamic(int fd, char *filename, char *cgiargs)
|
||||
{
|
||||
char buf[MAXLINE], *emptylist[] = { NULL };
|
||||
int pfd[2];
|
||||
|
||||
/* 构造HTTP响应头 */
|
||||
sprintf(buf, "HTTP/1.0 200 OK\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
sprintf(buf, "Server: weblet Web Server\r\n");
|
||||
rio_writen(fd, buf, strlen(buf));
|
||||
|
||||
/* 创建管道并启动子进程 */
|
||||
(void)pipe(pfd);
|
||||
if (fork() == 0) { // 子进程
|
||||
close(pfd[1]); // 关闭写端
|
||||
dup2(pfd[0], STDIN_FILENO); // 重定向标准输入
|
||||
dup2(fd, STDOUT_FILENO); // 重定向标准输出到客户端
|
||||
execve(filename, emptylist, environ); // 执行CGI程序
|
||||
}
|
||||
|
||||
close(pfd[0]); // 父进程关闭读端
|
||||
(void)write(pfd[1], cgiargs, strlen(cgiargs)+1); // 传递参数
|
||||
wait(NULL); // 等待子进程结束
|
||||
close(pfd[1]); // 关闭写端
|
||||
}
|
||||
BIN
server-exp2/实验2任务报告(多进程和多线程模型)(1).docx
Normal file
BIN
server-exp2/实验2任务报告(多进程和多线程模型)(1).docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user