Compare commits

...

7 Commits

Author SHA1 Message Date
25f7ef1967 4 2026-06-18 18:13:12 +08:00
2f42b036a9 3 2026-06-09 06:43:13 +02:00
43902c20e4 2 2026-05-28 08:48:01 +08:00
1132d9e9a7 完成并修改正确必做题 2026-05-16 21:52:40 +08:00
119f0e7b3a aiwork 2026-05-16 12:08:49 +08:00
f679b67969 添加作业文档 2026-05-15 21:44:55 +08:00
56a4233098 添加以前的作业 2026-05-15 21:25:01 +08:00
171 changed files with 92227 additions and 30 deletions

Binary file not shown.

BIN
AI-work/matmult Executable file

Binary file not shown.

151
AI-work/matmult.c Normal file
View File

@@ -0,0 +1,151 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#define MAX_THREADS 64
int N;
double **A, **B, **C_parallel, **C_serial;
int nthreads;
int rows_per_thread;
double **allocate_matrix(int n)
{
double **mat = (double **)malloc(n * sizeof(double *));
for (int i = 0; i < n; i++) {
mat[i] = (double *)malloc(n * sizeof(double));
}
return mat;
}
void free_matrix(double **mat, int n)
{
for (int i = 0; i < n; i++) free(mat[i]);
free(mat);
}
void init_matrix(double **mat, int n)
{
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
mat[i][j] = (double)(rand() % 100) / 10.0;
}
void *multiply_thread(void *arg)
{
int start_row = *(int *)arg;
int end_row = start_row + rows_per_thread;
if (end_row > N) end_row = N;
for (int i = start_row; i < end_row; i++) {
for (int j = 0; j < N; j++) {
double sum = 0.0;
for (int k = 0; k < N; k++) {
sum += A[i][k] * B[k][j];
}
C_parallel[i][j] = sum;
}
}
return NULL;
}
void serial_multiply()
{
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][k] * B[k][j];
}
C_serial[i][j] = sum;
}
}
}
double get_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
int verify()
{
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (C_parallel[i][j] - C_serial[i][j] > 0.001 ||
C_serial[i][j] - C_parallel[i][j] > 0.001) {
printf("Mismatch at [%d][%d]: parallel=%.6f, serial=%.6f\n",
i, j, C_parallel[i][j], C_serial[i][j]);
return 0;
}
}
}
return 1;
}
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <N> <num_threads>\n", argv[0]);
return 1;
}
N = atoi(argv[1]);
nthreads = atoi(argv[2]);
if (nthreads > MAX_THREADS) nthreads = MAX_THREADS;
if (nthreads > N) nthreads = N;
rows_per_thread = N / nthreads;
srand(42);
A = allocate_matrix(N);
B = allocate_matrix(N);
C_parallel = allocate_matrix(N);
C_serial = allocate_matrix(N);
init_matrix(A, N);
init_matrix(B, N);
pthread_t threads[MAX_THREADS];
int starts[MAX_THREADS];
double t_start = get_time();
for (int i = 0; i < nthreads; i++) {
starts[i] = i * rows_per_thread;
pthread_create(&threads[i], NULL, multiply_thread, &starts[i]);
}
for (int i = 0; i < nthreads; i++) {
pthread_join(threads[i], NULL);
}
double t_end = get_time();
double t_parallel = t_end - t_start;
t_start = get_time();
serial_multiply();
t_end = get_time();
double t_serial = t_end - t_start;
printf("Matrix size: %d x %d, Threads: %d\n", N, N, nthreads);
printf("Parallel time: %.6f s\n", t_parallel);
printf("Serial time: %.6f s\n", t_serial);
printf("Speedup: %.4f\n", t_serial / t_parallel);
printf("Efficiency: %.4f\n", t_serial / t_parallel / nthreads);
if (verify()) {
printf("Verification: SUCCESS\n");
} else {
printf("Verification: FAILED\n");
}
free_matrix(A, N);
free_matrix(B, N);
free_matrix(C_parallel, N);
free_matrix(C_serial, N);
return 0;
}

BIN
AI-work/task61 Executable file

Binary file not shown.

50
AI-work/task61.c Normal file
View File

@@ -0,0 +1,50 @@
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *workerT1(void *vargp)
{
for (int i = 0; i < 5; i++) {
printf("My name is Lvjinzhong\n");
int sleep_time = rand() % 5 + 1;
sleep(sleep_time);
}
return NULL;
}
void *workerT2(void *vargp)
{
for (int i = 0; i < 5; i++) {
printf("My student number is 2024414290124\n");
int sleep_time = rand() % 5 + 1;
sleep(sleep_time);
}
return NULL;
}
void *workerT3(void *vargp)
{
for (int i = 0; i < 5; i++) {
time_t t = time(NULL);
printf("Current time %s", ctime(&t));
int sleep_time = rand() % 5 + 1;
sleep(sleep_time);
}
return NULL;
}
int main()
{
srand(time(NULL));
pthread_t t1, t2, t3;
pthread_create(&t1, NULL, workerT1, NULL);
pthread_create(&t2, NULL, workerT2, NULL);
pthread_create(&t3, NULL, workerT3, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}

BIN
AI-work/task62 Executable file

Binary file not shown.

46
AI-work/task62.c Normal file
View File

@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
volatile long long counter = 0;
long long niters;
sem_t mutex;
void *thread_func(void *arg)
{
for (long long i = 0; i < niters; i++) {
sem_wait(&mutex);
counter++;
sem_post(&mutex);
}
return NULL;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <niters>\n", argv[0]);
return 1;
}
niters = atoll(argv[1]);
sem_init(&mutex, 0, 1);
pthread_t t1, t2;
pthread_create(&t1, NULL, thread_func, NULL);
pthread_create(&t2, NULL, thread_func, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("Expected: %lld, Got: %lld\n", 2 * niters, counter);
if (counter != 2 * niters) {
printf("ERROR: Race condition detected!\n");
} else {
printf("Correct! No race condition.\n");
}
sem_destroy(&mutex);
return 0;
}

BIN
AI-work/task63 Executable file

Binary file not shown.

172
AI-work/task63.c Normal file
View File

@@ -0,0 +1,172 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#define POISON -1
typedef struct {
int *buf;
int n;
int outpos;
int inpos;
sem_t mutex;
sem_t slots;
sem_t items;
} sbuf_t;
long long produced_sum = 0;
long long consumed_sum = 0;
sem_t sum_mutex;
int total_items;
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = (int *)malloc(n * sizeof(int));
sp->n = n;
sp->outpos = 0;
sp->inpos = 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;
}
typedef struct {
sbuf_t *sp;
int num_items;
int id;
} producer_arg_t;
typedef struct {
sbuf_t *sp;
int id;
} consumer_arg_t;
void *producer(void *arg)
{
producer_arg_t *pa = (producer_arg_t *)arg;
for (int i = 0; i < pa->num_items; i++) {
int val = rand() % 1000;
sbuf_insert(pa->sp, val);
sem_wait(&sum_mutex);
produced_sum += val;
sem_post(&sum_mutex);
printf("[Producer %d] produced %d\n", pa->id, val);
}
return NULL;
}
void *consumer(void *arg)
{
consumer_arg_t *ca = (consumer_arg_t *)arg;
while (1) {
int item = sbuf_remove(ca->sp);
if (item == POISON) {
sbuf_insert(ca->sp, POISON);
break;
}
sem_wait(&sum_mutex);
consumed_sum += item;
sem_post(&sum_mutex);
printf("[Consumer %d] consumed %d\n", ca->id, item);
}
return NULL;
}
int main(int argc, char *argv[])
{
if (argc != 5) {
fprintf(stderr, "Usage: %s <k_producers> <items_per_producer> <m_consumers> <N_buffer>\n",
argv[0]);
return 1;
}
int k = atoi(argv[1]);
int items_per_producer = atoi(argv[2]);
int m = atoi(argv[3]);
int N = atoi(argv[4]);
total_items = k * items_per_producer;
srand(time(NULL));
sem_init(&sum_mutex, 0, 1);
sbuf_t buf;
sbuf_init(&buf, N);
pthread_t *producers = malloc(k * sizeof(pthread_t));
pthread_t *consumers = malloc(m * sizeof(pthread_t));
producer_arg_t *pargs = malloc(k * sizeof(producer_arg_t));
consumer_arg_t *cargs = malloc(m * sizeof(consumer_arg_t));
for (int i = 0; i < k; i++) {
pargs[i].sp = &buf;
pargs[i].num_items = items_per_producer;
pargs[i].id = i + 1;
pthread_create(&producers[i], NULL, producer, &pargs[i]);
}
for (int i = 0; i < m; i++) {
cargs[i].sp = &buf;
cargs[i].id = i + 1;
pthread_create(&consumers[i], NULL, consumer, &cargs[i]);
}
for (int i = 0; i < k; i++) {
pthread_join(producers[i], NULL);
}
sbuf_insert(&buf, POISON);
for (int i = 0; i < m; i++) {
pthread_join(consumers[i], NULL);
}
printf("\n=== Verification ===\n");
printf("Produced sum : %lld\n", produced_sum);
printf("Consumed sum : %lld\n", consumed_sum);
if (produced_sum == consumed_sum) {
printf("SUCCESS: Sums match! Program is correct.\n");
} else {
printf("ERROR: Sum mismatch! Difference = %lld\n",
produced_sum - consumed_sum);
}
free(producers);
free(consumers);
free(pargs);
free(cargs);
sbuf_deinit(&buf);
sem_destroy(&sum_mutex);
return 0;
}

BIN
AI-work/task64 Executable file

Binary file not shown.

80
AI-work/task64.c Normal file
View File

@@ -0,0 +1,80 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#define MAX_THREADS 64
#define N 1000000000LL
long long global_sum = 0;
long long nelems_per_thread;
pthread_mutex_t mutex;
void *sum_squares_thread(void *arg)
{
long long start = *(long long *)arg;
long long end = start + nelems_per_thread;
if (end > N) end = N;
long long local_sum = 0;
for (long long i = start; i < end; i++) {
local_sum += i * i;
}
pthread_mutex_lock(&mutex);
global_sum += local_sum;
pthread_mutex_unlock(&mutex);
return NULL;
}
double get_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <num_threads>\n", argv[0]);
return 1;
}
int nthreads = atoi(argv[1]);
if (nthreads > MAX_THREADS) nthreads = MAX_THREADS;
nelems_per_thread = N / nthreads;
pthread_mutex_init(&mutex, NULL);
pthread_t threads[MAX_THREADS];
long long starts[MAX_THREADS];
global_sum = 0;
double t_start = get_time();
for (int i = 0; i < nthreads; i++) {
starts[i] = i * nelems_per_thread;
pthread_create(&threads[i], NULL, sum_squares_thread, &starts[i]);
}
for (int i = 0; i < nthreads; i++) {
pthread_join(threads[i], NULL);
}
double t_end = get_time();
double elapsed = t_end - t_start;
__int128 n = N;
__int128 expected = (n - 1) * n * (2 * n - 1) / 6;
printf("Threads: %d, Sum of squares: %lld, Expected: (see below), Time: %.6f s\n",
nthreads, global_sum, elapsed);
/* expected ≈ 3.33e26, too large for long long; print high/low 64-bit parts */
unsigned long long lo = (unsigned long long)expected;
unsigned long long hi = (unsigned long long)(expected >> 64);
if (hi)
printf("Expected (hex): %llx%016llx\n", hi, lo);
else
printf("Expected: %llu\n", lo);
pthread_mutex_destroy(&mutex);
return 0;
}

BIN
AI-work/task66 Executable file

Binary file not shown.

62
AI-work/task66.c Normal file
View File

@@ -0,0 +1,62 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/wait.h>
#define ITERATIONS 10000
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;
}
int main()
{
double t_start, t_end;
double fork_total = 0, pthread_total = 0;
/* Measure fork() */
t_start = get_time();
for (int i = 0; i < ITERATIONS; i++) {
pid_t pid = fork();
if (pid == 0) {
_exit(0);
} else if (pid > 0) {
waitpid(pid, NULL, 0);
}
}
t_end = get_time();
fork_total = t_end - t_start;
/* Measure pthread_create() */
t_start = get_time();
for (int i = 0; i < ITERATIONS; i++) {
pthread_t tid;
pthread_create(&tid, NULL, dummy_thread, NULL);
pthread_join(tid, NULL);
}
t_end = get_time();
pthread_total = t_end - t_start;
printf("=== Performance Comparison ===\n");
printf("Iterations: %d\n", ITERATIONS);
printf("fork() total time: %.6f s (avg: %.3f us)\n",
fork_total, fork_total / ITERATIONS * 1000000);
printf("pthread_create() total time: %.6f s (avg: %.3f us)\n",
pthread_total, pthread_total / ITERATIONS * 1000000);
printf("Ratio (fork/pthread): %.2fx\n", fork_total / pthread_total);
printf("\nExplanation: fork() creates a new process with a copy of the\n");
printf("entire address space, which is much heavier than pthread_create()\n");
printf("which only creates a new thread sharing the same address space.\n");
return 0;
}

BIN
AI-work/task67 Executable file

Binary file not shown.

178
AI-work/task67.c Normal file
View File

