Compare commits
7 Commits
fa2f3e2413
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 25f7ef1967 | |||
| 2f42b036a9 | |||
| 43902c20e4 | |||
| 1132d9e9a7 | |||
| 119f0e7b3a | |||
| f679b67969 | |||
| 56a4233098 |
BIN
AI-work/2024414290124-吕锦中-lab6.tar.gz
Normal file
BIN
AI-work/2024414290124-吕锦中-lab6.tar.gz
Normal file
Binary file not shown.
BIN
AI-work/matmult
Executable file
BIN
AI-work/matmult
Executable file
Binary file not shown.
151
AI-work/matmult.c
Normal file
151
AI-work/matmult.c
Normal 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
BIN
AI-work/task61
Executable file
Binary file not shown.
50
AI-work/task61.c
Normal file
50
AI-work/task61.c
Normal 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
BIN
AI-work/task62
Executable file
Binary file not shown.
46
AI-work/task62.c
Normal file
46
AI-work/task62.c
Normal 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
BIN
AI-work/task63
Executable file
Binary file not shown.
172
AI-work/task63.c
Normal file
172
AI-work/task63.c
Normal 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
BIN
AI-work/task64
Executable file
Binary file not shown.
80
AI-work/task64.c
Normal file
80
AI-work/task64.c
Normal 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
BIN
AI-work/task66
Executable file
Binary file not shown.
62
AI-work/task66.c
Normal file
62
AI-work/task66.c
Normal 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
BIN
AI-work/task67
Executable file
Binary file not shown.
178
AI-work/task67.c
Normal file
178
AI-work/task67.c
Normal 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;
|
||||||
|
}
|
||||||
375
AI-work/实验报告_Linux多线程编程.txt
Normal file
375
AI-work/实验报告_Linux多线程编程.txt
Normal 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/WSL2),GCC编译器,pthread线程库。
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
四、报告内容
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务1:task61.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秒随机变化。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务2:task62.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多大,使用信号量保护后结果始终正确。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务3:task63.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的更新。
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
任务4:task64.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服务器的线程池、数据库连接池等)。
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
21
exp0.5/.claude/settings.local.json
Normal file
21
exp0.5/.claude/settings.local.json
Normal 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
28
exp0.5/.vscode/tasks.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
BIN
exp0.5/Linux进程控制编程(2).docx
Normal file
BIN
exp0.5/Linux进程控制编程(2).docx
Normal file
Binary file not shown.
54
exp0.5/task51.c
Normal file
54
exp0.5/task51.c
Normal 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
259
exp0.5/task52.c
Normal 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
294
exp0.5/task52_readable.c
Normal 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
197
exp0.5/task53.c
Normal 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
205
exp0.5/task53_ai.c
Normal 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
243
exp0.5/task54.c
Normal 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
257
exp0.5/task54_ai.c
Normal 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;
|
||||||
|
}
|
||||||
BIN
exp1/Linux多线程编程实验(2).docx
Normal file
BIN
exp1/Linux多线程编程实验(2).docx
Normal file
Binary file not shown.
BIN
exp1/Linux多线程编程实验(2)_completed.docx
Normal file
BIN
exp1/Linux多线程编程实验(2)_completed.docx
Normal file
Binary file not shown.
BIN
exp1/badcount
BIN
exp1/badcount
Binary file not shown.
61
exp1/badcount.c
Normal file
61
exp1/badcount.c
Normal 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;
|
||||||
|
}
|
||||||
603
exp1/create_completed_doc.js
Normal file
603
exp1/create_completed_doc.js
Normal file
@@ -0,0 +1,603 @@
|
|||||||
|
const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
||||||
|
Header, Footer, AlignmentType, LevelFormat, BorderStyle,
|
||||||
|
WidthType, ShadingType, VerticalAlign, PageNumber, PageBreak } = require('docx');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Border style
|
||||||
|
const border = { style: BorderStyle.SINGLE, size: 1, color: "000000" };
|
||||||
|
const borders = { top: border, bottom: border, left: border, right: border };
|
||||||
|
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
|
||||||
|
const noBorders = { top: noBorder, bottom: noBorder, left: noBorder, right: noBorder };
|
||||||
|
|
||||||
|
// Helper function to create a paragraph with text
|
||||||
|
function createParagraph(text, options = {}) {
|
||||||
|
const { fontSize = 24, bold = false, alignment = AlignmentType.LEFT,
|
||||||
|
firstLine = 0, indent = 0, spaceBefore = 0, spaceAfter = 0,
|
||||||
|
font = "宋体", eastAsia = "宋体" } = options;
|
||||||
|
|
||||||
|
return new Paragraph({
|
||||||
|
alignment,
|
||||||
|
spacing: {
|
||||||
|
before: spaceBefore,
|
||||||
|
after: spaceAfter,
|
||||||
|
line: 480,
|
||||||
|
lineRule: "auto"
|
||||||
|
},
|
||||||
|
indent: {
|
||||||
|
firstLineChars: firstLine,
|
||||||
|
firstLine: firstLine > 0 ? firstLine * 10 : 0
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text,
|
||||||
|
font,
|
||||||
|
size: fontSize,
|
||||||
|
bold,
|
||||||
|
eastAsia
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for section heading
|
||||||
|
function createSectionHeading(text) {
|
||||||
|
return new Paragraph({
|
||||||
|
spacing: { before: 240, after: 120 },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text,
|
||||||
|
font: "宋体",
|
||||||
|
size: 28,
|
||||||
|
bold: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for subsection heading
|
||||||
|
function createSubHeading(text) {
|
||||||
|
return new Paragraph({
|
||||||
|
spacing: { before: 180, after: 80 },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text,
|
||||||
|
font: "宋体",
|
||||||
|
size: 24,
|
||||||
|
bold: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for normal paragraph with indentation
|
||||||
|
function createNormalParagraph(text, indent = 480) {
|
||||||
|
return new Paragraph({
|
||||||
|
spacing: { line: 480, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 200, firstLine: indent },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text,
|
||||||
|
font: "宋体",
|
||||||
|
size: 24
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the document
|
||||||
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
|
default: {
|
||||||
|
document: {
|
||||||
|
run: { font: "宋体", size: 24 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sections: [{
|
||||||
|
properties: {
|
||||||
|
page: {
|
||||||
|
size: { width: 11907, height: 16840 },
|
||||||
|
margin: { top: 1134, right: 567, bottom: 1134, left: 567 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
// Title
|
||||||
|
new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
spacing: { before: 0, after: 0 },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "网络空间安全学院",
|
||||||
|
font: "华文中宋",
|
||||||
|
size: 32
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
spacing: { before: 0, after: 0 },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "实验报告(电子版)",
|
||||||
|
font: "华文中宋",
|
||||||
|
size: 32
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Info table
|
||||||
|
new Table({
|
||||||
|
width: { size: 10411, type: WidthType.DXA },
|
||||||
|
columnWidths: [2016, 1461, 1800, 2224, 1318, 1592],
|
||||||
|
rows: [
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 2016, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "实验名称", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 5485, type: WidthType.DXA },
|
||||||
|
columnSpan: 3,
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [
|
||||||
|
new TextRun({ text: "Linux", font: "宋体", size: 24 }),
|
||||||
|
new TextRun({ text: "多线程编程", font: "宋体", size: 24 })
|
||||||
|
]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1318, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "指导教师", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1592, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({ children: [] })]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new TableRow({
|
||||||
|
children: [
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 2016, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "姓 名", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1461, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "吕锦中", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1800, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "学 号", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 2224, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "2024414290124", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1318, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "班 级", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
}),
|
||||||
|
new TableCell({
|
||||||
|
borders,
|
||||||
|
width: { size: 1592, type: WidthType.DXA },
|
||||||
|
verticalAlign: VerticalAlign.CENTER,
|
||||||
|
children: [new Paragraph({
|
||||||
|
alignment: AlignmentType.CENTER,
|
||||||
|
children: [new TextRun({ text: "软件工程一班", font: "宋体", size: 24 })]
|
||||||
|
})]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Section 1: 实验目的
|
||||||
|
createSectionHeading("一、实验目的:"),
|
||||||
|
createNormalParagraph("1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;"),
|
||||||
|
createNormalParagraph("2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;"),
|
||||||
|
createNormalParagraph("3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,建立进程和线程管理性能概念;"),
|
||||||
|
createNormalParagraph("4. 编写动态线程管理程序,建立负载均衡管理的初步概念。"),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Section 2: 实验内容
|
||||||
|
createSectionHeading("二、实验内容:"),
|
||||||
|
createNormalParagraph("任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。"),
|
||||||
|
createNormalParagraph("任务2(必做):编译、测试和运行badcount.c程序,用信号量方法改写程序实现对共享变量的安全访问。"),
|
||||||
|
createNormalParagraph("任务3(必做):编写多线程生产者/消费者程序task63.c,使用信号量实现同步。"),
|
||||||
|
createNormalParagraph("任务4(必做):编译、测试和运行psum64.c,实现并行计算平方和。"),
|
||||||
|
createNormalParagraph("任务5(选做):编写矩阵乘法并行程序matmult.c并验证正确性。"),
|
||||||
|
createNormalParagraph("任务6(任选):测量fork()与pthread_create()函数调用开销。"),
|
||||||
|
createNormalParagraph("任务7(任选):编写动态线程池管理程序task67.c。"),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Section 3: 相关情况介绍
|
||||||
|
createSectionHeading("三、相关情况介绍"),
|
||||||
|
createNormalParagraph("本实验使用Linux系统提供的POSIX线程库pthread进行多线程编程。实验中涉及线程创建、线程同步(信号量)、互斥锁、条件变量等概念。通过多个实际任务,涵盖了多线程编程的主要方面。"),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Section 4: 报告内容
|
||||||
|
createSectionHeading("四、报告内容"),
|
||||||
|
|
||||||
|
// Task 1
|
||||||
|
createSubHeading("任务1:多线程创建与基本同步"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("本程序创建三个对等线程T1、T2、T3,每个线程执行不同的输出任务。主线程使用pthread_join()等待所有对等线程结束。"),
|
||||||
|
createSubHeading("2. 源代码"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "#include <pthread.h>", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "#include <time.h>", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "// ... (完整源代码见task61.c)", font: "宋体", size: 22 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task61 task61.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据"),
|
||||||
|
createNormalParagraph("程序无输入参数,直接运行。"),
|
||||||
|
createSubHeading("5. 运行结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "My name is Lvjinzhong", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "My student number is 2024414290124", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Current time Fri May 29 11:32:12 2026", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 2
|
||||||
|
createSubHeading("任务2:信号量实现互斥访问"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("原badcount.c程序存在竞态条件,多个线程同时对共享变量cnt进行增减操作,导致结果不正确。本程序使用二元信号量mutex实现对cnt变量的互斥访问保护。"),
|
||||||
|
createSubHeading("2. 源代码(核心部分)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "sem_t mutex;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "sem_init(&mutex, 0, 1);", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "// 在increase/decrease函数中:", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "sem_wait(&mutex); cnt++; sem_post(&mutex);", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task62 task62.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据"),
|
||||||
|
createNormalParagraph("运行时指定迭代次数,如:./task62 100000"),
|
||||||
|
createSubHeading("5. 运行结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Correct! cnt=0", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 3
|
||||||
|
createSubHeading("任务3:生产者-消费者问题"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("使用有界缓冲区实现生产者-消费者同步。slots信号量表示缓冲区空槽数,items信号量表示缓冲区产品数,mutex保护缓冲区的临界区操作。"),
|
||||||
|
createSubHeading("2. 源代码(核心数据结构)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "typedef struct {", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " int *buf; int n; int inpos, outpos;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " sem_t mutex, slots, items;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "} sbuf_t;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task63 task63.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据"),
|
||||||
|
createNormalParagraph("./task63 2 3 5 表示2个生产者、3个消费者、缓冲区大小为5"),
|
||||||
|
createSubHeading("5. 运行结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Produced sum: 2765", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Consumed sum: 2765", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Verification successful", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 4
|
||||||
|
createSubHeading("任务4:并行计算平方和"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("将0到n-1的平方和分配给多个线程并行计算。每个线程计算自己分配区间的平方和,最后将各线程的结果求和。"),
|
||||||
|
createSubHeading("2. 源代码(核心部分)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "void *sum(void *vargp) {", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " unsigned long long begin = myid * nelems_per_thread;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " for (i = begin; i < end; i++) lsum += i * i;", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "}", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task64 task64.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据与结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " Threads=1: Time=0.001945s, result=549755289600", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " Threads=2: Time=0.001522s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " Threads=4: Time=0.001612s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " Threads=8: Time=0.001587s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " Threads=16: Time=0.002894s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 5
|
||||||
|
createSubHeading("任务5:矩阵乘法并行化"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("将N×N矩阵按行分配给多个线程并行计算。每个线程计算其负责的行与其他列的乘积,最后汇总结果。"),
|
||||||
|
createSubHeading("2. 源代码(核心部分)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "for (i = start_row; i < end_row; i++)", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " for (j = 0; j < N; j++)", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " C[i][j] = sum(A[i][k] * B[k][j]);", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o matmult matmult.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据与结果(N=128, 4线程)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Serial time: 0.010762s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Parallel time: 0.004444s", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Speedup: 2.42, Efficiency: 0.605", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 6
|
||||||
|
createSubHeading("任务6:进程与线程创建开销比较"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("通过反复调用fork()和pthread_create()各1000次,计算平均每次调用的执行时间。fork需要复制进程资源,而pthread_create只创建轻量级线程。"),
|
||||||
|
createSubHeading("2. 源代码(核心部分)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "// fork测量", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "pid_t pid = fork(); if(pid==0) _exit(0); waitpid(pid,NULL,0);", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "// pthread_create测量", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "pthread_create(&tid, NULL, func, NULL); pthread_join(tid, NULL);", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task66 task66.c -lpthread"),
|
||||||
|
createSubHeading("4. 运行结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "pthread_create() + pthread_join(): 0.178 ms", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "fork() + waitpid(): 0.549 ms", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "fork比pthread_create慢约3倍", font: "宋体", size: 22 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Task 7
|
||||||
|
createSubHeading("任务7:动态线程池管理"),
|
||||||
|
createSubHeading("1. 设计思想"),
|
||||||
|
createNormalParagraph("主线程预先创建工作线程,通过有界缓冲区接收任务。当缓冲区满时,工作线程数翻倍;当缓冲区空时,工作线程数减半。"),
|
||||||
|
createSubHeading("2. 源代码(核心部分)"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "void *worker(void *arg) {", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: " while(1) { task = sbuf_remove(&buf); sleep(task); }", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "}", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
createSubHeading("3. 编译过程"),
|
||||||
|
createNormalParagraph("gcc -o task67 task67.c -lpthread"),
|
||||||
|
createSubHeading("4. 测试数据"),
|
||||||
|
createNormalParagraph("运行后输入命令:10 5 表示创建10个任务,每个任务执行5秒"),
|
||||||
|
createSubHeading("5. 运行结果"),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Dynamic Thread Pool", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "Buffer size: 20, Initial threads: 5", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "> 10 5", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { line: 360, lineRule: "auto" },
|
||||||
|
indent: { firstLineChars: 100, firstLine: 200 },
|
||||||
|
children: [new TextRun({ text: "[Worker 0] executing task: sleep 5 seconds", font: "Courier New", size: 20 })]
|
||||||
|
}),
|
||||||
|
new Paragraph({ children: [] }),
|
||||||
|
|
||||||
|
// Section 5: 实验分析与总结
|
||||||
|
createSectionHeading("五、实验分析与总结"),
|
||||||
|
createSubHeading("任务2分析:互斥锁的正确性"),
|
||||||
|
createNormalParagraph("原badcount.c程序存在严重的竞态条件。当多个线程同时执行cnt++和cnt--时,操作不是原子的,可能导致计数错误。使用信号量实现互斥访问后,确保了同一时刻只有一个线程能修改共享变量,程序输出cnt=0,说明同步机制正确有效。"),
|
||||||
|
createSubHeading("任务3分析:生产者-消费者同步"),
|
||||||
|
createNormalParagraph("本程序使用三个信号量实现完整同步:slots计数空槽数,items计数产品数,mutex保护缓冲区。验证方案通过比较生产者产生的随机数之和与消费者接收的随机数之和来验证正确性。实验结果显示两者相等,证明同步机制工作正常。"),
|
||||||
|
createSubHeading("任务4分析:并行计算加速比"),
|
||||||
|
createNormalParagraph("从测试结果看,线程数从1增加到2时,执行时间从0.001945s降至0.001522s,有明显加速。但线程数继续增加时,加速效果不明显,甚至在16线程时时间增加。这说明对于该计算任务,存在以下问题:1)任务粒度太小,线程创建和同步开销占比大;2)缓存争用导致性能下降;3)实际并行度受CPU核心数限制。"),
|
||||||
|
createSubHeading("任务5分析:矩阵乘法并行效率"),
|
||||||
|
createNormalParagraph("N=128的矩阵乘法,使用4线程获得了2.42的加速比,效率为0.605。这说明并行化有一定效果,但由于矩阵不大,线程通信和同步开销仍占一定比例。随着矩阵规模增大和线程数增加,效率会进一步提升。"),
|
||||||
|
createSubHeading("任务6分析:进程与线程创建开销"),
|
||||||
|
createNormalParagraph("fork()创建进程需要复制整个进程空间,包括内存描述符、文件描述符等资源,而pthread_create()只需创建一个轻量级线程,共享进程资源。实验结果显示fork()的开销约为pthread_create()的3倍,符合理论预期。"),
|
||||||
|
createSubHeading("任务7分析:动态线程池"),
|
||||||
|
createNormalParagraph("动态线程池通过根据负载动态调整工作线程数来提高资源利用率。缓冲区满时增加线程,缓冲区空时减少线程。但实际测试中发现线程数调整可能导致任务重新分配的问题,需要更精细的同步机制。"),
|
||||||
|
createSubHeading("总结"),
|
||||||
|
createNormalParagraph("本次实验系统地练习了Linux多线程编程的各个方面,包括线程创建与同步、互斥访问、生产者-消费者问题、并行计算性能分析等。通过实际编程和测试,加深了对线程同步机制和并行程序性能特征的理解。")
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate the document
|
||||||
|
Packer.toBuffer(doc).then(buffer => {
|
||||||
|
fs.writeFileSync("/home/cho/C/C-exp-collection/exp1/Linux多线程编程实验(2)_completed.docx", buffer);
|
||||||
|
console.log("Document created successfully!");
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("Error creating document:", err);
|
||||||
|
});
|
||||||
327
exp1/create_completed_doc.py
Normal file
327
exp1/create_completed_doc.py
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from docx import Document
|
||||||
|
from docx.shared import Pt, Inches, Cm, Twips
|
||||||
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||||
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||||
|
from docx.oxml.ns import qn
|
||||||
|
from docx.oxml import OxmlElement
|
||||||
|
|
||||||
|
def set_cell_border(cell, **kwargs):
|
||||||
|
"""Set cell borders"""
|
||||||
|
tc = cell._tc
|
||||||
|
tcPr = tc.get_or_add_tcPr()
|
||||||
|
tcBorders = OxmlElement('w:tcBorders')
|
||||||
|
for edge in ('top', 'left', 'bottom', 'right'):
|
||||||
|
if edge in kwargs:
|
||||||
|
element = OxmlElement(f'w:{edge}')
|
||||||
|
element.set(qn('w:val'), kwargs[edge].get('val', 'single'))
|
||||||
|
element.set(qn('w:sz'), str(kwargs[edge].get('sz', 4)))
|
||||||
|
element.set(qn('w:color'), kwargs[edge].get('color', '000000'))
|
||||||
|
tcBorders.append(element)
|
||||||
|
tcPr.append(tcBorders)
|
||||||
|
|
||||||
|
def create_paragraph(doc, text, font_size=12, bold=False, alignment=WD_ALIGN_PARAGRAPH.LEFT, first_line_chars=0, first_line=0):
|
||||||
|
"""Create a paragraph with specified formatting"""
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
p.alignment = alignment
|
||||||
|
# Set paragraph spacing
|
||||||
|
p.paragraph_format.line_spacing = Pt(18)
|
||||||
|
if first_line_chars > 0:
|
||||||
|
p.paragraph_format.first_line_chars = first_line_chars
|
||||||
|
if first_line > 0:
|
||||||
|
p.paragraph_format.first_line_indent = Pt(first_line)
|
||||||
|
|
||||||
|
run = p.add_run(text)
|
||||||
|
run.font.size = Pt(font_size)
|
||||||
|
run.font.bold = bold
|
||||||
|
run.font.name = '宋体'
|
||||||
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
||||||
|
return p
|
||||||
|
|
||||||
|
def create_heading(doc, text, level=1):
|
||||||
|
"""Create a section heading"""
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
p.paragraph_format.line_spacing = Pt(18)
|
||||||
|
p.paragraph_format.space_before = Pt(12 if level == 1 else 6)
|
||||||
|
p.paragraph_format.space_after = Pt(6 if level == 1 else 3)
|
||||||
|
|
||||||
|
run = p.add_run(text)
|
||||||
|
run.font.size = Pt(14 if level == 1 else 12)
|
||||||
|
run.font.bold = True
|
||||||
|
run.font.name = '宋体'
|
||||||
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
||||||
|
return p
|
||||||
|
|
||||||
|
def create_code_paragraph(doc, text):
|
||||||
|
"""Create a paragraph with monospace font for code"""
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
p.paragraph_format.line_spacing = Pt(14)
|
||||||
|
p.paragraph_format.first_line_indent = Pt(0)
|
||||||
|
|
||||||
|
run = p.add_run(text)
|
||||||
|
run.font.size = Pt(10)
|
||||||
|
run.font.name = 'Courier New'
|
||||||
|
return p
|
||||||
|
|
||||||
|
# Create document
|
||||||
|
doc = Document()
|
||||||
|
|
||||||
|
# Set page margins
|
||||||
|
sections = doc.sections
|
||||||
|
for section in sections:
|
||||||
|
section.top_margin = Inches(0.8)
|
||||||
|
section.bottom_margin = Inches(0.8)
|
||||||
|
section.left_margin = Inches(0.4)
|
||||||
|
section.right_margin = Inches(0.4)
|
||||||
|
|
||||||
|
# Title
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||||
|
run = p.add_run("网络空间安全学院")
|
||||||
|
run.font.size = Pt(16)
|
||||||
|
run.font.bold = True
|
||||||
|
run.font.name = '华文中宋'
|
||||||
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
||||||
|
|
||||||
|
p = doc.add_paragraph()
|
||||||
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||||
|
run = p.add_run("实验报告(电子版)")
|
||||||
|
run.font.size = Pt(16)
|
||||||
|
run.font.bold = True
|
||||||
|
run.font.name = '华文中宋'
|
||||||
|
run._element.rPr.rFonts.set(qn('w:eastAsia'), '华文中宋')
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Info table
|
||||||
|
table = doc.add_table(rows=3, cols=6)
|
||||||
|
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||||
|
|
||||||
|
# Row 1: Experiment name
|
||||||
|
row = table.rows[0]
|
||||||
|
row.cells[0].text = "实验名称"
|
||||||
|
row.cells[1].merge(row.cells[2]).merge(row.cells[3])
|
||||||
|
row.cells[1].text = "Linux多线程编程"
|
||||||
|
row.cells[4].text = "指导教师"
|
||||||
|
row.cells[5].text = ""
|
||||||
|
|
||||||
|
# Row 2: Name, ID, Class
|
||||||
|
row = table.rows[1]
|
||||||
|
row.cells[0].text = "姓 名"
|
||||||
|
row.cells[1].text = "吕锦中"
|
||||||
|
row.cells[2].text = "学 号"
|
||||||
|
row.cells[3].text = "2024414290124"
|
||||||
|
row.cells[4].text = "班 级"
|
||||||
|
row.cells[5].text = "软件工程一班"
|
||||||
|
|
||||||
|
# Row 3: Location, Date, Group
|
||||||
|
row = table.rows[2]
|
||||||
|
row.cells[0].text = "实验地点"
|
||||||
|
row.cells[1].text = ""
|
||||||
|
row.cells[2].text = "实验日期"
|
||||||
|
row.cells[3].text = ""
|
||||||
|
row.cells[4].text = "同组同学"
|
||||||
|
row.cells[5].text = ""
|
||||||
|
|
||||||
|
# Format table cells
|
||||||
|
for row in table.rows:
|
||||||
|
for cell in row.cells:
|
||||||
|
for p in cell.paragraphs:
|
||||||
|
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||||
|
for run in p.runs:
|
||||||
|
run.font.size = Pt(12)
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Section 1: 实验目的
|
||||||
|
create_heading(doc, "一、实验目的:")
|
||||||
|
create_paragraph(doc, "1. 通过编程训练,掌握多线程编程、线程间互斥/同步编程基本方法;")
|
||||||
|
create_paragraph(doc, "2. 通过应用编程,掌握多线程并行程序设计与性能分析方法;")
|
||||||
|
create_paragraph(doc, "3. 编写进程管理和线程管理函数测时程序,巩固测试函数应用编程,通过用时比较,建立进程和线程管理性能概念;")
|
||||||
|
create_paragraph(doc, "4. 编写动态线程管理程序,建立负载均衡管理的初步概念。")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Section 2: 实验内容
|
||||||
|
create_heading(doc, "二、实验内容:")
|
||||||
|
create_paragraph(doc, "任务1(必做):编写程序task61.c,主线程创建3个对等线程T1、T2、T3,每个线程利用循环执行5次printf输出操作,两次循环间随机等待1-5s时间。主线程等待所有对等线程结束后终止进程。")
|
||||||
|
create_paragraph(doc, "任务2(必做):编译、测试和运行badcount.c程序,用信号量方法改写程序实现对共享变量的安全访问。")
|
||||||
|
create_paragraph(doc, "任务3(必做):编写多线程生产者/消费者程序task63.c,使用信号量实现同步。")
|
||||||
|
create_paragraph(doc, "任务4(必做):编译、测试和运行psum64.c,实现并行计算平方和。")
|
||||||
|
create_paragraph(doc, "任务5(选做):编写矩阵乘法并行程序matmult.c并验证正确性。")
|
||||||
|
create_paragraph(doc, "任务6(任选):测量fork()与pthread_create()函数调用开销。")
|
||||||
|
create_paragraph(doc, "任务7(任选):编写动态线程池管理程序task67.c。")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Section 3: 相关情况介绍
|
||||||
|
create_heading(doc, "三、相关情况介绍")
|
||||||
|
create_paragraph(doc, "本实验使用Linux系统提供的POSIX线程库pthread进行多线程编程。实验中涉及线程创建、线程同步(信号量)、互斥锁、条件变量等概念。通过多个实际任务,涵盖了多线程编程的主要方面。")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Section 4: 报告内容
|
||||||
|
create_heading(doc, "四、报告内容")
|
||||||
|
|
||||||
|
# Task 1
|
||||||
|
create_heading(doc, "任务1:多线程创建与基本同步", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "本程序创建三个对等线程T1、T2、T3,每个线程执行不同的输出任务。主线程使用pthread_join()等待所有对等线程结束。")
|
||||||
|
create_heading(doc, "2. 源代码", level=2)
|
||||||
|
create_paragraph(doc, "#include <pthread.h>")
|
||||||
|
create_paragraph(doc, "#include <time.h>")
|
||||||
|
create_paragraph(doc, "// 完整源代码见task61.c")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task61 task61.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据", level=2)
|
||||||
|
create_paragraph(doc, "程序无输入参数,直接运行。")
|
||||||
|
create_heading(doc, "5. 运行结果", level=2)
|
||||||
|
create_paragraph(doc, "My name is Lvjinzhong")
|
||||||
|
create_paragraph(doc, "My student number is 2024414290124")
|
||||||
|
create_paragraph(doc, "Current time Fri May 29 11:32:12 2026")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 2
|
||||||
|
create_heading(doc, "任务2:信号量实现互斥访问", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "原badcount.c程序存在竞态条件,多个线程同时对共享变量cnt进行增减操作,导致结果不正确。本程序使用二元信号量mutex实现对cnt变量的互斥访问保护。")
|
||||||
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||||
|
create_paragraph(doc, "sem_t mutex;")
|
||||||
|
create_paragraph(doc, "sem_init(&mutex, 0, 1);")
|
||||||
|
create_paragraph(doc, "// 在increase/decrease函数中:")
|
||||||
|
create_paragraph(doc, "sem_wait(&mutex); cnt++; sem_post(&mutex);")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task62 task62.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据", level=2)
|
||||||
|
create_paragraph(doc, "运行时指定迭代次数,如:./task62 100000")
|
||||||
|
create_heading(doc, "5. 运行结果", level=2)
|
||||||
|
create_paragraph(doc, "Correct! cnt=0")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 3
|
||||||
|
create_heading(doc, "任务3:生产者-消费者问题", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "使用有界缓冲区实现生产者-消费者同步。slots信号量表示缓冲区空槽数,items信号量表示缓冲区产品数,mutex保护缓冲区的临界区操作。")
|
||||||
|
create_heading(doc, "2. 源代码(核心数据结构)", level=2)
|
||||||
|
create_paragraph(doc, "typedef struct {")
|
||||||
|
create_paragraph(doc, " int *buf; int n; int inpos, outpos;")
|
||||||
|
create_paragraph(doc, " sem_t mutex, slots, items;")
|
||||||
|
create_paragraph(doc, "} sbuf_t;")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task63 task63.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据", level=2)
|
||||||
|
create_paragraph(doc, "./task63 2 3 5 表示2个生产者、3个消费者、缓冲区大小为5")
|
||||||
|
create_heading(doc, "5. 运行结果", level=2)
|
||||||
|
create_paragraph(doc, "Produced sum: 2765")
|
||||||
|
create_paragraph(doc, "Consumed sum: 2765")
|
||||||
|
create_paragraph(doc, "Verification successful")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 4
|
||||||
|
create_heading(doc, "任务4:并行计算平方和", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "将0到n-1的平方和分配给多个线程并行计算。每个线程计算自己分配区间的平方和,最后将各线程的结果求和。")
|
||||||
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||||
|
create_paragraph(doc, "void *sum(void *vargp) {")
|
||||||
|
create_paragraph(doc, " unsigned long long begin = myid * nelems_per_thread;")
|
||||||
|
create_paragraph(doc, " for (i = begin; i < end; i++) lsum += i * i;")
|
||||||
|
create_paragraph(doc, "}")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task64 task64.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据与结果", level=2)
|
||||||
|
create_paragraph(doc, "Threads=1: Time=0.001945s, result=549755289600")
|
||||||
|
create_paragraph(doc, "Threads=2: Time=0.001522s")
|
||||||
|
create_paragraph(doc, "Threads=4: Time=0.001612s")
|
||||||
|
create_paragraph(doc, "Threads=8: Time=0.001587s")
|
||||||
|
create_paragraph(doc, "Threads=16: Time=0.002894s")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 5
|
||||||
|
create_heading(doc, "任务5:矩阵乘法并行化", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "将N×N矩阵按行分配给多个线程并行计算。每个线程计算其负责的行与其他列的乘积,最后汇总结果。")
|
||||||
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||||
|
create_paragraph(doc, "for (i = start_row; i < end_row; i++)")
|
||||||
|
create_paragraph(doc, " for (j = 0; j < N; j++)")
|
||||||
|
create_paragraph(doc, " C[i][j] = sum(A[i][k] * B[k][j]);")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o matmult matmult.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据与结果(N=128, 4线程)", level=2)
|
||||||
|
create_paragraph(doc, "Serial time: 0.010762s")
|
||||||
|
create_paragraph(doc, "Parallel time: 0.004444s")
|
||||||
|
create_paragraph(doc, "Speedup: 2.42, Efficiency: 0.605")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 6
|
||||||
|
create_heading(doc, "任务6:进程与线程创建开销比较", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "通过反复调用fork()和pthread_create()各1000次,计算平均每次调用的执行时间。fork需要复制进程资源,而pthread_create只创建轻量级线程。")
|
||||||
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||||
|
create_paragraph(doc, "// fork测量")
|
||||||
|
create_paragraph(doc, "pid_t pid = fork(); if(pid==0) _exit(0); waitpid(pid,NULL,0);")
|
||||||
|
create_paragraph(doc, "// pthread_create测量")
|
||||||
|
create_paragraph(doc, "pthread_create(&tid, NULL, func, NULL); pthread_join(tid, NULL);")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task66 task66.c -lpthread")
|
||||||
|
create_heading(doc, "4. 运行结果", level=2)
|
||||||
|
create_paragraph(doc, "pthread_create() + pthread_join(): 0.178 ms")
|
||||||
|
create_paragraph(doc, "fork() + waitpid(): 0.549 ms")
|
||||||
|
create_paragraph(doc, "fork比pthread_create慢约3倍")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Task 7
|
||||||
|
create_heading(doc, "任务7:动态线程池管理", level=2)
|
||||||
|
create_heading(doc, "1. 设计思想", level=2)
|
||||||
|
create_paragraph(doc, "主线程预先创建工作线程,通过有界缓冲区接收任务。当缓冲区满时,工作线程数翻倍;当缓冲区空时,工作线程数减半。")
|
||||||
|
create_heading(doc, "2. 源代码(核心部分)", level=2)
|
||||||
|
create_paragraph(doc, "void *worker(void *arg) {")
|
||||||
|
create_paragraph(doc, " while(1) { task = sbuf_remove(&buf); sleep(task); }")
|
||||||
|
create_paragraph(doc, "}")
|
||||||
|
create_heading(doc, "3. 编译过程", level=2)
|
||||||
|
create_paragraph(doc, "gcc -o task67 task67.c -lpthread")
|
||||||
|
create_heading(doc, "4. 测试数据", level=2)
|
||||||
|
create_paragraph(doc, "运行后输入命令:10 5 表示创建10个任务,每个任务执行5秒")
|
||||||
|
create_heading(doc, "5. 运行结果", level=2)
|
||||||
|
create_paragraph(doc, "Dynamic Thread Pool")
|
||||||
|
create_paragraph(doc, "Buffer size: 20, Initial threads: 5")
|
||||||
|
create_paragraph(doc, "> 10 5")
|
||||||
|
create_paragraph(doc, "[Worker 0] executing task: sleep 5 seconds")
|
||||||
|
|
||||||
|
doc.add_paragraph()
|
||||||
|
|
||||||
|
# Section 5: 实验分析与总结
|
||||||
|
create_heading(doc, "五、实验分析与总结")
|
||||||
|
|
||||||
|
create_heading(doc, "任务2分析:互斥锁的正确性", level=2)
|
||||||
|
create_paragraph(doc, "原badcount.c程序存在严重的竞态条件。当多个线程同时执行cnt++和cnt--时,操作不是原子的,可能导致计数错误。使用信号量实现互斥访问后,确保了同一时刻只有一个线程能修改共享变量,程序输出cnt=0,说明同步机制正确有效。")
|
||||||
|
|
||||||
|
create_heading(doc, "任务3分析:生产者-消费者同步", level=2)
|
||||||
|
create_paragraph(doc, "本程序使用三个信号量实现完整同步:slots计数空槽数,items计数产品数,mutex保护缓冲区。验证方案通过比较生产者产生的随机数之和与消费者接收的随机数之和来验证正确性。实验结果显示两者相等,证明同步机制工作正常。")
|
||||||
|
|
||||||
|
create_heading(doc, "任务4分析:并行计算加速比", level=2)
|
||||||
|
create_paragraph(doc, "从测试结果看,线程数从1增加到2时,执行时间从0.001945s降至0.001522s,有明显加速。但线程数继续增加时,加速效果不明显,甚至在16线程时时间增加。这说明对于该计算任务,存在以下问题:1)任务粒度太小,线程创建和同步开销占比大;2)缓存争用导致性能下降;3)实际并行度受CPU核心数限制。")
|
||||||
|
|
||||||
|
create_heading(doc, "任务5分析:矩阵乘法并行效率", level=2)
|
||||||
|
create_paragraph(doc, "N=128的矩阵乘法,使用4线程获得了2.42的加速比,效率为0.605。这说明并行化有一定效果,但由于矩阵不大,线程通信和同步开销仍占一定比例。随着矩阵规模增大和线程数增加,效率会进一步提升。")
|
||||||
|
|
||||||
|
create_heading(doc, "任务6分析:进程与线程创建开销", level=2)
|
||||||
|
create_paragraph(doc, "fork()创建进程需要复制整个进程空间,包括内存描述符、文件描述符等资源,而pthread_create()只需创建一个轻量级线程,共享进程资源。实验结果显示fork()的开销约为pthread_create()的3倍,符合理论预期。")
|
||||||
|
|
||||||
|
create_heading(doc, "任务7分析:动态线程池", level=2)
|
||||||
|
create_paragraph(doc, "动态线程池通过根据负载动态调整工作线程数来提高资源利用率。缓冲区满时增加线程,缓冲区空时减少线程。但实际测试中发现线程数调整可能导致任务重新分配的问题,需要更精细的同步机制。")
|
||||||
|
|
||||||
|
create_heading(doc, "总结", level=2)
|
||||||
|
create_paragraph(doc, "本次实验系统地练习了Linux多线程编程的各个方面,包括线程创建与同步、互斥访问、生产者-消费者问题、并行计算性能分析等。通过实际编程和测试,加深了对线程同步机制和并行程序性能特征的理解。")
|
||||||
|
|
||||||
|
# Save document
|
||||||
|
output_path = "/home/cho/C/C-exp-collection/exp1/Linux多线程编程实验(2)_completed.docx"
|
||||||
|
doc.save(output_path)
|
||||||
|
print(f"Document saved to: {output_path}")
|
||||||
BIN
exp1/matmult
Executable file
BIN
exp1/matmult
Executable file
Binary file not shown.
163
exp1/matmult.c
Normal file
163
exp1/matmult.c
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
任务5(选做):编写一个N×N矩阵乘法函数的并行线程化版本,程序保存为matmult.c,
|
||||||
|
设计一种方案,验证并行程序正确性。
|
||||||
|
编译、调试、运行程序,设2^k ≤ N < 2^(k+1)
|
||||||
|
给出线程数为1、2、4、…、2^(k-1)、2^k等情况下的运行时间,计算加速比与效率,并对结果给出解释。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
double get_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int id;
|
||||||
|
int start_row;
|
||||||
|
int end_row;
|
||||||
|
int N;
|
||||||
|
double *A;
|
||||||
|
double *B;
|
||||||
|
double *C;
|
||||||
|
} thread_arg_t;
|
||||||
|
|
||||||
|
void *mul_thread(void *arg)
|
||||||
|
{
|
||||||
|
thread_arg_t *ta = (thread_arg_t *)arg;
|
||||||
|
for (int i = ta->start_row; i < ta->end_row; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < ta->N; j++)
|
||||||
|
{
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int k = 0; k < ta->N; k++)
|
||||||
|
{
|
||||||
|
sum += ta->A[i * ta->N + k] * ta->B[k * ta->N + j];
|
||||||
|
}
|
||||||
|
ta->C[i * ta->N + j] = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 串行版本用于验证 */
|
||||||
|
void matmul_serial(double *A, double *B, double *C, int N)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < N; j++)
|
||||||
|
{
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int k = 0; k < N; k++)
|
||||||
|
{
|
||||||
|
sum += A[i * N + k] * B[k * N + j];
|
||||||
|
}
|
||||||
|
C[i * N + j] = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int verify(double *C1, double *C2, int N)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < N * N; i++)
|
||||||
|
{
|
||||||
|
if (C1[i] - C2[i] > 1e-6 || C2[i] - C1[i] > 1e-6)
|
||||||
|
{
|
||||||
|
printf("Mismatch at index %d: %f vs %f\n", i, C1[i], C2[i]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int N, nthreads;
|
||||||
|
|
||||||
|
if (argc != 3)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <N> <nthreads>\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
N = atoi(argv[1]);
|
||||||
|
nthreads = atoi(argv[2]);
|
||||||
|
|
||||||
|
double *A = (double *)malloc(N * N * sizeof(double));
|
||||||
|
double *B = (double *)malloc(N * N * sizeof(double));
|
||||||
|
double *C = (double *)malloc(N * N * sizeof(double));
|
||||||
|
double *C_serial = (double *)malloc(N * N * sizeof(double));
|
||||||
|
|
||||||
|
srand(42);
|
||||||
|
for (int i = 0; i < N * N; i++)
|
||||||
|
{
|
||||||
|
A[i] = (double)(rand() % 100) / 10.0;
|
||||||
|
B[i] = (double)(rand() % 100) / 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 并行版本计时 */
|
||||||
|
pthread_t *tid = (pthread_t *)malloc(nthreads * sizeof(pthread_t));
|
||||||
|
thread_arg_t *targs = (thread_arg_t *)malloc(nthreads * sizeof(thread_arg_t));
|
||||||
|
|
||||||
|
double t_start = get_time();
|
||||||
|
int rows_per_thread = N / nthreads;
|
||||||
|
int extra = N % nthreads;
|
||||||
|
int current_row = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < nthreads; i++)
|
||||||
|
{
|
||||||
|
targs[i].id = i;
|
||||||
|
targs[i].start_row = current_row;
|
||||||
|
targs[i].end_row = current_row + rows_per_thread + (i < extra ? 1 : 0);
|
||||||
|
targs[i].N = N;
|
||||||
|
targs[i].A = A;
|
||||||
|
targs[i].B = B;
|
||||||
|
targs[i].C = C;
|
||||||
|
current_row = targs[i].end_row;
|
||||||
|
pthread_create(&tid[i], NULL, mul_thread, &targs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nthreads; i++)
|
||||||
|
{
|
||||||
|
pthread_join(tid[i], NULL);
|
||||||
|
}
|
||||||
|
double t_end = get_time();
|
||||||
|
double t_parallel = t_end - t_start;
|
||||||
|
|
||||||
|
/* 串行版本验证 */
|
||||||
|
double t_ser_start = get_time();
|
||||||
|
matmul_serial(A, B, C_serial, N);
|
||||||
|
double t_ser_end = get_time();
|
||||||
|
double t_serial = t_ser_end - t_ser_start;
|
||||||
|
|
||||||
|
printf("Matrix size: %d x %d, Threads: %d\n", N, N, nthreads);
|
||||||
|
printf("Serial time: %f seconds\n", t_serial);
|
||||||
|
printf("Parallel time: %f seconds\n", t_parallel);
|
||||||
|
|
||||||
|
if (verify(C, C_serial, N))
|
||||||
|
{
|
||||||
|
printf("Verification: PASSED - parallel result matches serial.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Verification: FAILED.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t_parallel > 0)
|
||||||
|
{
|
||||||
|
printf("Speedup: %f\n", t_serial / t_parallel);
|
||||||
|
printf("Efficiency: %f\n", t_serial / (nthreads * t_parallel));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(A);
|
||||||
|
free(B);
|
||||||
|
free(C);
|
||||||
|
free(C_serial);
|
||||||
|
free(tid);
|
||||||
|
free(targs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
exp1/psum64
Executable file
BIN
exp1/psum64
Executable file
Binary file not shown.
89
exp1/psum64.c
Normal file
89
exp1/psum64.c
Normal 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;
|
||||||
|
}
|
||||||
BIN
exp1/task61
BIN
exp1/task61
Binary file not shown.
@@ -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
BIN
exp1/task62
Executable file
Binary file not shown.
@@ -5,51 +5,29 @@
|
|||||||
#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;
|
||||||
pthread_t t1,t2;
|
pthread_t t1,t2;
|
||||||
|
|
||||||
if (argc != 2)
|
if (argc != 2)
|
||||||
{
|
{
|
||||||
printf("Usage: %s <niters>\n", argv[0]);
|
printf("Usage: %s <niters>\n", argv[0]);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
niters = atoll(argv[1]);
|
niters = atoll(argv[1]);
|
||||||
|
|
||||||
sem_init(&mutex, 0, 1);
|
sem_init(&mutex, 0, 1);
|
||||||
pthread_create(&t1,NULL,increase,(void*)niters);
|
pthread_create(&t1,NULL,increase,(void *)niters);
|
||||||
pthread_create(&t2,NULL,decrease,(void*)niters);
|
pthread_create(&t2,NULL,decrease,(void *)niters);
|
||||||
pthread_join(t1,NULL);
|
pthread_join(t1,NULL);
|
||||||
pthread_join(t2,NULL);
|
pthread_join(t2,NULL);
|
||||||
|
|
||||||
if (cnt != 0)
|
if (cnt != 0)
|
||||||
{
|
{
|
||||||
printf("Error! cnt=%d\n", cnt);
|
printf("Error! cnt=%d\n", cnt);
|
||||||
@@ -60,4 +38,28 @@ 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
BIN
exp1/task63
Executable file
Binary file not shown.
185
exp1/task63.c
185
exp1/task63.c
@@ -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
BIN
exp1/task64
Executable file
Binary file not shown.
88
exp1/task64.c
Normal file
88
exp1/task64.c
Normal 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
BIN
exp1/task66
Executable file
Binary file not shown.
80
exp1/task66.c
Normal file
80
exp1/task66.c
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
任务6(任选):测量和比较fork、pthread_create函数调用所需的执行时间,并进行解释。
|
||||||
|
可以多次反复调用读写函数,计算累积时间。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#define ITERATIONS 1000
|
||||||
|
|
||||||
|
double get_time()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dummy_thread(void *arg)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double measure_fork_create()
|
||||||
|
{
|
||||||
|
double t_start = get_time();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++)
|
||||||
|
{
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0)
|
||||||
|
{
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
/* 子进程立即退出 */
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* 父进程等待子进程结束 */
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double t_end = get_time();
|
||||||
|
return (t_end - t_start) / ITERATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
double measure_pthread_create()
|
||||||
|
{
|
||||||
|
pthread_t tid;
|
||||||
|
double t_start = get_time();
|
||||||
|
for (int i = 0; i < ITERATIONS; i++)
|
||||||
|
{
|
||||||
|
pthread_create(&tid, NULL, dummy_thread, NULL);
|
||||||
|
pthread_join(tid, NULL);
|
||||||
|
}
|
||||||
|
double t_end = get_time();
|
||||||
|
return (t_end - t_start) / ITERATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("Measuring fork() and pthread_create() overhead...\n");
|
||||||
|
printf("Iterations per test: %d\n\n", ITERATIONS);
|
||||||
|
|
||||||
|
double avg_pthread_time = measure_pthread_create();
|
||||||
|
printf("Average pthread_create() + pthread_join() time: %.6f ms\n",
|
||||||
|
avg_pthread_time * 1000);
|
||||||
|
|
||||||
|
double avg_fork_time = measure_fork_create();
|
||||||
|
printf("Average fork() + waitpid() time: %.6f ms\n",
|
||||||
|
avg_fork_time * 1000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
exp1/task67
Executable file
BIN
exp1/task67
Executable file
Binary file not shown.
272
exp1/task67.c
Normal file
272
exp1/task67.c
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
任务7(任选):编写一个多线程并发应用程序task67.c。
|
||||||
|
1)主线程预先创建5个工作线程,然后通过缓冲区发放任务,对等线程从缓冲区提取任务执行,当缓冲区为空时,等待任务。
|
||||||
|
2)每个任务内容是以秒为单位的整数等待时间,对等线程取得任务后,调用sleep睡眠指定的秒数,就算任务执行完成。
|
||||||
|
3)主线程从终端读取命令,发布任务,命令格式为"<任务数> <秒数>",如输入"10 5"表示创建10个任务,每个任务的运行时间是5秒。
|
||||||
|
4)应用程序应支持动态地增加或减少工作线程的数目,一个策略是当缓冲区变满时,将线程数量翻倍,而当缓冲区变为空时,将线程数目减半。
|
||||||
|
5)每次工作线程发送变动时,应输出相关信息。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
#define DEFAULT_BUF_SIZE 20
|
||||||
|
#define INITIAL_THREADS 5
|
||||||
|
#define MAX_THREADS 256
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int *buf;
|
||||||
|
int n;
|
||||||
|
int inpos;
|
||||||
|
int outpos;
|
||||||
|
sem_t mutex;
|
||||||
|
sem_t slots;
|
||||||
|
sem_t items;
|
||||||
|
} sbuf_t;
|
||||||
|
|
||||||
|
/* 线程池管理 */
|
||||||
|
int current_thread_count = INITIAL_THREADS;
|
||||||
|
int thread_should_exit[MAX_THREADS];
|
||||||
|
pthread_t worker_tids[MAX_THREADS];
|
||||||
|
pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
volatile int program_running = 1;
|
||||||
|
sbuf_t buf;
|
||||||
|
|
||||||
|
void sbuf_init(sbuf_t *sp, int n)
|
||||||
|
{
|
||||||
|
sp->buf = (int *)malloc(n * sizeof(int));
|
||||||
|
sp->n = n;
|
||||||
|
sp->inpos = 0;
|
||||||
|
sp->outpos = 0;
|
||||||
|
sem_init(&sp->mutex, 0, 1);
|
||||||
|
sem_init(&sp->slots, 0, n);
|
||||||
|
sem_init(&sp->items, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_deinit(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
free(sp->buf);
|
||||||
|
sem_destroy(&sp->mutex);
|
||||||
|
sem_destroy(&sp->slots);
|
||||||
|
sem_destroy(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbuf_insert(sbuf_t *sp, int item)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->slots);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
sp->buf[sp->inpos] = item;
|
||||||
|
sp->inpos = (sp->inpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sbuf_remove(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
sem_wait(&sp->items);
|
||||||
|
sem_wait(&sp->mutex);
|
||||||
|
int item = sp->buf[sp->outpos];
|
||||||
|
sp->outpos = (sp->outpos + 1) % sp->n;
|
||||||
|
sem_post(&sp->mutex);
|
||||||
|
sem_post(&sp->slots);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sbuf_is_full(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
sem_getvalue(&sp->slots, &val);
|
||||||
|
return val == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sbuf_is_empty(sbuf_t *sp)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
sem_getvalue(&sp->items, &val);
|
||||||
|
return val == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *worker(void *arg)
|
||||||
|
{
|
||||||
|
int my_id = *((int *)arg);
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
/* 检查是否应该退出 */
|
||||||
|
pthread_mutex_lock(&pool_mutex);
|
||||||
|
if (thread_should_exit[my_id])
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
|
||||||
|
int task_seconds = sbuf_remove(&buf);
|
||||||
|
|
||||||
|
/* 再次检查退出标志(可能在等待期间被标记) */
|
||||||
|
pthread_mutex_lock(&pool_mutex);
|
||||||
|
if (thread_should_exit[my_id])
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
/* 任务还在缓冲区中被取出了,需要重新放回去 */
|
||||||
|
sbuf_insert(&buf, task_seconds);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
|
||||||
|
printf("[Worker %d] executing task: sleep %d seconds\n", my_id, task_seconds);
|
||||||
|
sleep(task_seconds);
|
||||||
|
printf("[Worker %d] task completed\n", my_id);
|
||||||
|
}
|
||||||
|
printf("[Worker %d] exiting\n", my_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjust_threads_up()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&pool_mutex);
|
||||||
|
int new_count = current_thread_count * 2;
|
||||||
|
if (new_count > MAX_THREADS) new_count = MAX_THREADS;
|
||||||
|
if (new_count <= current_thread_count)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int added = new_count - current_thread_count;
|
||||||
|
for (int i = 0; i < added; i++)
|
||||||
|
{
|
||||||
|
int tid = current_thread_count + i;
|
||||||
|
thread_should_exit[tid] = 0;
|
||||||
|
int *arg = malloc(sizeof(int));
|
||||||
|
*arg = tid;
|
||||||
|
pthread_create(&worker_tids[tid], NULL, worker, arg);
|
||||||
|
}
|
||||||
|
current_thread_count = new_count;
|
||||||
|
printf("缓冲区变满,工作线程数翻倍,当前工作线程数为%d个\n", current_thread_count);
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjust_threads_down()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&pool_mutex);
|
||||||
|
int new_count = current_thread_count / 2;
|
||||||
|
if (new_count < 1) new_count = 1;
|
||||||
|
if (new_count >= current_thread_count)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int removed = current_thread_count - new_count;
|
||||||
|
/* 标记尾部线程为退出 */
|
||||||
|
for (int i = 0; i < removed; i++)
|
||||||
|
{
|
||||||
|
int tid = current_thread_count - 1 - i;
|
||||||
|
thread_should_exit[tid] = 1;
|
||||||
|
}
|
||||||
|
current_thread_count = new_count;
|
||||||
|
printf("缓冲区变空,工作线程数减半,当前工作线程数为%d个\n", current_thread_count);
|
||||||
|
|
||||||
|
/* 插入额外的空槽通知以便标记线程能退出 */
|
||||||
|
for (int i = 0; i < removed; i++)
|
||||||
|
{
|
||||||
|
sbuf_insert(&buf, 0);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int buf_size = DEFAULT_BUF_SIZE;
|
||||||
|
if (argc >= 2)
|
||||||
|
buf_size = atoi(argv[1]);
|
||||||
|
|
||||||
|
printf("Dynamic Thread Pool\n");
|
||||||
|
printf("Buffer size: %d, Initial threads: %d\n", buf_size, INITIAL_THREADS);
|
||||||
|
printf("Commands: <task_count> <seconds> | 'quit' to exit\n\n");
|
||||||
|
|
||||||
|
sbuf_init(&buf, buf_size);
|
||||||
|
|
||||||
|
/* 创建初始线程 */
|
||||||
|
for (int i = 0; i < INITIAL_THREADS; i++)
|
||||||
|
{
|
||||||
|
thread_should_exit[i] = 0;
|
||||||
|
int *arg = malloc(sizeof(int));
|
||||||
|
*arg = i;
|
||||||
|
pthread_create(&worker_tids[i], NULL, worker, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主线程命令循环 */
|
||||||
|
char line[256];
|
||||||
|
while (program_running)
|
||||||
|
{
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (fgets(line, sizeof(line), stdin) == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strncmp(line, "quit", 4) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int task_count, task_seconds;
|
||||||
|
if (sscanf(line, "%d %d", &task_count, &task_seconds) != 2)
|
||||||
|
{
|
||||||
|
printf("Invalid command. Usage: <task_count> <seconds>\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Adding %d tasks, each %d seconds...\n", task_count, task_seconds);
|
||||||
|
|
||||||
|
for (int i = 0; i < task_count; i++)
|
||||||
|
{
|
||||||
|
/* 插入前检查是否满 */
|
||||||
|
if (sbuf_is_full(&buf))
|
||||||
|
{
|
||||||
|
adjust_threads_up();
|
||||||
|
}
|
||||||
|
sbuf_insert(&buf, task_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查插入后是否需要调整 */
|
||||||
|
int slots_val, items_val;
|
||||||
|
sem_getvalue(&buf.slots, &slots_val);
|
||||||
|
sem_getvalue(&buf.items, &items_val);
|
||||||
|
printf("Buffer status: %d/%d slots free, %d items pending\n",
|
||||||
|
slots_val, buf_size, items_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 等待所有任务完成并通知线程退出 */
|
||||||
|
printf("\nWaiting for pending tasks to complete...\n");
|
||||||
|
while (!sbuf_is_empty(&buf))
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标记所有线程退出并唤醒它们 */
|
||||||
|
pthread_mutex_lock(&pool_mutex);
|
||||||
|
for (int i = 0; i < current_thread_count; i++)
|
||||||
|
{
|
||||||
|
thread_should_exit[i] = 1;
|
||||||
|
}
|
||||||
|
/* 插入足够多的信号量以唤醒所有等待的线程 */
|
||||||
|
for (int i = 0; i < current_thread_count; i++)
|
||||||
|
{
|
||||||
|
sbuf_insert(&buf, 0);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pool_mutex);
|
||||||
|
|
||||||
|
/* 等待所有线程结束 */
|
||||||
|
for (int i = 0; i < current_thread_count; i++)
|
||||||
|
{
|
||||||
|
pthread_join(worker_tids[i], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("All workers exited. Program terminating.\n");
|
||||||
|
sbuf_deinit(&buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
server-exp/cgi-bin/add
Executable file
BIN
server-exp/cgi-bin/add
Executable file
Binary file not shown.
27
server-exp/cgi-bin/add.c
Normal file
27
server-exp/cgi-bin/add.c
Normal 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
242
server-exp/common.c
Normal 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
76
server-exp/common.h
Normal 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
BIN
server-exp/example.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 189 KiB |
BIN
server-exp/favicon.ico
Normal file
BIN
server-exp/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
server-exp/ftpc
Executable file
BIN
server-exp/ftpc
Executable file
Binary file not shown.
109
server-exp/ftpc.c
Normal file
109
server-exp/ftpc.c
Normal 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);
|
||||||
|
}
|
||||||
0
server-exp/ftpc.c:Zone.Identifier
Normal file
0
server-exp/ftpc.c:Zone.Identifier
Normal file
BIN
server-exp/ftps
Executable file
BIN
server-exp/ftps
Executable file
Binary file not shown.
101
server-exp/ftps.c
Normal file
101
server-exp/ftps.c
Normal 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);
|
||||||
|
}
|
||||||
0
server-exp/ftps.c:Zone.Identifier
Normal file
0
server-exp/ftps.c:Zone.Identifier
Normal file
BIN
server-exp/http_load-09Mar2016.tar.gz
Normal file
BIN
server-exp/http_load-09Mar2016.tar.gz
Normal file
Binary file not shown.
13
server-exp/index.html
Normal file
13
server-exp/index.html
Normal 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
214
server-exp/perf.txt
Normal 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
8
server-exp/test.html
Normal 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
1
server-exp/testfile.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello ftp test
|
||||||
BIN
server-exp/togglec
Executable file
BIN
server-exp/togglec
Executable file
Binary file not shown.
29
server-exp/togglec.c
Normal file
29
server-exp/togglec.c
Normal 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
BIN
server-exp/togglesi
Executable file
Binary file not shown.
57
server-exp/togglesi.c
Normal file
57
server-exp/togglesi.c
Normal 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
3
server-exp/urls
Normal 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
BIN
server-exp/webclient
Executable file
Binary file not shown.
55
server-exp/webclient.c
Normal file
55
server-exp/webclient.c
Normal 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
BIN
server-exp/weblet
Executable file
Binary file not shown.
278
server-exp/weblet.c
Normal file
278
server-exp/weblet.c
Normal 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]); // 关闭写端
|
||||||
|
}
|
||||||
BIN
server-exp/实验1任务报告web服务器初步实现_填写版.docx
Normal file
BIN
server-exp/实验1任务报告web服务器初步实现_填写版.docx
Normal file
Binary file not shown.
2
server-exp/实验1任务报告web服务器初步实现_填写版.docx:Zone.Identifier
Normal file
2
server-exp/实验1任务报告web服务器初步实现_填写版.docx:Zone.Identifier
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[ZoneTransfer]
|
||||||
|
ZoneId=3
|
||||||
14
server-exp/实验一文件清单.txt
Normal file
14
server-exp/实验一文件清单.txt
Normal 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
81151
server-exp2/abc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
server-exp2/cgi-bin/add
Normal file
BIN
server-exp2/cgi-bin/add
Normal file
Binary file not shown.
27
server-exp2/cgi-bin/add.c
Normal file
27
server-exp2/cgi-bin/add.c
Normal 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
243
server-exp2/common.c
Normal 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
78
server-exp2/common.h
Normal 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
BIN
server-exp2/example.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 189 KiB |
BIN
server-exp2/favicon.ico
Normal file
BIN
server-exp2/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
13
server-exp2/index.html
Normal file
13
server-exp2/index.html
Normal 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
200
server-exp2/perf.txt
Normal 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
6
server-exp2/t1.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
hello
|
||||||
|
world
|
||||||
|
dgut
|
||||||
|
computer
|
||||||
|
Hello
|
||||||
|
WORLD
|
||||||
6
server-exp2/t2.txt
Normal file
6
server-exp2/t2.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
hello
|
||||||
|
world
|
||||||
|
dgut
|
||||||
|
computer
|
||||||
|
Hello
|
||||||
|
WORLD
|
||||||
45
server-exp2/taskline.c
Normal file
45
server-exp2/taskline.c
Normal 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
20
server-exp2/taskline.h
Normal 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
8
server-exp2/test.html
Normal 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
BIN
server-exp2/togglec
Executable file
Binary file not shown.
30
server-exp2/togglec.c
Normal file
30
server-exp2/togglec.c
Normal 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
6
server-exp2/togglecm
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
for i in {1..20}
|
||||||
|
do
|
||||||
|
./togglec localhost 12345 < $1
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
BIN
server-exp2/togglesp
Executable file
BIN
server-exp2/togglesp
Executable file
Binary file not shown.
83
server-exp2/togglesp.c
Normal file
83
server-exp2/togglesp.c
Normal 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
BIN
server-exp2/togglest
Executable file
Binary file not shown.
86
server-exp2/togglest.c
Normal file
86
server-exp2/togglest.c
Normal 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
104
server-exp2/togglest_pool.c
Normal 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
Reference in New Issue
Block a user