This commit is contained in:
2026-06-09 06:43:13 +02:00
parent 43902c20e4
commit 2f42b036a9
33 changed files with 2077 additions and 40 deletions

Binary file not shown.

Binary file not shown.

View 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);
});

View 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

Binary file not shown.

163
exp1/matmult.c Normal file
View 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

Binary file not shown.

View File

@@ -7,7 +7,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <pthread.h> #include <pthread.h>
#include <semaphore.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 #define MAXTHREADS 16
@@ -32,6 +39,7 @@ int main(int argc, char **argv)
nelems = (1LL << log_nelems); nelems = (1LL << log_nelems);
nelems_per_thread = nelems / nthreads; 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; myid[i] = i;
@@ -50,12 +58,14 @@ int main(int argc, char **argv)
if (result == (nelems*(nelems-1))/2) if (result == (nelems*(nelems-1))/2)
{ {
printf("Correct result: %ld\n",result); printf("Correct result: %llu\n",result);
} }
else 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); exit(0);
} }

Binary file not shown.

View File

@@ -41,6 +41,7 @@ void *wokerT3(void *vargp)
int main() int main()
{ {
pthread_t t1,t2,t3; pthread_t t1,t2,t3;
srand(time(NULL));
pthread_create(&t1, NULL, wokerT1, NULL); pthread_create(&t1, NULL, wokerT1, NULL);
pthread_create(&t2, NULL, wokerT2, NULL); pthread_create(&t2, NULL, wokerT2, NULL);
pthread_create(&t3, NULL, wokerT3, NULL); pthread_create(&t3, NULL, wokerT3, NULL);

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
/*任务4(必做编译、测试和运行示例程序psum64.c /*
任务4(必做编译、测试和运行示例程序psum64.c
1测量线程数为1、2、4、8、16时程序的执行时间计算加速比和效率并做出解释。 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> #include <stdio.h>
@@ -19,19 +20,18 @@ double get_time()
return tv.tv_sec + tv.tv_usec / 1000000.0; return tv.tv_sec + tv.tv_usec / 1000000.0;
} }
unsigned long long psum64[MAXTHREADS]; unsigned long long psum64[MAXTHREADS];
unsigned long long nelems_per_thread; unsigned long long nelems_per_thread;
int main(int argc, char **argv) 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]; pthread_t tid[MAXTHREADS];
int myid[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); exit(0);
} }
nthreads = atoi(argv[1]); nthreads = atoi(argv[1]);
@@ -40,29 +40,30 @@ int main(int argc, char **argv)
nelems_per_thread = nelems / nthreads; nelems_per_thread = nelems / nthreads;
double t_start = get_time(); double t_start = get_time();
for ( i = 0; i < nthreads; i++) for (i = 0; i < nthreads; i++)
{ {
myid[i] = 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]; 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 else
{ {
printf("Wrong result: %llu\n",result); printf("Wrong result: %llu\n", result);
} }
double t_end = get_time(); double t_end = get_time();
printf("Time taken: %f seconds\n", t_end - t_start); printf("Time taken: %f seconds\n", t_end - t_start);
@@ -70,14 +71,12 @@ int main(int argc, char **argv)
exit(0); exit(0);
} }
void *sum(void *vargp) void *sum(void *vargp)
{ {
int myid = *((int *)vargp); int myid = *((int *)vargp);
unsigned long long begin = myid * nelems_per_thread; unsigned long long begin = myid * nelems_per_thread;
unsigned long long end = begin + 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++) for (i = begin; i < end; i++)
{ {

BIN
exp1/task66 Executable file

Binary file not shown.

80
exp1/task66.c Normal file
View 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

Binary file not shown.

272
exp1/task67.c Normal file
View 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.

View File

@@ -0,0 +1,2 @@
[ZoneTransfer]
ZoneId=3

View File

@@ -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.

View File

@@ -16,6 +16,7 @@ void Signal(int sig, sighandler_t handler){
sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCHLD, &sa, NULL);
} }
//工作函数
void toggle(int conn_sock,int hit) void toggle(int conn_sock,int hit)
{ {
size_t n; int i,no=0; size_t n; int i,no=0;

Binary file not shown.

View File

@@ -63,6 +63,7 @@ void * serve_client (void *vargp)
return NULL; return NULL;
} }
//工作函数
void toggle(int conn_sock,int hit) void toggle(int conn_sock,int hit)
{ {
size_t n; int i,no=0; size_t n; int i,no=0;

Binary file not shown.

BIN
server-exp2/webletsp Executable file

Binary file not shown.

290
server-exp2/webletsp.c Normal file
View 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

Binary file not shown.

292
server-exp2/webletst.c Normal file
View 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]); // 关闭写端
}