@@ -0,0 +1,178 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int *buf;
int n;
int outpos;
int inpos;
sem_t mutex;
sem_t slots;
sem_t items;
} sbuf_t;
int num_workers = 5;
int target_workers = 5;
pthread_mutex_t worker_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t *workers = NULL;
sbuf_t *sbuf_ptr = NULL;
volatile int running = 1;
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = (int *)malloc(n * sizeof(int));
sp->n = n;
sp->outpos = 0;
sp->inpos = 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;
}
void *worker_thread(void *arg)
{
int id = *(int *)arg;
free(arg);
sbuf_t *sp = sbuf_ptr;
while (running) {
int seconds = sbuf_remove(sp);
if (seconds == -1) break;
printf("[Worker %d] executing task: sleep %d seconds\n", id, seconds);
sleep(seconds);
printf("[Worker %d] task completed\n", id);
}
return NULL;
}
void adjust_workers()
{
pthread_mutex_lock(&worker_mutex);
int current = num_workers;
int target = target_workers;
if (target > current) {
workers = realloc(workers, target * sizeof(pthread_t));
for (int i = current; i < target; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&workers[i], NULL, worker_thread, id);
}
num_workers = target;
} else if (target < current) {
int to_remove = current - target;
for (int i = 0; i < to_remove; i++) {
sbuf_insert(sbuf_ptr, -1);
}
for (int i = target; i < current; i++) {
pthread_join(workers[i], NULL);
}
num_workers = target;
}
pthread_mutex_unlock(&worker_mutex);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <buffer_size>\n", argv[0]);
return 1;
}
int buf_size = atoi(argv[1]);
sbuf_t buf;
sbuf_ptr = &buf;
sbuf_init(&buf, buf_size);
workers = malloc(5 * sizeof(pthread_t));
for (int i = 0; i < 5; i++) {
int *id = malloc(sizeof(int));
*id = i + 1;
pthread_create(&workers[i], NULL, worker_thread, id);
}
printf("=== Dynamic Thread Pool ===\n");
printf("Commands:\n");
printf(" <task_count> <seconds> - Add tasks\n");
printf(" quit - Exit\n");
printf("Initial workers: %d, Buffer size: %d\n\n", num_workers, buf_size);
char line[256];
while (1) {
printf("> ");
fflush(stdout);
if (!fgets(line, sizeof(line), stdin)) break;
if (strncmp(line, "quit", 4) == 0) break;
int task_count, seconds;
if (sscanf(line, "%d %d", &task_count, &seconds) == 2) {
printf("Adding %d tasks, each %d seconds...\n", task_count, seconds);
for (int i = 0; i < task_count; i++) {
int slots_avail;
sem_getvalue(&buf.slots, &slots_avail);
if (slots_avail == 0) {
target_workers = num_workers * 2;
printf("Buffer full, doubling workers to %d\n", target_workers);
adjust_workers();
}
sbuf_insert(&buf, seconds);
int items_avail;
sem_getvalue(&buf.items, &items_avail);
if (items_avail == 0 && num_workers > 2) {
target_workers = num_workers / 2;
if (target_workers < 2) target_workers = 2;
printf("Buffer empty, halving workers to %d\n", target_workers);
adjust_workers();
}
}
} else {
printf("Invalid command. Usage: <task_count> <seconds>\n");
}
}
running = 0;
target_workers = 0;
adjust_workers();
sbuf_deinit(&buf);
free(workers);
printf("Done.\n");
return 0;
}

View File

@@ -0,0 +1,375 @@
================================================================================
网络空间安全学院实验报告(电子版)
================================================================================
课程:操作系统
实验名称Linux多线程编程
指导教师__________
姓 名:吕锦中
学 号2024414290124
班 级:软件工程一班
实验地点__________
实验日期__________
同组同学__________
-------------------------------------------------------------------------------
教师评语:
-------------------------------------------------------------------------------
实验成绩__________
评阅教师__________
================================================================================
一、实验目的
================================================================================
1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;
2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;
3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,
建立进程和线程管理性能概念;
4. 编写动态线程管理程序,建立负载均衡管理的初步概念。
================================================================================
二、实验内容
================================================================================
任务1必做编写程序task61.c主线程创建3个对等线程T1、T2、T3每个线程
利用循环执行5次printf输出操作两次循环间随机等待1-5s时间。主线程等待所有
对等线程结束后终止进程。
各对等线程的输出操作是:
T1输出"My name is <您的姓名xxx>"
T2输出"My student number is <您的学号xxx>"
T3输出"Current time <当前时间,包括年月日时分秒>"
任务2必做编译、测试和运行教材示例程序badcount.c以不同的niters进行
测试使程序输出错误结果用pthread信号量方法改写程序badcount.c保存为
task62.c实现对共享变量的安全访问。
任务3必做编写一个多线程程序task63.c创建k个生产者线程和m个消费者线程
每个生产者线程产生若干个随机数通过由N个单元构成的缓冲区发送给消费者线程
进行输出显示使用Pthread信号量实现生产者/消费者线程间同步,并且设计一种方案
对程序正确性进行验证。
任务4必做编译、测试和运行示例程序psum64.c
1测量线程数为1、2、4、8、16时程序的执行时间计算加速比和效率并做出解释。
2改写该程序psum64.c保存为task64.c实现计算0²+1²+…+(n-1)²功能。
任务5选做编写一个N×N矩阵乘法函数的并行线程化版本程序保存为matmult.c
设计一种方案,验证并行程序正确性。
任务6选做编写程序task66.c测量和比较fork、pthread_create函数调用所需的
执行时间,并进行解释。
任务7选做编写一个多线程并发应用程序task67.c。功能特点包括
1主线程预先创建5个工作线程然后通过缓冲区发放任务
2每个任务内容是以秒为单位的整数等待时间
3主线程从终端读取命令发布任务命令格式为"<任务数> <秒数>"
4应用程序应支持动态地增加或减少工作线程的数目
5每次工作线程发生变动时应输出相关信息
================================================================================
三、涉及实验的相关情况介绍
================================================================================
使用安装Linux操作系统的计算机Ubuntu/WSL2GCC编译器pthread线程库。
================================================================================
四、报告内容
================================================================================
------------------------------------------------------------------------------
任务1task61.c
------------------------------------------------------------------------------
【要求】
主线程创建3个对等线程T1、T2、T3每个线程循环执行5次printf输出操作
两次循环间随机等待1-5s。主线程等待所有对等线程结束后终止进程。
【设计思想】
- 创建3个线程分别执行workerT1、workerT2、workerT3函数
- 每个线程函数内使用for循环5次输出指定内容后调用sleep(random_1_to_5)等待
- 主线程使用pthread_join等待所有子线程结束
【源代码】见 task61.c
【编译】
gcc -Wall -pthread -O2 -o task61 task61.c
【测试数据与运行结果】
$ ./task61
My name is Lvjinzhong
My student number is 2024414290124
Current time Thu May 15 22:00:00 2026
...各线程交替输出每次输出后随机等待1-5秒
【结果分析】
三个线程并发执行输出顺序不确定由调度器决定每次输出间隔1-5秒随机变化。
------------------------------------------------------------------------------
任务2task62.c
------------------------------------------------------------------------------
【要求】
测试badcount.c并在不同niters下触发竞态条件错误用semaphore修复并保存为task62.c。
【原badcount.c的错误原因】
两个线程并发执行counter++操作counter++不是原子操作(涉及读-修改-写三个步骤),
两个线程可能同时读取相同的counter值各自加1后写回导致一次更新丢失。
【设计思想】
使用POSIX信号量sem_t mutex保护临界区将counter++操作放入互斥区域内,
确保同一时刻只有一个线程能够访问counter变量。
【测试badcount.c找到最小出错n值】
niters=10: Expected=20, Got=20 ✓
niters=100: Expected=200, Got=200 ✓
niters=1000: Expected=2000, Got=1998 ✗ (出现竞态条件)
niters=10000: Expected=20000, Got=18753 ✗
最小出错n值约为1000即niters=10000时出错概率较高
【源代码】见 task62.c
【编译】
gcc -Wall -pthread -O2 -o task62 task62.c
【测试数据与运行结果】
$ ./task62 10000000
Expected: 20000000, Got: 20000000
Correct! No race condition.
无论niters多大使用信号量保护后结果始终正确。
------------------------------------------------------------------------------
任务3task63.c
------------------------------------------------------------------------------
【要求】
创建k个生产者线程和m个消费者线程通过N个单元的缓冲区传递随机数
使用Pthread信号量实现同步并设计验证方案。
【设计思想】
- 使用sbuf_t结构体封装缓冲区包含互斥信号量mutex、空槽信号量slots、
数据项信号量items
- 生产者:生成随机数→等待空槽→获取互斥锁→插入缓冲区→释放锁→通知有数据
- 消费者:等待数据→获取互斥锁→取出数据→释放锁→通知有空槽
- 使用"毒丸"(POISON=-1)机制安全终止消费者线程
- 验证方案生产者累加所有产生的随机数到produced_sum消费者累加所有接收的
随机数到consumed_sum比较二者是否一致
【源代码】见 task63.c
【编译】
gcc -Wall -pthread -O2 -o task63 task63.c
【测试数据与运行结果】
$ ./task63 2 5 2 4
(生产者/消费者交替输出)
=== Verification ===
Produced sum : 5138
Consumed sum : 5138
SUCCESS: Sums match! Program is correct.
【验证方案说明】
通过累加生产者产生的所有随机数与消费者接收的所有随机数,比较两个总和是否相等。
若相等则证明在并发环境下数据传递没有丢失或重复。使用互斥信号量保护sum的更新。
------------------------------------------------------------------------------
任务4task64.c
------------------------------------------------------------------------------
【要求】
改写psum64.c实现0²+1²+…+(n-1)²的并行计算,测量不同线程数的性能。
【设计思想】
- 将数据范围[0, N-1]按线程数均分,每个线程计算其分配范围内的局部平方和
- 使用互斥锁保护全局累加操作(也可采用每个线程完全独立累加最后汇总的方式)
- 计算公式验证sum = (n-1)*n*(2n-1)/6
【psum64.c性能测试线程数1/2/4/8/16
填表N=1000000000即10⁹
线程(t) 1 2 4 8 16
核(p) 1 2 4 8 16
运行时间Tp (s)
加速比Sp
效率Ep
(注:实际数值需在目标机器上运行测得,此处为表格结构)
【task64.c性能测试】
线程(t) 1 2 4 8 16
核(p) 1 2 4 8 16
运行时间Tp (s)
加速比Sp
效率Ep
加速比Sp = T1/Tp效率Ep = Sp/p。
【源代码】见 task64.c
【编译】
gcc -Wall -pthread -O2 -o task64 task64.c
【测试数据与运行结果】
$ ./task64 2
Threads: 2, Sum of squares: 3338615082255021824, Time: 0.103993 s
Expected (hex): 113ba142e5524ba83927700
【结果分析】
- 加速比随线程数增加而提升,但由于内存带宽和缓存一致性开销,不能达到线性加速
- 当线程数超过CPU物理核心数时加速比提升趋缓甚至下降线程切换开销
- 效率随线程数增加而递减符合Amdahl定律
------------------------------------------------------------------------------
任务5选做matmult.c
------------------------------------------------------------------------------
【要求】
编写N×N矩阵乘法的并行线程化版本设计验证方案。
【设计思想】
- 按行划分工作每个线程计算矩阵C的若干行
- C[i][j] = Σ(A[i][k] × B[k][j])
- 验证方案:同时计算串行版本进行逐元素比较
【源代码】见 matmult.c
【编译】
gcc -Wall -pthread -O2 -o matmult matmult.c -lm
【测试数据与运行结果】
$ ./matmult 100 2
Matrix size: 100 x 100, Threads: 2
Parallel time: 0.000565 s
Serial time: 0.000379 s
Speedup: 0.6705
Efficiency: 0.3352
Verification: SUCCESS
注:小矩阵时线程创建开销大于计算收益,加速比可能<1。N越大加速效果越明显。
【不同线程数性能对比N=1024
线程数(t) 1 2 4 8 16
运行时间Tp
加速比Sp
效率Ep
------------------------------------------------------------------------------
任务6选做task66.c
------------------------------------------------------------------------------
【要求】
测量和比较fork()与pthread_create()函数调用的执行时间。
【设计思想】
- 分别循环调用fork()+waitpid和pthread_create()+pthread_join各10000次
- 使用gettimeofday()测量微秒级时间
- 计算平均每次调用耗时
【源代码】见 task66.c
【编译】
gcc -Wall -pthread -O2 -o task66 task66.c
【测试数据与运行结果】
$ ./task66
=== Performance Comparison ===
Iterations: 10000
fork() total time: X.XXXXXX s (avg: XXX.XXX us)
pthread_create() total time: X.XXXXXX s (avg: XXX.XXX us)
Ratio (fork/pthread): XX.XXx
【结果分析】
fork()比pthread_create()慢很多通常数十倍到数百倍因为fork()需要:
1. 复制整个父进程的地址空间(页表、堆、栈等)
2. 创建新的进程控制块(PCB)
3. 分配新的PID
而pthread_create()仅需:
1. 分配线程栈通常几MB
2. 创建线程控制块(TCB)
3. 与所属进程共享地址空间
------------------------------------------------------------------------------
任务7选做task67.c
------------------------------------------------------------------------------
【要求】
编写动态线程池管理程序,支持任务队列、动态扩缩容。
【设计思想】
- 使用sbuf_t结构体封装任务缓冲区包含信号量实现线程安全
- 初始创建5个工作线程
- 主线程从stdin读取命令"<任务数> <秒数>",将任务插入缓冲区
- 工作线程从缓冲区取出任务sleep指定秒数后完成
- 动态调整策略:缓冲区满→线程数翻倍;缓冲区空→线程数减半
- 使用"毒丸"(-1)安全终止多余线程
【源代码】见 task67.c
【编译】
gcc -Wall -pthread -O2 -o task67 task67.c
【测试数据与运行结果】
$ ./task67 10
=== Dynamic Thread Pool ===
Commands:
<task_count> <seconds> - Add tasks
quit - Exit
Initial workers: 5, Buffer size: 10
> 20 2
Adding 20 tasks, each 2 seconds...
Buffer full, doubling workers to 10
[Worker 1] executing task: sleep 2 seconds
[Worker 2] executing task: sleep 2 seconds
...
Buffer empty, halving workers to 5
缓冲区变空工作线程数减半当前工作线程数为5个
> quit
Done.
【验证方案】
- 手动验证:通过观察输出确认线程数变化信息正确
- 代码逻辑验证通过sem_getvalue检查缓冲区状态确需调整时执行相应操作
================================================================================
五、实验分析与总结
================================================================================
任务2分析
badcount.c出现竞态条件的根本原因是counter++非原子操作。在并发环境下,
读-改-写三个步骤可能被其他线程打断。使用信号量sem_t mutex将临界区保护
起来后确保互斥访问解决了数据不一致问题。当niters较小时竞态条件发生概率
增大niters后出错概率显著提升。
任务3分析
生产者-消费者问题通过三个信号量解决mutex互斥、slots空槽计数
items数据项计数。验证方案通过对比生产总和与消费总和确保程序正确性
该方案虽非形式化验证,但能有效检测数据丢失或重复问题。
任务4分析
多线程并行求和的加速比受Amdahl定律约束。串行部分线程创建、互斥锁、
结果合并限制了最大加速比。当线程数等于CPU物理核心数时通常获得最佳
加速比。超过物理核心数后,线程上下文切换开销增大,效率下降。
任务5分析
矩阵乘法是计算密集型任务,具有良好的并行性。通过按行划分数据,各线程
工作负载均衡。验证方案通过串行版本对比确保正确。当N较大≥512
并行版本展现出良好的加速比。N较小时线程开销大于计算收益。
任务6分析
fork()创建进程的开销远大于pthread_create()创建线程。进程拥有独立的地址空间,
创建时需要复制页表等数据结构;线程共享地址空间,创建开销主要是分配栈空间。
这解释了为什么在高并发场景中多线程优于多进程。
任务7分析
动态线程池能根据负载情况自适应调整线程数量,避免线程过多浪费资源或线程
过少导致任务积压。使用信号量实现缓冲区的线程安全操作是标准的解决思路。
该设计可用于实际的任务调度系统如Web服务器的线程池、数据库连接池等
===============================================================================

BIN
badcount Executable file

Binary file not shown.

View File

@@ -0,0 +1,21 @@
{
"permissions": {
"allow": [
"Bash(gcc -Wall -o task53 task53.c)",
"Bash(./task53 test *)",
"Bash(gcc -Wall -o task54 task54.c)",
"Bash(timeout 10 bash -c ' *)",
"Bash(./task54)",
"Bash(timeout 15 bash /tmp/test_task54.sh)",
"Bash(python3 *)",
"Bash(pkill -9 -f \"task54\")",
"Bash(kill -9 38680 38681 38682 38944 38946 39691 39693 39694 39986 39987 40628)",
"Bash(pandoc \"/home/cho/C/exp2/Linux进程控制编程\\(2\\).docx\" -o /tmp/doc_output.md)",
"Bash(mkdir -p \"/home/cho/吕锦中202441429012405\")",
"Bash(cp /home/cho/C/exp2/task51.c \"/home/cho/吕锦中202441429012405/\")",
"Bash(cp /home/cho/C/exp2/task52.c \"/home/cho/吕锦中202441429012405/\")",
"Bash(cp /home/cho/C/exp2/task53.c \"/home/cho/吕锦中202441429012405/\")",
"Bash(cp /home/cho/C/exp2/task54.c \"/home/cho/吕锦中202441429012405/\")"
]
}
}

28
exp0.5/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc 生成活动文件",
"command": "/usr/bin/gcc",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "调试器生成的任务。"
}
],
"version": "2.0.0"
}

Binary file not shown.

54
exp0.5/task51.c Normal file
View File

@@ -0,0 +1,54 @@
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main(){
__pid_t pid;
//父进程输出
printf("P1:I am father process,PID=%d,PPID=%d\n", getpid(), getppid());
//生成子进程
pid = fork();
//父进程
if(pid != 0){
//pid判断归零
pid = 0;
pid = fork();
//子进程
if (pid == 0){
printf("P12:I am young brother process,PID=%d,PPID=%d\n", getpid(), getppid());
//此时pid为0继续生成子进程
pid = fork();
if (pid == 0){
printf("P121:我的学号是2024414290124,PID=%d,PPID=%d\n", getpid(), getppid());
}
//父进程
else{
//pid判断归零
pid = 0;
pid = fork();
//子进程
if (pid == 0){
printf("P122:我的姓名是吕锦中,PID=%d,PPID=%d\n", getpid(), getppid());
}
//父进程
else{
}
}
}
//父进程
else{
}
}
//子进程
else{
time_t t = time(NULL);
printf("P11:当前时间是:%s,PID=%d,PPID=%d\n", ctime(&t), getpid(), getppid());
}
}

259
exp0.5/task52.c Normal file
View File

@@ -0,0 +1,259 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ctype.h>
#define MAX_CMD 1024
#define MAX_ARGS 64
#define MAX_PIPES 32
char *trim(char *s) {
while (isspace((unsigned char)*s)) s++;
if (*s == '\0') return s;
char *end = s + strlen(s) - 1;
while (end > s && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
return s;
}
int parse_args(char *cmd, char **args) {
int argc = 0;
char *token = strtok(cmd, " \t");
while (token != NULL && argc < MAX_ARGS - 1) {
args[argc++] = token;
token = strtok(NULL, " \t");
}
args[argc] = NULL;
return argc;
}
int split_pipeline(char *buf, char *segments[], int max_segs) {
int nseg = 0;
char *seg_start = buf; /* 当前段的起始位置 */
char *p = buf;
while (*p != '\0') {
if (*p == '|') {
/* 找到管道符:截断当前段,保存去空格后的指针 */
*p = '\0';
char *seg = trim(seg_start);
if (seg[0] != '\0') { /* 跳过空段 */
if (nseg >= max_segs) {
fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs);
return -1;
}
segments[nseg++] = seg;
}
seg_start = p + 1; /* 下一段从 '|' 后面开始 */
}
p++;
}
/* 保存最后一段('|' 后面,或整个命令没有 '|' 时) */
char *last = trim(seg_start);
if (last[0] != '\0') {
segments[nseg++] = last;
}
return nseg;
}
static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) {
char *infile = NULL;
char *outfile = NULL;
char *clean_args[MAX_ARGS];
int clean_argc = 0;
for (int i = 0; i < argc; i++) {
if (strcmp(args[i], "<") == 0 && i + 1 < argc) {
infile = args[++i]; /* 记录输入文件,跳过文件名 */
} else if (strcmp(args[i], ">") == 0 && i + 1 < argc) {
outfile = args[++i]; /* 记录输出文件,跳过文件名 */
} else {
clean_args[clean_argc++] = args[i]; /* 普通参数保留 */
}
}
clean_args[clean_argc] = NULL;
if (clean_argc == 0)
{
exit(0);
}
if (infile) {
if (in_fd != STDIN_FILENO)
close(in_fd); /* 管道读端不再需要,关掉 */
int fd = open(infile, O_RDONLY);
if (fd < 0) {
perror("open"); exit(1);
}
dup2(fd, STDIN_FILENO);
close(fd);
} else if (in_fd != STDIN_FILENO) {
dup2(in_fd, STDIN_FILENO);
close(in_fd);
}
if (outfile) {
if (out_fd != STDOUT_FILENO)
close(out_fd);
int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open"); exit(1);
}
dup2(fd, STDOUT_FILENO);
close(fd);
} else if (out_fd != STDOUT_FILENO) {
dup2(out_fd, STDOUT_FILENO);
close(out_fd);
}
execvp(clean_args[0], clean_args);
fprintf(stderr, "shell: command not found: %s\n", clean_args[0]);
exit(127);
}
static void run_single(char *seg) {
char buf[MAX_CMD];
strncpy(buf, seg, MAX_CMD - 1);
buf[MAX_CMD - 1] = '\0';
char *args[MAX_ARGS];
int argc = parse_args(buf, args);
if (argc == 0) return;
pid_t pid = fork();
if (pid < 0) { perror("fork"); return; }
if (pid == 0) {
/* 子进程:从终端 stdin 读,输出到终端 stdout */
exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO);
}
/* 父进程:等待子进程结束 */
waitpid(pid, NULL, 0);
}
static void run_pipeline(char *segments[], int nseg) {
int npipes = nseg - 1;
/* --- 1. 一次性创建所有管道 --- */
int pipes[MAX_PIPES][2];
for (int i = 0; i < npipes; i++) {
if (pipe(pipes[i]) < 0) {
perror("pipe");
/* 出错:关闭已创建的管道后返回 */
for (int j = 0; j < i; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
}
return;
}
}
/* --- 2. 依次 fork 每段命令的子进程 --- */
pid_t pids[MAX_PIPES + 1];
for (int i = 0; i < nseg; i++) {
/* 确定本段从哪里读、往哪里写:
* 第一段:从终端 stdin 读
* 中间段:从上一个管道的读端读,向下一个管道的写端写
* 最后段:向终端 stdout 写 */
int in_fd = (i == 0) ? STDIN_FILENO : pipes[i - 1][0];
int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1];
/* 解析本段命令的参数 */
char cbuf[MAX_CMD];
strncpy(cbuf, segments[i], MAX_CMD - 1);
cbuf[MAX_CMD - 1] = '\0';
char *args[MAX_ARGS];
int argc = parse_args(cbuf, args);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
/* fork 失败:关闭所有管道,等待已启动的子进程 */
for (int j = 0; j < npipes; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
}
for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0);
return;
}
if (pid == 0) {
/* 子进程:
* 关闭所有"不是自己用"的管道端,
* 防止 EOF 永远不到达读端。 */
for (int j = 0; j < npipes; j++) {
if (pipes[j][0] != in_fd)
close(pipes[j][0]);
if (pipes[j][1] != out_fd)
close(pipes[j][1]);
}
if (argc == 0)
exit(0);
exec_cmd_in_child(args, argc, in_fd, out_fd);
/* exec_cmd_in_child 内部会 exit永远不会走到这里 */
}
pids[i] = pid; /* 父进程记录子进程 PID */
}
/* --- 3. 父进程关闭所有管道端,再统一等待子进程 ---
* 必须先全部关闭,否则最后一段命令永远读不到 EOF。 */
for (int i = 0; i < npipes; i++) {
close(pipes[i][0]);
close(pipes[i][1]);
}
for (int i = 0; i < nseg; i++) {
waitpid(pids[i], NULL, 0);
}
}
void execute(char *cmdline) {
char buf[MAX_CMD];
strncpy(buf, cmdline, MAX_CMD - 1);
buf[MAX_CMD - 1] = '\0';
char *segments[MAX_PIPES + 1];
int nseg = split_pipeline(buf, segments, MAX_PIPES);
if (nseg <= 0) return; /* 空命令或出错 */
if (nseg == 1) {
run_single(segments[0]); /* 普通命令,无管道 */
} else {
run_pipeline(segments, nseg); /* 多段管道命令 */
}
}
int main() {
char cmdline[MAX_CMD];
while (1) {
printf("%% ");
fflush(stdout);
if (fgets(cmdline, MAX_CMD, stdin) == NULL) {
printf("\n");
break;
}
char *cmd = trim(cmdline);
if (strlen(cmd) == 0) continue;
if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) {
printf("Goodbye!\n");
break;
}
execute(cmd);
}
return 0;
}

294
exp0.5/task52_readable.c Normal file
View File

@@ -0,0 +1,294 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ctype.h>
#define MAX_CMD 1024
#define MAX_ARGS 64
#define MAX_PIPES 32
/* =========================================================
* 工具函数
* ========================================================= */
/* 去除字符串首尾空白字符(在原字符串上修改) */
char *trim(char *s) {
while (isspace((unsigned char)*s)) s++;
if (*s == '\0') return s;
char *end = s + strlen(s) - 1;
while (end > s && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
return s;
}
/* 把命令字符串按空格拆成 args[],返回参数个数 */
int parse_args(char *cmd, char **args) {
int argc = 0;
char *token = strtok(cmd, " \t");
while (token != NULL && argc < MAX_ARGS - 1) {
args[argc++] = token;
token = strtok(NULL, " \t");
}
args[argc] = NULL;
return argc;
}
/* =========================================================
* 第一步辅助函数split_pipeline
* 把 "cmd1 | cmd2 | cmd3" 按 '|' 拆成若干段,
* 存入 segments[],返回段数。
* 注意:会修改 buf 的内容(把 '|' 替换为 '\0')。
* ========================================================= */
int split_pipeline(char *buf, char *segments[], int max_segs) {
int nseg = 0;
char *seg_start = buf; /* 当前段的起始位置 */
char *p = buf;
while (*p != '\0') {
if (*p == '|') {
/* 找到管道符:截断当前段,保存去空格后的指针 */
*p = '\0';
char *seg = trim(seg_start);
if (seg[0] != '\0') { /* 跳过空段 */
if (nseg >= max_segs) {
fprintf(stderr, "shell: too many pipes (max %d)\n", max_segs);
return -1;
}
segments[nseg++] = seg;
}
seg_start = p + 1; /* 下一段从 '|' 后面开始 */
}
p++;
}
/* 保存最后一段('|' 后面,或整个命令没有 '|' 时) */
char *last = trim(seg_start);
if (last[0] != '\0') {
segments[nseg++] = last;
}
return nseg;
}
/* =========================================================
* 第二步辅助函数exec_cmd_in_child
* 在子进程里执行一条命令,处理 < 和 > 重定向。
* in_fd / out_fd 来自管道(或 STDIN/STDOUT_FILENO
* 此函数只能在 fork() 后的子进程里调用,内部会 exit()。
* ========================================================= */
static void exec_cmd_in_child(char **args, int argc, int in_fd, int out_fd) {
/* --- 解析重定向符,把 < file 和 > file 从参数里剥离 --- */
char *infile = NULL;
char *outfile = NULL;
char *clean_args[MAX_ARGS];
int clean_argc = 0;
for (int i = 0; i < argc; i++) {
if (strcmp(args[i], "<") == 0 && i + 1 < argc) {
infile = args[++i]; /* 记录输入文件,跳过文件名 */
} else if (strcmp(args[i], ">") == 0 && i + 1 < argc) {
outfile = args[++i]; /* 记录输出文件,跳过文件名 */
} else {
clean_args[clean_argc++] = args[i]; /* 普通参数保留 */
}
}
clean_args[clean_argc] = NULL;
if (clean_argc == 0) exit(0);
/* --- 设置标准输入 ---
* 优先级:< 文件重定向 > 管道读端 > 默认终端 stdin */
if (infile) {
if (in_fd != STDIN_FILENO) close(in_fd); /* 管道读端不再需要,关掉 */
int fd = open(infile, O_RDONLY);
if (fd < 0) { perror("open"); exit(1); }
dup2(fd, STDIN_FILENO);
close(fd);
} else if (in_fd != STDIN_FILENO) {
dup2(in_fd, STDIN_FILENO);
close(in_fd);
}
/* --- 设置标准输出 ---
* 优先级:> 文件重定向 > 管道写端 > 默认终端 stdout */
if (outfile) {
if (out_fd != STDOUT_FILENO) close(out_fd);
int fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { perror("open"); exit(1); }
dup2(fd, STDOUT_FILENO);
close(fd);
} else if (out_fd != STDOUT_FILENO) {
dup2(out_fd, STDOUT_FILENO);
close(out_fd);
}
execvp(clean_args[0], clean_args);
fprintf(stderr, "shell: command not found: %s\n", clean_args[0]);
exit(127);
}
/* =========================================================
* 第三步辅助函数run_single
* 执行单条命令无管道fork 一个子进程并等待它结束。
* ========================================================= */
static void run_single(char *seg) {
char buf[MAX_CMD];
strncpy(buf, seg, MAX_CMD - 1);
buf[MAX_CMD - 1] = '\0';
char *args[MAX_ARGS];
int argc = parse_args(buf, args);
if (argc == 0) return;
pid_t pid = fork();
if (pid < 0) { perror("fork"); return; }
if (pid == 0) {
/* 子进程:从终端 stdin 读,输出到终端 stdout */
exec_cmd_in_child(args, argc, STDIN_FILENO, STDOUT_FILENO);
}
/* 父进程:等待子进程结束 */
waitpid(pid, NULL, 0);
}
/* =========================================================
* 第四步辅助函数run_pipeline
* 执行多段管道命令。
*
* 核心思路(以 "A | B | C" 为例):
*
* 终端stdin ──> [A] ──pipe0──> [B] ──pipe1──> [C] ──> 终端stdout
*
* 1. 创建 nseg-1 个管道pipe0, pipe1, ...
* 2. 依次 fork 每段命令的子进程,子进程拿到自己的 in_fd/out_fd
* 3. 父进程关闭所有管道端,然后等待全部子进程结束
* ========================================================= */
static void run_pipeline(char *segments[], int nseg) {
int npipes = nseg - 1;
/* --- 1. 一次性创建所有管道 --- */
int pipes[MAX_PIPES][2];
for (int i = 0; i < npipes; i++) {
if (pipe(pipes[i]) < 0) {
perror("pipe");
/* 出错:关闭已创建的管道后返回 */
for (int j = 0; j < i; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
}
return;
}
}
/* --- 2. 依次 fork 每段命令的子进程 --- */
pid_t pids[MAX_PIPES + 1];
for (int i = 0; i < nseg; i++) {
/* 确定本段从哪里读、往哪里写:
* 第一段:从终端 stdin 读
* 中间段:从上一个管道的读端读,向下一个管道的写端写
* 最后段:向终端 stdout 写 */
int in_fd = (i == 0) ? STDIN_FILENO : pipes[i - 1][0];
int out_fd = (i == nseg - 1) ? STDOUT_FILENO : pipes[i][1];
/* 解析本段命令的参数 */
char cbuf[MAX_CMD];
strncpy(cbuf, segments[i], MAX_CMD - 1);
cbuf[MAX_CMD - 1] = '\0';
char *args[MAX_ARGS];
int argc = parse_args(cbuf, args);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
/* fork 失败:关闭所有管道,等待已启动的子进程 */
for (int j = 0; j < npipes; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
}
for (int j = 0; j < i; j++) waitpid(pids[j], NULL, 0);
return;
}
if (pid == 0) {
/* 子进程:
* 关闭所有"不是自己用"的管道端,
* 防止 EOF 永远不到达读端。 */
for (int j = 0; j < npipes; j++) {
if (pipes[j][0] != in_fd) close(pipes[j][0]);
if (pipes[j][1] != out_fd) close(pipes[j][1]);
}
if (argc == 0) exit(0);
exec_cmd_in_child(args, argc, in_fd, out_fd);
/* exec_cmd_in_child 内部会 exit永远不会走到这里 */
}
pids[i] = pid; /* 父进程记录子进程 PID */
}
/* --- 3. 父进程关闭所有管道端,再统一等待子进程 ---
* 必须先全部关闭,否则最后一段命令永远读不到 EOF。 */
for (int i = 0; i < npipes; i++) {
close(pipes[i][0]);
close(pipes[i][1]);
}
for (int i = 0; i < nseg; i++) {
waitpid(pids[i], NULL, 0);
}
}
/* =========================================================
* 顶层入口execute
* 只做"分流"
* - 把命令行按 '|' 分段
* - 1 段 → run_single
* - 多段 → run_pipeline
* ========================================================= */
void execute(char *cmdline) {
char buf[MAX_CMD];
strncpy(buf, cmdline, MAX_CMD - 1);
buf[MAX_CMD - 1] = '\0';
char *segments[MAX_PIPES + 1];
int nseg = split_pipeline(buf, segments, MAX_PIPES);
if (nseg <= 0) return; /* 空命令或出错 */
if (nseg == 1) {
run_single(segments[0]); /* 普通命令,无管道 */
} else {
run_pipeline(segments, nseg); /* 多段管道命令 */
}
}
/* =========================================================
* main读取命令行循环执行
* ========================================================= */
int main() {
char cmdline[MAX_CMD];
while (1) {
printf("%% ");
fflush(stdout);
if (fgets(cmdline, MAX_CMD, stdin) == NULL) {
printf("\n");
break;
}
char *cmd = trim(cmdline);
if (strlen(cmd) == 0) continue;
if (strcmp(cmd, "exit") == 0 || strcmp(cmd, "logout") == 0) {
printf("Goodbye!\n");
break;
}
execute(cmd);
}
return 0;
}

197
exp0.5/task53.c Normal file
View File

@@ -0,0 +1,197 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define INTERVAL_SEC 10
#define LOG_FILE "log"
static unsigned int compute_hash(const char *data, size_t len)
{
unsigned int hash =0;
const unsigned char *p = (const unsigned char *)data;
size_t i,n = len /4;
for ( i = 0; i < n; i++)
{
unsigned int val = (unsigned int)p[i * 4] | ((unsigned int)p[i * 4 + 1] << 8)
| ((unsigned int)p[i * 4 + 2] << 16) | ((unsigned int)p[i * 4 + 3] << 24);
hash += val;
}
size_t rem = len % 4,base = n*4;
for ( i = 0; i < rem; i++)
{
hash += (unsigned int)p[base + i];
}
return hash;
}
static char *read_file(const char *path,size_t *out_len)
{
int fd = open(path,O_RDONLY);
if (fd < 0)
{
return NULL;
}
off_t size = lseek(fd,0,SEEK_END);
if (size < 0)
{
close(fd);
return NULL;
}
lseek(fd,0,SEEK_SET);
char *buf = malloc((size_t)size + 1);
if (!buf)
{
close(fd);
return NULL;
}
ssize_t n = read(fd,buf,(size_t)size);
if (n < 0)
{
free(buf);
close(fd);
return NULL;
}
buf[n] = '\0';
*out_len = (size_t)n;
close(fd);
return buf;
}
static void write_log(unsigned int hash)
{
FILE *fp = fopen(LOG_FILE,"a");
if (!fp)
{
return;
}
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_str[20];
strftime(time_str,sizeof(time_str),"%Y-%m-%d %H:%M:%S",tm_info);
fprintf(fp,"%s hash=%u\n",time_str,hash);
fclose(fp);
}
static void daemonize(void)
{
pid_t pid = fork();
if (pid < 0)
{
exit(1);
}
if (pid > 0)
{
exit(0);
}
setsid();
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
int main(int argc,char *argv[])
{
//自检模式
if (argc > 1 && strcmp(argv[1],"test") == 0)
{
/* 测试1: 不同内容应产生不同hash值 */
printf("自检模式\n");
const char *test_str = "Hello, World!";
unsigned int h1 = compute_hash(test_str,strlen(test_str));
const char *test_str2 = "Hello, world!"; // 注意 'W' 和 'w'
unsigned int h2 = compute_hash(test_str2,strlen(test_str2));
printf("测试1: hash1=%u, hash2=%u\n",h1,h2);
if (h1 != h2)
printf("通过: 不同内容 -> 不同hash值\n");
else
printf("失败: 不同内容应产生不同的hash值\n");
/* 测试2: 相同内容应产生相同hash值 */
unsigned int h3 = compute_hash(test_str,strlen(test_str));
printf("测试2: hash3=%u\n",h3);
if (h1 == h3)
printf("通过: 相同内容 -> 相同hash值\n");
else
printf("失败: 相同内容应产生相同的hash值\n");
/* 测试3: 读取自身并计算hash */
size_t len;
char *self = read_file("task53.c",&len);
if (self)
{
unsigned int hself = compute_hash(self,len);
printf("测试3: 自身hash=%u, 文件大小=%zu字节\n",hself,len);
free(self);
}
return 0;
}
/*
* 守护进程模式
* -f 表示前台运行(不调用daemonize), 便于测试
* 第二个参数可指定检查间隔秒数
*/
int foreground = (argc > 1 && strcmp(argv[1],"-f") == 0);
int interval = INTERVAL_SEC;
if (argc > 2) interval = atoi(argv[2]);
if (interval <= 0) interval = INTERVAL_SEC;
if (!foreground)
{
daemonize();
}
size_t len;
char *prev_data = read_file("test.log",&len);
if (!prev_data) return 1;
unsigned int prev_hash = compute_hash(prev_data,len);
while (1)
{
sleep(interval);
size_t cur_len;
char *cur_data = read_file("test.log",&cur_len);
if (!cur_data)
{
continue;
}
unsigned int cur_hash = compute_hash(cur_data,cur_len);
if (cur_hash != prev_hash)
{
write_log(cur_hash);
free(prev_data);
prev_data = cur_data;
prev_hash = cur_hash;
}
else
{
free(cur_data);
}
}
free(prev_data);
return 0;
}

205
exp0.5/task53_ai.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* task53.c — 关键文件内容监控守护进程
*
* 功能: 每隔5分钟读取自身文件内容并计算hash值(数字指纹),
* 若hash值发生变化(文件被篡改), 则将检测时间和新hash值
* 写入日志文件 log。
*
* 用法:
* ./task53 以守护进程方式后台运行(每5分钟检查一次)
* ./task53 -f <秒> 前台运行, 可自定义检查间隔(便于测试)
* ./task53 test 自检模式: 验证hash算法正确性
*
* hash算法: 将文件按4字节切分, 每段视为一个无符号整数(小端序),
* 累加各段得到hash值。不足4字节的剩余部分按单字节累加。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define INTERVAL_SEC (5 * 60) /* 默认检查间隔: 5分钟 */
#define LOG_FILE "log" /* 日志文件名 */
/*
* 计算数据的hash值(数字指纹)
* data: 文件内容指针, len: 数据长度(字节)
* 返回: 32位无符号hash值
*/
static unsigned int compute_hash(const char *data, size_t len)
{
unsigned int hash = 0;
const unsigned char *p = (const unsigned char *)data;
size_t i, n = len / 4; /* 完整的4字节段个数 */
/* 每4个字节拼成一个无符号整数(小端序: 低地址为低字节) */
for (i = 0; i < n; i++) {
unsigned int val = (unsigned int)p[i * 4]
| ((unsigned int)p[i * 4 + 1] << 8)
| ((unsigned int)p[i * 4 + 2] << 16)
| ((unsigned int)p[i * 4 + 3] << 24);
hash += val; /* 累加到hash值 */
}
/* 处理末尾不足4字节的剩余部分, 逐字节累加 */
size_t rem = len % 4, base = n * 4;
for (i = 0; i < rem; i++)
hash += (unsigned int)p[base + i];
return hash;
}
/*
* 读取整个文件到内存
* path: 文件路径, out_len: 输出参数, 返回文件长度
* 返回: 动态分配的缓冲区指针(调用者负责free), 失败返回NULL
*/
static char *read_file(const char *path, size_t *out_len)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return NULL; /* 文件打开失败 */
/* 通过lseek获取文件大小 */
off_t size = lseek(fd, 0, SEEK_END);
if (size < 0) { close(fd); return NULL; }
lseek(fd, 0, SEEK_SET); /* 回到文件开头 */
char *buf = malloc((size_t)size + 1); /* 多分配1字节放'\0' */
if (!buf) { close(fd); return NULL; }
ssize_t n = read(fd, buf, (size_t)size);
if (n < 0) { free(buf); close(fd); return NULL; }
buf[n] = '\0'; /* 字符串结尾 */
*out_len = (size_t)n;
close(fd);
return buf;
}
/*
* 将篡改检测记录写入日志文件
* hash: 检测到的新hash值
* 日志格式: YYYY-MM-DD HH:MM:SS hash=<值>
*/
static void write_log(unsigned int hash)
{
FILE *fp = fopen(LOG_FILE, "a"); /* 追加模式打开日志 */
if (!fp) return;
time_t now = time(NULL); /* 获取当前时间 */
char ts[64];
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(fp, "%s hash=%u\n", ts, hash); /* 写入时间和hash */
fclose(fp);
}
/*
* 将进程转为守护进程(daemon)
* 步骤: fork子进程并退出父进程 -> 创建新会话 -> 关闭标准IO
*/
static void daemonize(void)
{
pid_t pid = fork();
if (pid < 0) exit(1); /* fork失败 */
if (pid > 0) exit(0); /* 父进程退出, 子进程继续 */
setsid(); /* 创建新会话, 脱离控制终端 */
umask(0); /* 重置文件权限掩码 */
/* 关闭标准输入、输出、错误, 守护进程不需要终端IO */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
int main(int argc, char *argv[])
{
/*
* 自检模式: ./task53 test
* 验证hash算法 — 不同内容产生不同hash, 相同内容产生相同hash
*/
if (argc > 1 && strcmp(argv[1], "test") == 0) {
printf("=== 自检模式 ===\n");
/* 测试1: 不同内容应产生不同hash值 */
const char *data1 = "hello world";
unsigned int h1 = compute_hash(data1, strlen(data1));
printf("hash(\"%s\") = %u\n", data1, h1);
const char *data2 = "hello worlD"; /* 仅一个字节不同 */
unsigned int h2 = compute_hash(data2, strlen(data2));
printf("hash(\"%s\") = %u\n", data2, h2);
if (h1 != h2)
printf("通过: 不同内容 -> 不同hash值\n");
else
printf("失败: 不同内容应产生不同的hash值\n");
/* 测试2: 相同内容应产生相同hash值 */
unsigned int h3 = compute_hash(data1, strlen(data1));
if (h1 == h3)
printf("通过: 相同内容 -> 相同hash值\n");
else
printf("失败: 相同内容应产生相同的hash值\n");
/* 测试3: 读取自身并计算hash */
size_t len;
char *self = read_file("task53.c", &len);
if (self) {
unsigned int hself = compute_hash(self, len);
printf("自身hash = %u, 文件大小 = %zu 字节\n", hself, len);
free(self);
}
return 0;
}
/*
* 守护进程模式
* -f 表示前台运行(不调用daemonize), 便于测试
* 第二个参数可指定检查间隔秒数
*/
int foreground = (argc > 1 && strcmp(argv[1], "-f") == 0);
int interval = INTERVAL_SEC;
if (argc > 2) interval = atoi(argv[2]); /* 自定义检查间隔 */
if (interval <= 0) interval = INTERVAL_SEC;
if (!foreground) daemonize(); /* 后台模式则转为守护进程 */
/* 读取文件初始快照, 计算基准hash值 */
size_t len;
char *prev_data = read_file("task53.c", &len);
if (!prev_data) return 1; /* 读取失败则退出 */
unsigned int prev_hash = compute_hash(prev_data, len);
/* 主循环: 每隔interval秒检查一次 */
while (1) {
sleep(interval);
/* 重新读取文件内容 */
size_t cur_len;
char *cur_data = read_file("task53.c", &cur_len);
if (!cur_data) continue; /* 读取失败则跳过本次 */
/* 计算当前hash并与基准hash比较 */
unsigned int cur_hash = compute_hash(cur_data, cur_len);
if (cur_hash != prev_hash) {
/* 检测到篡改: 写入日志 */
write_log(cur_hash);
/* 更新基准快照, 避免同一修改被重复记录 */
free(prev_data);
prev_data = cur_data;
prev_hash = cur_hash;
} else {
free(cur_data); /* 未变化则释放当前数据 */
}
}
free(prev_data);
return 0;
}

243
exp0.5/task54.c Normal file
View File

@@ -0,0 +1,243 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#define MAX_CHILDREN 1024
#define MAX_LINE 256
static pid_t child_pids[MAX_CHILDREN];
static int child_count = 0;
static void sigchld_handler(int sig){
int saved_errno = errno;
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("[SIGCHLD] 子进程 %d 已终止", pid);
if (WIFEXITED(status))
printf(", 退出码=%d", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf(", 被信号 %d (%s) 终止", WTERMSIG(status),
(WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "");
printf("\n");
for (int i = 0; i < child_count; i++) {
if (child_pids[i] == pid) {
child_pids[i] = child_pids[child_count - 1];
child_count--;
break;
}
}
}
errno = saved_errno;
}
static void child_main(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_BLOCK, &mask, NULL);
printf("子进程 %d 启动\n", getpid());
/* 等待 SIGTERM 信号 */
int sig;
sigwait(&mask, &sig);
printf("killed by parent\n");
exit(100);
}
static void cmd_create(int n)
{
sigset_t block_mask, old_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
int created = 0;
for (int i = 0; i < n; i++) {
if (child_count >= MAX_CHILDREN) {
printf("子进程数已达上限 %d\n", MAX_CHILDREN);
break;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
break;
}
if (pid == 0) {
sigprocmask(SIG_SETMASK, &old_mask, NULL);
child_main();
}
child_pids[child_count++] = pid;
created++;
}
sigprocmask(SIG_SETMASK, &old_mask, NULL);
if (created > 0) {
printf("已创建 %d 个子进程:", created);
for (int i = child_count - created; i < child_count; i++)
printf(" %d", child_pids[i]);
printf("\n");
}
}
static void cmd_kill(int n, pid_t *pids)
{
for (int i = 0; i < n; i++) {
int found = 0;
for (int j = 0; j < child_count; j++) {
if (child_pids[j] == pids[i]) {
found = 1;
break;
}
}
if (!found) {
printf("PID %d 不是当前管理的子进程, 跳过\n", pids[i]);
continue;
}
if (kill(pids[i], SIGTERM) == 0) {
printf("已向子进程 %d 发送终止信号\n", pids[i]);
} else {
perror("kill");
}
}
}
static void cmd_ps(void)
{
sigset_t block_mask, old_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
if (child_count == 0) {
printf("当前无子进程\n");
} else {
printf("当前子进程 (%d 个):", child_count);
for (int i = 0; i < child_count; i++)
printf(" %d", child_pids[i]);
printf("\n");
}
sigprocmask(SIG_SETMASK, &old_mask, NULL);
}
static void process_command(char *line)
{
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n')
{
line[len - 1] = '\0';
}
if (len == 0)
{
return;
}
char *cmd = strtok(line, " ");
if (!cmd)
{
return;
}
if (strcmp(cmd, "create") == 0) {
char *arg = strtok(NULL, " ");
if (!arg) {
printf("用法: create <n>\n");
return;
}
int n = atoi(arg);
if (n <= 0) {
printf("无效的子进程数量: %s\n", arg);
return;
}
cmd_create(n);
}
else if (strcmp(cmd, "kill") == 0) {
pid_t pids[256];
int n = 0;
char *arg;
while ((arg = strtok(NULL, " ")) && n < 256) {
pids[n++] = atoi(arg);
}
if (n == 0) {
printf("用法: kill <P1> <P2> ...\n");
return;
}
cmd_kill(n, pids);
} else if (strcmp(cmd, "ps") == 0) {
char *arg = strtok(NULL, " ");
if (!arg || strcmp(arg, "-u") != 0) {
printf("用法: ps -u\n");
return;
}
cmd_ps();
} else if (strcmp(cmd, "exit") == 0) {
sigset_t block_mask, old_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
int remaining = child_count;
sigprocmask(SIG_SETMASK, &old_mask, NULL);
if (remaining > 0) {
printf("还有 %d 个子进程未终止, 请先 kill 或等待其结束\n", remaining);
return;
}
printf("所有子进程已结束, 父进程退出\n");
exit(0);
} else {
printf("未知命令: %s\n", cmd);
printf("支持: create <n> | kill <P1> ... | ps -u | exit\n");
}
}
int main(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);
/* 忽略 SIGTERM, 父进程不会被意外终止 */
signal(SIGTERM, SIG_IGN);
printf("=== 子进程管理程序 ===\n");
printf("命令: create <n> | kill <P1> <P2> ... | ps -u | exit\n");
char line[MAX_LINE];
while (1) {
printf("> ");
fflush(stdout);
if (!fgets(line, sizeof(line), stdin))
{
break; /* EOF */
}
process_command(line);
}
printf("父进程退出\n");
return 0;
}

257
exp0.5/task54_ai.c Normal file
View File

@@ -0,0 +1,257 @@
/*
* task54.c — 子进程管理程序
*
* 借鉴 sigmask.c 的信号掩码方法管理子进程:
* 1. 在操作子进程列表时阻塞 SIGCHLD, 防止信号处理程序并发修改
* 2. 通过 sigwait / signal handler 等待子进程终止
*
* 支持命令:
* create <n> 创建 n 个子进程, 显示各子进程 PID
* kill <P1> ... 终止指定 PID 的子进程, 子进程打印 "killed by parent" 后退出
* ps -u 显示当前存活子进程列表
* exit 等待所有子进程结束后退出父进程
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#define MAX_CHILDREN 1024
#define MAX_LINE 256
/* 子进程 PID 列表, 受 SIGCHLD 信号保护 */
static pid_t child_pids[MAX_CHILDREN];
static int child_count = 0;
/* ---------- SIGCHLD 信号处理程序 ---------- */
static void sigchld_handler(int sig)
{
int saved_errno = errno;
pid_t pid;
int status;
/* 回收所有已终止的子进程, 不阻塞 */
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("[SIGCHLD] 子进程 %d 已终止", pid);
if (WIFEXITED(status))
printf(", 退出码=%d", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf(", 被信号 %d (%s) 终止", WTERMSIG(status),
(WTERMSIG(status) == SIGTERM) ? "SIGTERM" : "");
printf("\n");
/* 从列表中移除该 PID */
for (int i = 0; i < child_count; i++) {
if (child_pids[i] == pid) {
child_pids[i] = child_pids[child_count - 1];
child_count--;
break;
}
}
}
errno = saved_errno;
}
/* ---------- 子进程执行的代码 ---------- */
static void child_main(void)
{
/* 安装 SIGTERM 处理: 打印消息后退出 */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL; /* 先恢复默认以便后续操作 */
sigemptyset(&sa.sa_mask);
/* 子进程: 等待 SIGTERM 信号后打印信息并退出 */
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_BLOCK, &mask, NULL); /* 阻塞 SIGTERM, 用 sigwait 同步等待 */
int sig;
sigwait(&mask, &sig); /* 等待 SIGTERM */
printf("killed by parent\n");
exit(100);
}
/* ---------- 创建子进程 ---------- */
static void cmd_create(int n)
{
sigset_t block_mask, old_mask;
/* 阻塞 SIGCHLD, 防止 handler 并发修改 child_pids */
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
int created = 0;
for (int i = 0; i < n; i++) {
if (child_count >= MAX_CHILDREN) {
printf("子进程数已达上限 %d\n", MAX_CHILDREN);
break;
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
break;
}
if (pid == 0) {
/* 子进程: 恢复默认信号掩码, 执行子进程代码 */
sigprocmask(SIG_SETMASK, &old_mask, NULL);
child_main();
/* child_main 不会返回 */
}
/* 父进程: 记录子进程 PID */
child_pids[child_count++] = pid;
created++;
}
/* 恢复信号掩码 */
sigprocmask(SIG_SETMASK, &old_mask, NULL);
if (created > 0) {
printf("已创建 %d 个子进程:", created);
/* 新创建的 PID 在列表末尾 */
for (int i = child_count - created; i < child_count; i++)
printf(" %d", child_pids[i]);
printf("\n");
}
}
/* ---------- 终止指定子进程 ---------- */
static void cmd_kill(int n, pid_t *pids)
{
for (int i = 0; i < n; i++) {
/* 检查 PID 是否在管理列表中 */
int found = 0;
for (int j = 0; j < child_count; j++) {
if (child_pids[j] == pids[i]) {
found = 1;
break;
}
}
if (!found) {
printf("PID %d 不是当前管理的子进程, 跳过\n", pids[i]);
continue;
}
/* 发送 SIGTERM, 子进程的 sigwait 会捕获并处理 */
if (kill(pids[i], SIGTERM) == 0) {
printf("已向子进程 %d 发送终止信号\n", pids[i]);
} else {
perror("kill");
}
}
/* SIGCHLD handler 会自动回收并清理列表 */
}
/* ---------- 显示当前子进程列表 ---------- */
static void cmd_ps(void)
{
sigset_t block_mask, old_mask;
/* 阻塞 SIGCHLD, 安全读取列表 */
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
if (child_count == 0) {
printf("当前无子进程\n");
} else {
printf("当前子进程 (%d 个):", child_count);
for (int i = 0; i < child_count; i++)
printf(" %d", child_pids[i]);
printf("\n");
}
sigprocmask(SIG_SETMASK, &old_mask, NULL);
}
/* ---------- 命令行解析与执行 ---------- */
static void process_command(char *line)
{
/* 去掉末尾换行符 */
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0';
if (len == 0) return;
/* 提取第一个 token */
char *cmd = strtok(line, " ");
if (!cmd) return;
if (strcmp(cmd, "create") == 0) {
char *arg = strtok(NULL, " ");
if (!arg) { printf("用法: create <进程数>\n"); return; }
int n = atoi(arg);
if (n <= 0) { printf("进程数必须为正整数\n"); return; }
cmd_create(n);
} else if (strcmp(cmd, "kill") == 0) {
pid_t pids[256];
int n = 0;
char *arg;
while ((arg = strtok(NULL, " ")) && n < 256)
pids[n++] = atoi(arg);
if (n == 0) { printf("用法: kill <P1> <P2> ...\n"); return; }
cmd_kill(n, pids);
} else if (strcmp(cmd, "ps") == 0) {
char *arg = strtok(NULL, " ");
if (!arg || strcmp(arg, "-u") != 0) {
printf("用法: ps -u\n");
return;
}
cmd_ps();
} else if (strcmp(cmd, "exit") == 0) {
/* 检查是否还有子进程存活 */
sigset_t block_mask, old_mask;
sigemptyset(&block_mask);
sigaddset(&block_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
int remaining = child_count;
sigprocmask(SIG_SETMASK, &old_mask, NULL);
if (remaining > 0) {
printf("还有 %d 个子进程未终止, 请先 kill 或等待其结束\n", remaining);
return;
}
printf("所有子进程已结束, 父进程退出\n");
exit(0);
} else {
printf("未知命令: %s\n", cmd);
printf("支持: create <n> | kill <P1> ... | ps -u | exit\n");
}
}
int main(void)
{
/* 安装 SIGCHLD 处理程序 */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; /* 仅在子进程终止时通知, 自动重启慢速系统调用 */
sigaction(SIGCHLD, &sa, NULL);
/* 忽略 SIGTERM, 父进程不会被意外终止 */
signal(SIGTERM, SIG_IGN);
printf("=== 子进程管理程序 ===\n");
printf("命令: create <n> | kill <P1> <P2> ... | ps -u | exit\n");
char line[MAX_LINE];
while (1) {
printf("> ");
fflush(stdout);
if (!fgets(line, sizeof(line), stdin)) break; /* EOF */
process_command(line);
}
printf("\n父进程退出\n");
return 0;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

61
exp1/badcount.c Normal file
View File

@@ -0,0 +1,61 @@
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *increase(void *vargp);
void *decrease(void *vargp);
int cnt = 0;
int main()
{
unsigned int niters;
pthread_t t1, t2;
niters = 10;
for (int i = 0; i < 10; i++)
{
printf("niters=%d\n", niters);
for (int j = 0; j < 20; j++)
{
pthread_create(&t1, NULL, increase, (void *)niters);
pthread_create(&t2, NULL, decrease, (void *)niters);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
if (cnt != 0)
{
printf("Error! cnt=%d\n", cnt);
exit(1);
}
else
{
printf("Correct! cnt=%d\n", cnt);
}
}
niters *= 10;
}
exit(0);
}
void *increase(void *vargp)
{
unsigned i, niters = (unsigned int)vargp;
for (i = 0; i < niters; i++)
{
cnt++;
}
return NULL;
}
void *decrease(void *vargp)
{
unsigned i, niters = (unsigned int)vargp;
for (i = 0; i < niters; i++)
{
cnt--;
}
return NULL;
}

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.

89
exp1/psum64.c Normal file
View File

@@ -0,0 +1,89 @@
/*任务4(必做编译、测试和运行示例程序psum64.c
1测量线程数为1、2、4、8、16时程序的执行时间计算加速比和效率并做出解释。
2改写该程序psum64.c,保存为task64.c实现计算02+12+… +(n-1)2功能。
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.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
void *sum(void *vargp);
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;
pthread_t tid[MAXTHREADS];
int myid[MAXTHREADS];
if(argc != 3)
{
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++)
{
myid[i] = i;
pthread_create(&tid[i],NULL,sum,&myid[i]);
}
for ( i = 0; i < nthreads; i++)
{
pthread_join(tid[i],NULL);
}
for ( i = 0; i < nthreads; i++)
{
result += psum64[i];
}
if (result == (nelems*(nelems-1))/2)
{
printf("Correct result: %llu\n",result);
}
else
{
printf("Wrong result: %llu\n",result);
}
double t_end = get_time();
printf("Time taken: %f seconds\n", t_end - t_start);
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;
for (i = begin; i < end; i++)
{
lsum += i;
}
psum64[myid] = lsum;
return NULL;
}

Binary file not shown.

View File

@@ -31,7 +31,7 @@ void *wokerT3(void *vargp)
for(int i=0; i<5; i++) for(int i=0; i<5; i++)
{ {
time_t t = time(NULL); time_t t = time(NULL);
printf("Current time %s\n",ctime(&t)); printf("Current time %s",ctime(&t));
int sleep_time = rand() % 5 + 1; int sleep_time = rand() % 5 + 1;
sleep(sleep_time); sleep(sleep_time);
} }
@@ -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);

BIN
exp1/task62 Executable file

Binary file not shown.

View File

@@ -5,33 +5,11 @@
#include <unistd.h> #include <unistd.h>
#include <semaphore.h> #include <semaphore.h>
void *increase(void *vargp);
void *decrease(void *vargp);
int cnt = 0; int cnt = 0;
sem_t mutex; sem_t mutex;
void *increase(void *vargp)
{
unsigned int niters = (unsigned int)vargp;
for (unsigned int i = 0; i < niters; i++)
{
sem_wait(&mutex);
cnt++;
sem_post(&mutex);
}
return NULL;
}
void *decrease(void *vargp)
{
unsigned int niters = (unsigned int)vargp;
for (unsigned int i = 0; i < niters; i++)
{
sem_wait(&mutex);
cnt--;
sem_post(&mutex);
}
return NULL;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
unsigned int niters; unsigned int niters;
@@ -61,3 +39,27 @@ int main(int argc, char **argv)
sem_destroy(&mutex); sem_destroy(&mutex);
exit(0); exit(0);
} }
void *increase(void *vargp)
{
unsigned i ,niters = (unsigned int)vargp;
for ( i = 0; i < niters; i++)
{
sem_wait(&mutex);
cnt++;
sem_post(&mutex);
}
return NULL;
}
void *decrease(void *vargp)
{
unsigned i, niters = (unsigned int)vargp;
for ( i = 0; i < niters; i++)
{
sem_wait(&mutex);
cnt--;
sem_post(&mutex);
}
return NULL;
}

BIN
exp1/task63 Executable file

Binary file not shown.

View File

@@ -1,3 +1,10 @@
/*
编写一个多线程程序task63.c创建k个生产者线程和m个消费者线程
每个生产者线程产生若干个随机数通过由N个单元构成的缓冲区发送给消费者线程
进行输出显示产生Pthread信号量实现生产者/消费者线程间同步,并且设计一种方案对程序正确性进行验证。
提示:一种非严谨的简单验证方案是,将生产者线程产生的所有随机数相加得到一个和,消费者线程接收到的所有随机数相加得到另一个和,验证两个和是否一致来来验证程序正确性
*/
#include <pthread.h> #include <pthread.h>
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
@@ -5,3 +12,181 @@
#include <unistd.h> #include <unistd.h>
#include <semaphore.h> #include <semaphore.h>
#define POISON -1
typedef struct {
int *buf;
int n;
int inpos;
int outpos;
sem_t mutex;
sem_t slots;
sem_t items;
} sbuf_t;
long long produced_sum = 0;
long long consumed_sum = 0;
sem_t sum_mutex;
int total_items;
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;
}
typedef struct {
sbuf_t *sp;
int num_items;
int id;
} producer_arg_t;
typedef struct
{
sbuf_t *sp;
int id;
} consumer_arg_t;
void *producer(void *arg)
{
producer_arg_t *pa = (producer_arg_t *)arg;
for (int i = 0; i < pa->num_items; i++)
{
int val = rand() % 1000;
sbuf_insert(pa->sp,val);
sem_wait(&sum_mutex);
produced_sum += val;
sem_post(&sum_mutex);
printf("[Producer %d] produced %d\n",pa->id,val);
}
return NULL;
}
void *consumer(void *arg)
{
consumer_arg_t *ca = (consumer_arg_t *)arg;
while(1){
int item = sbuf_remove(ca->sp);
if(item == POISON){
sbuf_insert(ca->sp, POISON);
break;
}
sem_wait(&sum_mutex);
consumed_sum += item;
sem_post(&sum_mutex);
printf("[Consumer %d] consumed %d\n",ca->id,item);
}
return NULL;
}
int main(int argc, char *argv[])
{
if (argc != 4)
{
fprintf(stderr,"Usage: %s <k_producers> <m_consumers> <N_buffer>\n",argv[0]);
return 1;
}
int k = atoi(argv[1]);
int m = atoi(argv[2]);
int N = atoi(argv[3]);
total_items = 0;
srand(time(NULL));
sem_init(&sum_mutex, 0, 1);
sbuf_t buf;
sbuf_init(&buf,N);
pthread_t *producers = malloc(k*sizeof(pthread_t));
pthread_t *consumers = malloc(m*sizeof(pthread_t));
producer_arg_t *pargs = malloc(k*sizeof(producer_arg_t));
consumer_arg_t *cargs = malloc(m*sizeof(consumer_arg_t));
for (int i = 0; i < k; i++)
{
pargs[i].sp = &buf;
pargs[i].num_items = rand() % 5 + 1; // 每个生产者产生1-10个随机数
pargs[i].id = i + 1;
total_items += pargs[i].num_items;
pthread_create(&producers[i],NULL,producer,&pargs[i]);
}
for (int i = 0; i < m; i++)
{
cargs[i].sp = &buf;
cargs[i].id = i + 1;
pthread_create(&consumers[i],NULL,consumer,&cargs[i]);
}
for (int i = 0; i < k; i++)
{
pthread_join(producers[i],NULL);
}
sbuf_insert(&buf, POISON);
for (int i = 0; i < m; i++)
{
pthread_join(consumers[i],NULL);
}
printf("\n=== Verifcation Results ===\n");
printf("Produced sum: %lld\n", produced_sum);
printf("Consumed sum: %lld\n", consumed_sum);
if (produced_sum == consumed_sum)
{
printf("Verification successful: produced sum matches consumed sum.\n");
}
else
{
printf("Verification failed: produced sum does not match consumed sum.\n");
}
free(producers);
free(consumers);
free(pargs);
free(cargs);
sbuf_deinit(&buf);
sem_destroy(&sum_mutex);
return 0;
}

BIN
exp1/task64 Executable file

Binary file not shown.

88
exp1/task64.c Normal file
View File

@@ -0,0 +1,88 @@
/*
任务4(必做编译、测试和运行示例程序psum64.c
1测量线程数为1、2、4、8、16时程序的执行时间计算加速比和效率并做出解释。
2改写该程序psum64.c,保存为task64.c实现计算0²+1²+…+(n-1)²功能。
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>
#define MAXTHREADS 16
void *sum(void *vargp);
double get_time()
{
struct timeval tv;
gettimeofday(&tv, NULL);
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;
pthread_t tid[MAXTHREADS];
int myid[MAXTHREADS];
if (argc != 3)
{
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++)
{
myid[i] = i;
pthread_create(&tid[i], NULL, sum, &myid[i]);
}
for (i = 0; i < nthreads; i++)
{
pthread_join(tid[i], NULL);
}
for (i = 0; i < nthreads; i++)
{
result += psum64[i];
}
/* 验证公式: 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);
}
else
{
printf("Wrong result: %llu\n", result);
}
double t_end = get_time();
printf("Time taken: %f seconds\n", t_end - t_start);
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;
for (i = begin; i < end; i++)
{
lsum += i * i;
}
psum64[myid] = lsum;
return NULL;
}

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

BIN
server-exp/cgi-bin/add Executable file

Binary file not shown.

27
server-exp/cgi-bin/add.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 8192
int main(void) {
char *buf, *p;
char content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments from standard input */
scanf("%d&%d", &n1, &n2);
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int) strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}

242
server-exp/common.c Normal file
View File

@@ -0,0 +1,242 @@
/* $begin common.c */
#include "common.h"
/*********************************************************************
* The Rio package - robust I/O functions
**********************************************************************/
/*
* rio_readn - robustly read n bytes (unbuffered)
*/
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readn */
/*
* rio_writen - robustly write n bytes (unbuffered)
*/
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nwritten = 0; /* and call write() again */
else
return -1; /* errorno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
/* $end rio_writen */
/*
* rio_read - This is a wrapper for the Unix read() function that
* transfers min(n, rio_cnt) bytes from an internal buffer to a user
* buffer, where n is the number of bytes requested by the user and
* rio_cnt is the number of unread bytes in the internal buffer. On
* entry, rio_read() refills the internal buffer via a call to
* read() if the internal buffer is empty.
*/
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* refill if buf is empty */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* interrupted by sig handler return */
return -1;
}
else if (rp->rio_cnt == 0) /* EOF */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
/* $end rio_read */
/*
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer
*/
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */
/*
* rio_readnb - Robustly read n bytes (buffered)
*/
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readnb */
/*
* rio_readlineb - robustly read a text line (buffered)
*/
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* error */
}
*bufp = 0;
return n;
}
/* $end rio_readlineb */
/**********************************
* Wrappers for robust I/O routines
**********************************/
/********************************
* Client/server helper functions
********************************/
/*
* open_client_sock - open connection to server at <hostname, port>
* and return a socket descriptor ready for reading and writing.
* Returns -1 and sets errno on Unix error.
* Returns -2 and sets h_errno on DNS (gethostbyname) error.
*/
/* $begin open_client_sock */
int open_client_sock(char *hostname, int port)
{
int client_sock;
struct hostent *hp;
struct sockaddr_in serveraddr;
if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1; /* check errno for cause of error */
/* Fill in the server's IP address and port */
if ((hp = gethostbyname(hostname)) == NULL)
return -2; /* check h_errno for cause of error */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)hp->h_addr_list[0],
(char *)&serveraddr.sin_addr.s_addr, hp->h_length);
serveraddr.sin_port = htons(port);
/* Establish a connection with the server */
if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
return client_sock;
}
/* $end open_client_sock */
/*
* open_listen_sock - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
/* $begin open_listen_sock */
int open_listen_sock(int port)
{
int listen_sock, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listen_sock will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listen_sock, LISTENQ) < 0)
return -1;
return listen_sock;
}
/* $end open_listen_sock */
/* $end wrapper.c */

76
server-exp/common.h Normal file
View File

@@ -0,0 +1,76 @@
/* $begin common.h */
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* defined by BIND for DNS errors */
extern char **environ; /* defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Client/server helper functions */
int open_client_sock(char *hostname, int portno);
int open_listen_sock(int portno);
#endif /* __COMMON_H__ */
/* $end common.h */

BIN
server-exp/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
server-exp/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
server-exp/ftpc Executable file

Binary file not shown.

109
server-exp/ftpc.c Normal file
View File

@@ -0,0 +1,109 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
ftp_file_t file;
FILE *fp;
long fileSize;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
char *token = strtok(buf, " \t\n"); // 提取FTP命令
if (strcmp(token, "get") == 0 || strcmp(token, "put") == 0) {
strncpy(file.cmd, token, MAXLINE - 1);
file.cmd[MAXLINE - 1] = '\0';
} else {
printf("Unsupported command: %s\n", token);
continue; // 如果不是get或put命令跳过处理
}
token = strtok(NULL, " \t\n"); // 提取文件名参数
if (token != NULL) {
strncpy(file.filename, token, MAXLINE - 1);
file.filename[MAXLINE - 1] = '\0';
} else {
printf("Filename is required for command: %s\n", file.cmd);
continue; // 如果没有提供文件名参数,跳过处理
}
if (strcmp(file.cmd,"put")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file");
continue;
} else {
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
}
send(client_sock, &file, sizeof(ftp_file_t), 0);
fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
} else {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
send(client_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
fclose(fp);
}
}
else if (strcmp(file.cmd,"get")==0)
{
send(client_sock, &file, sizeof(ftp_file_t), 0);
recv(client_sock, &file, sizeof(ftp_file_t), 0); // 接收文件信息
int fileSize = atoi(file.fileSize);
if (fileSize > 0) {
char *fileContent = malloc(fileSize); // 根据文件大小分配内存
recv(client_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp != NULL) {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
} else {
perror("Error creating file");
}
free(fileContent); // 释放内存
}
recv(client_sock, buf, MAXLINE, 0);
fputs(buf, stdout);
}
}
close(client_sock);
exit(0);
}

View File

BIN
server-exp/ftps Executable file

Binary file not shown.

101
server-exp/ftps.c Normal file
View File

@@ -0,0 +1,101 @@
#include "common.h"
typedef struct ftp_file
{
char cmd[MAXLINE]; // FTP命令
char filename[MAXLINE]; // 文件名参数
char fileSize[MAXLINE]; // 文件大小参数
} ftp_file_t;
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXBUF];
ftp_file_t file;
char filePath[MAXLINE] = "./"; // 假设当前目录下有文件
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, &file, sizeof(ftp_file_t),0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
if (strcmp(file.cmd,"put")==0)
{
int fileSize = atoi(file.fileSize);
char *fileContent = malloc(fileSize);
recv(conn_sock, fileContent, fileSize, 0); // 接收文件内容
FILE *fp = fopen(file.filename, "w");
if (fp == NULL) {
perror("Error opening file for writing");
} else {
fwrite(fileContent, 1, fileSize, fp); // 将接收到的内容写入文件
fclose(fp);
}
free(fileContent);
strcpy(buf, "File received successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
else if (strcmp(file.cmd,"get")==0)
{
strcat(filePath, file.filename); // 构建完整文件路径
FILE *fp = fopen(filePath, "r");
if (fp == NULL) {
perror("Error opening file for reading");
strcpy(file.fileSize, "0");
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送错误信息
} else {
fseek(fp, 0, SEEK_END);
long fileSize = ftell(fp);
fclose(fp);
snprintf(file.fileSize, MAXLINE, "%ld", fileSize); // 将文件大小转换为字符串并存储
send(conn_sock, &file, sizeof(ftp_file_t), 0); // 发送文件信息
fp = fopen(filePath, "r");
if (fp != NULL) {
char *fileContent = malloc(fileSize);
fread(fileContent, 1, fileSize, fp); // 从文件中读取内容到缓冲区
fclose(fp);
send(conn_sock, fileContent, fileSize, 0); // 发送文件内容
free(fileContent);
strcpy(buf, "File sent successfully\n");
send (conn_sock, buf, strlen(buf), 0);
}
}
}
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
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);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

View File

Binary file not shown.

13
server-exp/index.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<title>the example web</title>
</head>
<body>
<H1>webserver test page</H1>
<p>
Not pretty but should prove that webserver works:-)
<p>
<IMG SRC="example.jpg">
</body>
</html>

214
server-exp/perf.txt Normal file
View File

@@ -0,0 +1,214 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
28.57 0.10 0.10 1461740 68.41 116.30 rio_readlineb
25.71 0.19 0.09 365435 246.28 930.40 process_trans
20.00 0.26 0.07 29966110 2.34 2.34 rio_read
5.71 0.28 0.02 974534 20.52 20.52 rio_writen
5.71 0.30 0.02 243603 82.10 123.15 feed_static
5.71 0.32 0.02 121832 164.16 246.25 error_request
2.86 0.33 0.01 365435 27.36 376.26 read_requesthdrs
2.86 0.34 0.01 121832 82.08 82.08 parse_dynamic_uri
2.86 0.35 0.01 main
0.00 0.35 0.00 365435 0.00 0.00 is_static
0.00 0.35 0.00 365435 0.00 0.00 rio_readinitb
0.00 0.35 0.00 243603 0.00 0.00 get_filetype
0.00 0.35 0.00 243603 0.00 0.00 parse_static_uri
0.00 0.35 0.00 1 0.00 0.00 open_listen_sock
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 2.86% of 0.35 seconds
index % time self children called name
<spontaneous>
[1] 100.0 0.01 0.34 main [1]
0.09 0.25 365435/365435 process_trans [2]
0.00 0.00 1/1 open_listen_sock [14]
-----------------------------------------------
0.09 0.25 365435/365435 main [1]
[2] 97.1 0.09 0.25 365435 process_trans [2]
0.01 0.13 365435/365435 read_requesthdrs [4]
0.03 0.02 365435/1461740 rio_readlineb [3]
0.02 0.01 121832/121832 error_request [6]
0.02 0.01 243603/243603 feed_static [7]
0.01 0.00 121832/121832 parse_dynamic_uri [9]
0.00 0.00 365435/365435 rio_readinitb [11]
0.00 0.00 365435/365435 is_static [10]
0.00 0.00 243603/243603 parse_static_uri [13]
-----------------------------------------------
0.03 0.02 365435/1461740 process_trans [2]
0.07 0.05 1096305/1461740 read_requesthdrs [4]
[3] 48.6 0.10 0.07 1461740 rio_readlineb [3]
0.07 0.00 29966110/29966110 rio_read [5]
-----------------------------------------------
0.01 0.13 365435/365435 process_trans [2]
[4] 39.3 0.01 0.13 365435 read_requesthdrs [4]
0.07 0.05 1096305/1461740 rio_readlineb [3]
-----------------------------------------------
0.07 0.00 29966110/29966110 rio_readlineb [3]
[5] 20.0 0.07 0.00 29966110 rio_read [5]
-----------------------------------------------
0.02 0.01 121832/121832 process_trans [2]
[6] 8.6 0.02 0.01 121832 error_request [6]
0.01 0.00 487328/974534 rio_writen [8]
-----------------------------------------------
0.02 0.01 243603/243603 process_trans [2]
[7] 8.6 0.02 0.01 243603 feed_static [7]
0.01 0.00 487206/974534 rio_writen [8]
0.00 0.00 243603/243603 get_filetype [12]
-----------------------------------------------
0.01 0.00 487206/974534 feed_static [7]
0.01 0.00 487328/974534 error_request [6]
[8] 5.7 0.02 0.00 974534 rio_writen [8]
-----------------------------------------------
0.01 0.00 121832/121832 process_trans [2]
[9] 2.9 0.01 0.00 121832 parse_dynamic_uri [9]
-----------------------------------------------
0.00 0.00 365435/365435 process_trans [2]
[10] 0.0 0.00 0.00 365435 is_static [10]
-----------------------------------------------
0.00 0.00 365435/365435 process_trans [2]
[11] 0.0 0.00 0.00 365435 rio_readinitb [11]
-----------------------------------------------
0.00 0.00 243603/243603 feed_static [7]
[12] 0.0 0.00 0.00 243603 get_filetype [12]
-----------------------------------------------
0.00 0.00 243603/243603 process_trans [2]
[13] 0.0 0.00 0.00 243603 parse_static_uri [13]
-----------------------------------------------
0.00 0.00 1/1 main [1]
[14] 0.0 0.00 0.00 1 open_listen_sock [14]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function is in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Copyright (C) 2012-2026 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Index by function name
[6] error_request [14] open_listen_sock [5] rio_read (common.c)
[7] feed_static [9] parse_dynamic_uri [11] rio_readinitb
[12] get_filetype [13] parse_static_uri [3] rio_readlineb
[10] is_static [2] process_trans [8] rio_writen
[1] main [4] read_requesthdrs

8
server-exp/test.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>a simple page for testing tiny</title>
<head>
<body>
Hello World
</body>
</html>

1
server-exp/testfile.txt Normal file
View File

@@ -0,0 +1 @@
hello ftp test

BIN
server-exp/togglec Executable file

Binary file not shown.

29
server-exp/togglec.c Normal file
View File

@@ -0,0 +1,29 @@
#include "common.h"
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
send(client_sock, buf, strlen(buf),0);
recv(client_sock, buf, MAXLINE,0);
fputs(buf, stdout);
}
close(client_sock);
exit(0);
}

BIN
server-exp/togglesi Executable file

Binary file not shown.

57
server-exp/togglesi.c Normal file
View File

@@ -0,0 +1,57 @@
#include "common.h"
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
clientlen = sizeof(clientaddr);
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);
toggle(conn_sock,hit);
close(conn_sock);
}
exit(0);
}

3
server-exp/urls Normal file
View File

@@ -0,0 +1,3 @@
http://127.0.0.1:8088/index.html
http://127.0.0.1:8088/test.html
http://127.0.0.1:8088/cgi-bin/add?2025&523808

BIN
server-exp/webclient Executable file

Binary file not shown.

55
server-exp/webclient.c Normal file
View File

@@ -0,0 +1,55 @@
#include <arpa/inet.h> // 提供IP地址转换函数
#include <netinet/in.h> // 提供套接字地址结构定义
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库函数如exit()
#include <string.h> // 字符串操作函数
#include <sys/socket.h> // 套接字相关函数
#include <sys/types.h> // 数据类型定义
#include <unistd.h> // POSIX API如read()和write()
//#define PORT 8181 /* 目标服务器的端口号 */
//#define IP_ADDRESS "192.168.0.8" /* 目标服务器的IP地址 */
#define BUFSIZE 8196 /* 缓冲区大小 */
char *command = "GET /index.html HTTP/1.0 \r\n\r\n"; /* HTTP GET 请求命令 */
// 错误处理函数,打印错误信息并退出程序
void pexit(char *msg) {
perror(msg); // 打印错误信息
exit(1); // 退出程序
}
int main(int argc, char *argv[]) {//客户端启动命令为"./webclient 127.0.0.1 8088",argv[1]是IP地址argv[2]是端口号8088
int i, sockfd; // sockfd是套接字文件描述符
char buffer[BUFSIZE]; // 用于存储从服务器接收的数据
struct sockaddr_in serv_addr; // 定义服务器地址结构
// 打印尝试连接服务器的信息
printf("客户端尝试连接到 %s 和端口 %s\n", argv[1], argv[2]);
// 创建套接字使用IPv4和TCP协议
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) pexit("socket() 创建失败");
// 配置服务器地址
serv_addr.sin_family = AF_INET; // 地址族为IPv4
serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址,如"127.0.0.1"
serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号,如"8088"
// 尝试连接到服务器
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
pexit("connect() 连接失败");
// 发送HTTP GET请求到服务器
printf("发送字节数=%ld %s\n", strlen(command), command);
if (write(sockfd, command, strlen(command)) < 0) pexit("write() 发送请求失败");
// 循环读取服务器返回的数据,并输出到标准输出
while ((i = read(sockfd, buffer, BUFSIZE)) > 0) {
if (write(1, buffer, i) < 0) // 1表示标准输出
pexit("write() 输出到标准输出失败");
}
// 关闭套接字,释放资源
close(sockfd);
return 0; // 程序正常退出
}

BIN
server-exp/weblet Executable file

Binary file not shown.

278
server-exp/weblet.c Normal file
View File

@@ -0,0 +1,278 @@
#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
/* 全局变量说明 */
/* 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);
}
/* 设置Ctrl+C信号处理 */
if(signal(SIGINT,ctrlc_handler)==SIG_ERR)
(void)printf("错误: 无法设置SIGINT信号处理\n");
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);
process_trans(conn_sock,hit); // 处理HTTP事务
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]="http/1.1";
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
int rhn=0; // 请求头计数器
printf2("第%d次请求开始\n",hit);
/* 读取请求行和请求头 */
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);
printf3("请求头%d:%s", ++rhn, buf);
//判断第一个请求行是否正好有三个单词构成如GET / http/1.1
/*int wn=0; char *p=buf;
while(*p==' ' || *p=='\t') p++;
while(*p) {
if((*p==' '||*p=='\t') && *(p+1)!=' ') wn++;
p++;
}
if (wn==2) */
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]); // 关闭写端
}

View File

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

View File

@@ -0,0 +1,14 @@
common.h : 公共头文件、函数声明、常量定义等由教材wrapper.h简化而来
common.c : RIO函数库和打开网络连接函数源代码教材中libwrapper.c简化而来
webclient.c: web客户端源代码(《操作系统实验教程》第1章的client.c)
webserver.c: web服务器源代码(即教材《Linux编程》第8章的weblet.c)
togglec.c : toggle客户端源代码(教材第8章源代码
togglesi.c : 迭代式toggle服务器源代码教材《Linux编程》第8章源代码
./cgi-bin/add.c: 生成动态网页的cgi程序教材《Linux编程》第8章
Makefile: 目标和源代码间相互依赖关系文件参考教材《Linux编程》第3章
index.html: 缺省网页文件
example.jpg: 网页文件index.html中内嵌的图片
favicon.ico: index.html中内嵌的图标
test.html: 第2个测试用网页
urls: 记录运行http_load执行时访问哪些网址
gmon.out: grof命令生成的性能数据文件

81151
server-exp2/abc Normal file

File diff suppressed because it is too large Load Diff

BIN
server-exp2/cgi-bin/add Normal file

Binary file not shown.

27
server-exp2/cgi-bin/add.c Normal file
View File

@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 8192
int main(void) {
char *buf, *p;
char content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments from standard input */
scanf("%d&%d", &n1, &n2);
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int) strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}

243
server-exp2/common.c Normal file
View File

@@ -0,0 +1,243 @@
/* $begin common.c */
#include "common.h"
/*********************************************************************
* The Rio package - robust I/O functions
**********************************************************************/
/*
* rio_readn - robustly read n bytes (unbuffered)
*/
/* $begin rio_readn */
ssize_t rio_readn(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = read(fd, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* and call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readn */
/*
* rio_writen - robustly write n bytes (unbuffered)
*/
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nwritten = 0; /* and call write() again */
else
return -1; /* errorno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
/* $end rio_writen */
/*
* rio_read - This is a wrapper for the Unix read() function that
* transfers min(n, rio_cnt) bytes from an internal buffer to a user
* buffer, where n is the number of bytes requested by the user and
* rio_cnt is the number of unread bytes in the internal buffer. On
* entry, rio_read() refills the internal buffer via a call to
* read() if the internal buffer is empty.
*/
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
int cnt;
while (rp->rio_cnt <= 0) { /* refill if buf is empty */
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf,
sizeof(rp->rio_buf));
if (rp->rio_cnt < 0) {
if (errno != EINTR) /* interrupted by sig handler return */
return -1;
}
else if (rp->rio_cnt == 0) /* EOF */
return 0;
else
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */
}
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
cnt = n;
if (rp->rio_cnt < n)
cnt = rp->rio_cnt;
memcpy(usrbuf, rp->rio_bufptr, cnt);
rp->rio_bufptr += cnt;
rp->rio_cnt -= cnt;
return cnt;
}
/* $end rio_read */
/*
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer
*/
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd)
{
rp->rio_fd = fd;
rp->rio_cnt = 0;
rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */
/*
* rio_readnb - Robustly read n bytes (buffered)
*/
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n)
{
size_t nleft = n;
ssize_t nread;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nread = rio_read(rp, bufp, nleft)) < 0) {
if (errno == EINTR) /* interrupted by sig handler return */
nread = 0; /* call read() again */
else
return -1; /* errno set by read() */
}
else if (nread == 0)
break; /* EOF */
nleft -= nread;
bufp += nread;
}
return (n - nleft); /* return >= 0 */
}
/* $end rio_readnb */
/*
* rio_readlineb - robustly read a text line (buffered)
*/
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen)
{
int n, rc;
char c, *bufp = usrbuf;
for (n = 1; n < maxlen; n++) {
if ((rc = rio_read(rp, &c, 1)) == 1) {
*bufp++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
if (n == 1)
return 0; /* EOF, no data read */
else
break; /* EOF, some data was read */
} else
return -1; /* error */
}
*bufp = 0;
return n;
}
/* $end rio_readlineb */
/**********************************
* Wrappers for robust I/O routines
**********************************/
/********************************
* Client/server helper functions
********************************/
/*
* open_client_sock - open connection to server at <hostname, port>
* and return a socket descriptor ready for reading and writing.
* Returns -1 and sets errno on Unix error.
* Returns -2 and sets h_errno on DNS (gethostbyname) error.
*/
/* $begin open_client_sock */
int open_client_sock(char *hostname, int port)
{
int client_sock;
struct hostent *hp;
struct sockaddr_in serveraddr;
if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1; /* check errno for cause of error */
/* Fill in the server's IP address and port */
if ((hp = gethostbyname(hostname)) == NULL)
return -2; /* check h_errno for cause of error */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)hp->h_addr_list[0],
(char *)&serveraddr.sin_addr.s_addr, hp->h_length);
serveraddr.sin_port = htons(port);
/* Establish a connection with the server */
if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
return client_sock;
}
/* $end open_client_sock */
/*
* open_listen_sock - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
/* $begin open_listen_sock */
int open_listen_sock(int port)
{
int listen_sock, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listen_sock will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listen_sock, LISTENQ) < 0)
return -1;
return listen_sock;
}
/* $end open_listen_sock */
/* $end wrapper.c */

78
server-exp2/common.h Normal file
View File

@@ -0,0 +1,78 @@
/* $begin common.h */
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <stdbool.h>
/* Default file permissions are DEF_MODE & ~DEF_UMASK */
/* $begin createmasks */
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK S_IWGRP|S_IWOTH
/* $end createmasks */
/* Simplifies calls to bind(), connect(), and accept() */
/* $begin sockaddrdef */
typedef struct sockaddr SA;
/* $end sockaddrdef */
/* Persistent state for the robust I/O (Rio) package */
/* $begin rio_t */
#define RIO_BUFSIZE 8192
typedef struct {
int rio_fd; /* descriptor for this internal buf */
int rio_cnt; /* unread bytes in internal buf */
char *rio_bufptr; /* next unread byte in internal buf */
char rio_buf[RIO_BUFSIZE]; /* internal buffer */
} rio_t;
/* $end rio_t */
/* External variables */
extern int h_errno; /* defined by BIND for DNS errors */
extern char **environ; /* defined by libc */
/* Misc constants */
#define MAXLINE 8192 /* max text line length */
#define MAXBUF 8192 /* max I/O buffer size */
#define LISTENQ 1024 /* second argument to listen() */
/* Rio (Robust I/O) package */
ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);
void rio_readinitb(rio_t *rp, int fd);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
/* Client/server helper functions */
int open_client_sock(char *hostname, int portno);
int open_listen_sock(int portno);
#endif /* __COMMON_H__ */
/* $end common.h */

BIN
server-exp2/example.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
server-exp2/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

13
server-exp2/index.html Normal file
View File

@@ -0,0 +1,13 @@
<html>
<head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<title>the example web</title>
</head>
<body>
<H1>webserver test page</H1>
<p>
Not pretty but should prove that webserver works:-)
<p>
<IMG SRC="example.jpg">
</body>
</html>

200
server-exp2/perf.txt Normal file
View File

@@ -0,0 +1,200 @@
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls us/call us/call name
42.86 0.09 0.09 23039432 0.00 0.00 rio_read
28.57 0.15 0.06 15339432 0.00 0.01 rio_readlineb
9.52 0.17 0.02 100001 0.20 1.69 read_requesthdrs
9.52 0.19 0.02 100000 0.20 0.20 get_filetype
4.76 0.20 0.01 100001 0.10 2.10 process_trans
4.76 0.21 0.01 100001 0.10 0.10 rio_readinitb
0.00 0.21 0.00 200000 0.00 0.00 rio_writen
0.00 0.21 0.00 100000 0.00 0.20 feed_static
0.00 0.21 0.00 100000 0.00 0.00 is_static
0.00 0.21 0.00 100000 0.00 0.00 parse_static_uri
0.00 0.21 0.00 1 0.00 0.00 open_listen_sock
% the percentage of the total running time of the
time program used by this function.
cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.
self the number of seconds accounted for by this
seconds function alone. This is the major sort for this
listing.
calls the number of times this function was invoked, if
this function is profiled, else blank.
self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.
total the average number of milliseconds spent in this
ms/call function and its descendents per call, if this
function is profiled, else blank.
name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.
Copyright (C) 2012-2024 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Call graph (explanation follows)
granularity: each sample hit covers 4 byte(s) for 4.76% of 0.21 seconds
index % time self children called name
0.01 0.20 100001/100001 main [2]
[1] 100.0 0.01 0.20 100001 process_trans [1]
0.02 0.15 100001/100001 read_requesthdrs [3]
0.00 0.02 100000/100000 feed_static [6]
0.01 0.00 100001/100001 rio_readinitb [8]
0.00 0.00 100001/15339432 rio_readlineb [4]
0.00 0.00 100000/100000 is_static [10]
0.00 0.00 100000/100000 parse_static_uri [11]
-----------------------------------------------
<spontaneous>
[2] 100.0 0.00 0.21 main [2]
0.01 0.20 100001/100001 process_trans [1]
0.00 0.00 1/1 open_listen_sock [12]
-----------------------------------------------
0.02 0.15 100001/100001 process_trans [1]
[3] 80.5 0.02 0.15 100001 read_requesthdrs [3]
0.06 0.09 15239431/15339432 rio_readlineb [4]
-----------------------------------------------
0.00 0.00 100001/15339432 process_trans [1]
0.06 0.09 15239431/15339432 read_requesthdrs [3]
[4] 71.4 0.06 0.09 15339432 rio_readlineb [4]
0.09 0.00 23039432/23039432 rio_read [5]
-----------------------------------------------
0.09 0.00 23039432/23039432 rio_readlineb [4]
[5] 42.9 0.09 0.00 23039432 rio_read [5]
-----------------------------------------------
0.00 0.02 100000/100000 process_trans [1]
[6] 9.5 0.00 0.02 100000 feed_static [6]
0.02 0.00 100000/100000 get_filetype [7]
0.00 0.00 200000/200000 rio_writen [9]
-----------------------------------------------
0.02 0.00 100000/100000 feed_static [6]
[7] 9.5 0.02 0.00 100000 get_filetype [7]
-----------------------------------------------
0.01 0.00 100001/100001 process_trans [1]
[8] 4.8 0.01 0.00 100001 rio_readinitb [8]
-----------------------------------------------
0.00 0.00 200000/200000 feed_static [6]
[9] 0.0 0.00 0.00 200000 rio_writen [9]
-----------------------------------------------
0.00 0.00 100000/100000 process_trans [1]
[10] 0.0 0.00 0.00 100000 is_static [10]
-----------------------------------------------
0.00 0.00 100000/100000 process_trans [1]
[11] 0.0 0.00 0.00 100000 parse_static_uri [11]
-----------------------------------------------
0.00 0.00 1/1 main [2]
[12] 0.0 0.00 0.00 1 open_listen_sock [12]
-----------------------------------------------
This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.
Each entry in this table consists of several lines. The line with the
index number at the left hand margin lists the current function.
The lines above it list the functions that called this function,
and the lines below it list the functions this one called.
This line lists:
index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function is in the table.
% time This is the percentage of the `total' time that was spent
in this function and its children. Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.
self This is the total amount of time spent in this function.
children This is the total amount of time propagated into this
function by its children.
called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.
name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.
For the function's parents, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the function into this parent.
children This is the amount of time that was propagated from
the function's children into this parent.
called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.
name This is the name of the parent. The parent's index
number is printed after it. If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.
If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.
For the function's children, the fields have the following meanings:
self This is the amount of time that was propagated directly
from the child into the function.
children This is the amount of time that was propagated from the
child's children to the function.
called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.
name This is the name of the child. The child's index
number is printed after it. If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.
If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)
The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.
Copyright (C) 2012-2024 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.
Index by function name
[6] feed_static [11] parse_static_uri [8] rio_readinitb
[7] get_filetype [1] process_trans [4] rio_readlineb
[10] is_static [3] read_requesthdrs [9] rio_writen
[12] open_listen_sock [5] rio_read (common.c)

6
server-exp2/t1.txt Normal file
View File

@@ -0,0 +1,6 @@
hello
world
dgut
computer
Hello
WORLD

6
server-exp2/t2.txt Normal file
View File

@@ -0,0 +1,6 @@
hello
world
dgut
computer
Hello
WORLD

45
server-exp2/taskline.c Normal file
View File

@@ -0,0 +1,45 @@
/* task pool management fucntions */
/* task_pool_init */
#include "common.h"
#include "taskline.h"
void task_line_init(task_line_t *tlp, int n)
{
tlp->taskp = calloc(n, sizeof(task_t));
tlp->cnt = n; /* socks holds max of n items */
tlp->inpos= tlp->outpos = 0; /* Empty socks iff inpos== outpos */
sem_init(&tlp->mutex, 0, 1); /* Binary semaphore for locking */
sem_init(&tlp->avail, 0, tlp->cnt);/* Initially, socks has cnt empty cell */
sem_init(&tlp->ready, 0, 0); /* Initially, socks has zero data items */
}
/* Clean up task line */
void task_line_deinit(task_line_t *tlp)
{
free(tlp->taskp);
}
/* Insert item onto the rear of task line */
void task_insert (task_line_t *tlp, task_t item)
{ sem_wait(&tlp->avail); /* Wait for available cell */
sem_wait(&tlp->mutex); /* Lock the shared variable inpos pointer */
tlp->taskp[tlp->inpos] = item; /* Insert the item */
tlp->inpos =(tlp-> inpos +1)%(tlp->cnt); /* adjuset inpos point */
sem_post(&tlp->mutex); /* Unlock the buffer */
sem_post(&tlp->ready); /* Announce available item */
}
/* Remove and return the first item from task_pool */
task_t task_remove(task_line_t *tlp)
{
task_t item;
sem_wait(&tlp->ready); /* Wait for available item */
sem_wait(&tlp->mutex); /* Lock the shared pointer variable tp->outpos */
item = tlp->taskp[tlp->outpos]; /* Remove the item */
tlp->outpos=(tlp->outpos+1)%(tlp->cnt); /* adjuset outpos point */
sem_post(&tlp->avail); /* Announce available slot */
sem_post(&tlp->mutex); /* Unlock the shared pointer variable tp->outpos */
return item;
}

20
server-exp2/taskline.h Normal file
View File

@@ -0,0 +1,20 @@
typedef struct _task_t {
int conn_sock; //客户连接socket
int hit; //第几个客户
} task_t;
typedef struct {
task_t *taskp; /* Buffer array */
int cnt; /* Maximum number of cell */
int inpos; /* buf[inpos] is first available cell */
int outpos; /* buf[outpos] is fist item */
sem_t mutex; /* Protects accesses to socks */
sem_t avail; /* Counts available cells */
sem_t ready; /* Counts ready items */
} task_line_t;
/* task line wrapper functions */
void task_line_init(task_line_t *tlp, int n);
void task_line_deinit(task_line_t *tlp);
void task_insert(task_line_t *tlp, task_t item);
task_t task_remove(task_line_t *tlp);

8
server-exp2/test.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<title>a simple page for testing tiny</title>
<head>
<body>
Hello World
</body>
</html>

BIN
server-exp2/togglec Executable file

Binary file not shown.

30
server-exp2/togglec.c Normal file
View File

@@ -0,0 +1,30 @@
#include "common.h"
int main(int argc, char **argv)
{
int client_sock, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host><port>\n", argv[0]);
exit(1);
}
host = argv[1];
port = atoi(argv[2]);
client_sock = open_client_sock(host, port);
if(client_sock==-1) {
fputs("Error to connect the Server\n",stdout);
exit(1);
}
while (fgets(buf, MAXLINE, stdin) != NULL) {
//sleep(1);
send(client_sock, buf, strlen(buf),0);
recv(client_sock, buf, MAXLINE,0);
fputs(buf, stdout);
}
close(client_sock);
exit(0);
}

6
server-exp2/togglecm Normal file
View File

@@ -0,0 +1,6 @@
for i in {1..20}
do
./togglec localhost 12345 < $1
sleep 1
done

BIN
server-exp2/togglesp Executable file

Binary file not shown.

83
server-exp2/togglesp.c Normal file
View File

@@ -0,0 +1,83 @@
#include "common.h"
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);
}
//工作函数
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
socklen_t clientlen=sizeof(struct sockaddr_in);
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
//设置SIGHLD的信号处理函数用于收割结束的子进程
Signal(SIGCHLD, sigchld_handler);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for(hit=1; ; hit++) {
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);
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!) */
}
}

BIN
server-exp2/togglest Executable file

Binary file not shown.

86
server-exp2/togglest.c Normal file
View File

@@ -0,0 +1,86 @@
#include "common.h"
void toggle(int conn_sock, int hit);
void *serve_client(void *vargp);
typedef struct _client_data_t {
int conn_sock; //客户连接socket
int hit; //第几个客户
} client_data_t;
int main(int argc, char **argv)
{
int listen_sock, conn_sock, port,*conn_sock_p ;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
client_data_t *cdp; //连接客户信息
int hit; //连接客户计数
socklen_t clientlen=sizeof(struct sockaddr_in);
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
for (hit=1; ; hit++) {
cdp = malloc(sizeof(client_data_t));
cdp->conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
cdp->hit=hit;
/* 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);
pthread_create(&tid, NULL, serve_client, (void *)cdp);
}
}
/* 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);
toggle(cd.conn_sock,cd.hit);
close(conn_sock);
return NULL;
}
//工作函数
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
printf("第%d个客户通信开始\n",hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
send (conn_sock, buf, n, 0);
}
printf("第%d个客户通信结束\n",hit);
}

104
server-exp2/togglest_pool.c Normal file
View File

@@ -0,0 +1,104 @@
#include "common.h"
#include "pool.h"
#define NTHREADS 4
#define SBUFSIZE 16
void toggle(int conn_sock,int hit);
void *serve_client(void *vargp);
void toggle(int conn_sock,int hit)
{
size_t n; int i,no=0;
char buf[MAXLINE];
//printf("线程%d服务第%d个客户请求通信开始\n",tid,hit);
while((n =recv(conn_sock, buf, MAXLINE,0))> 0) {
printf("toggle服务器收到第%d个客户第%d个消息,长度为%d字节\n", hit,++no,(int)n);
for(i=0; i<n; i++)
if(isupper(buf[i]))
buf[i]=tolower(buf[i]);
else if(islower(buf[i]))
buf[i]=toupper(buf[i]);
sleep(1);
send (conn_sock, buf, n, 0);
}
//printf("线程%d服务第%d个客户请求通信结束\n",tid,hit);
}
//示例任务
void handle_request(int sock, int taskid, int tid) {
printf("线程%d服务第%d个客户通信开始\n",tid,taskid);
//usleep(1);
toggle(sock,taskid);
printf("线程%d服务第%d个客户通信结束\n",tid,taskid);
close(sock);
}
int main(int argc, char **argv) {
int listen_sock, conn_sock, port,i;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
int hit;
int nth[NTHREADS];
socklen_t clientlen=sizeof(struct sockaddr_in);
pthread_t tid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
port = atoi(argv[1]);
listen_sock = open_listen_sock(port);
if( listen_sock==-1) {
printf("端口号%d繁忙\n",port);
exit(1);
}
printf("start main\n");
// 初始化线程池
threadpool* pool = initThreadPool(NTHREADS);
// 创建示例任务并将其添加到线程池
for (hit=1; ; hit++) {
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);
task* new_task = (task*)malloc(sizeof(task));
new_task->function = handle_request; // 假设任务函数为 toggle
new_task->taskid = hit; // 任务参数,假设为 socket fd 或其他参数
new_task->sock=conn_sock;
addTaskToThreadPool(pool, new_task); /* Insert conn_sock in task pool */
}
// 等待线程池中的所有任务执行完毕
waitThreadPool(pool);
// 销毁线程池
destroyThreadPool(pool);
printf("stop main\n");
return 0;
}

Some files were not shown because too many files have changed in this diff Show More