69天探索操作系统-第9天:线程与进程比较 - 在 C 中实现并发系统

211 阅读16分钟

并发系统.avif

1.介绍

了解线程和进程之间的差异对于设计高效的并发应用程序至关重要。本文提供了线程和进程的详细比较,重点关注它们的实现、性能特征和适当的用例。

2.核心概念

基本差异

  1. 内存空间: 进程中的内存空间是完全独立和隔离的。每个进程都有自己的地址空间,包括代码、数据和堆段。例如,如果进程A的变量'x'位于地址0x1000,那么它与进程B在同一地址的变量'x'完全不同。

  2. 资源所有者: 进程独立拥有和控制其所有资源。当一个进程创建文件句柄时,该句柄不能直接被其他进程访问,除非有明确的共享机制。不过,线程可以在同一个进程中共享资源,从而使资源共享更加直观。

  3. 上下文切换开销: 进程上下文切换需要保存和加载整个内存映射、缓存内容和CPU寄存器。线程上下文切换只需要保存和恢复CPU寄存器和栈指针,因此开销要轻得多。

  4. 创建时间: 创建一个新进程涉及复制父进程的全部内存空间和资源。线程的创建只需要分配一个新的堆栈和线程特定的数据结构。

以下是演示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/wait.h>

// Timing structure
typedef struct {
    struct timeval start;
    struct timeval end;
} timing_t;

// Function to measure process creation time
void measure_process_creation() {
    timing_t timing;
    gettimeofday(&timing.start, NULL);
    
    pid_t pid = fork();
    if (pid == 0) {
        // Child process
        exit(0);
    } else {
        // Parent process
        wait(NULL);
    }
    
    gettimeofday(&timing.end, NULL);
    
    long microseconds = (timing.end.tv_sec - timing.start.tv_sec) * 1000000 +
                       (timing.end.tv_usec - timing.start.tv_usec);
    printf("Process creation time: %ld microseconds\n", microseconds);
}

// Function for thread creation measurement
void* thread_function(void* arg) {
    return NULL;
}

void measure_thread_creation() {
    timing_t timing;
    pthread_t thread;
    
    gettimeofday(&timing.start, NULL);
    
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    
    gettimeofday(&timing.end, NULL);
    
    long microseconds = (timing.end.tv_sec - timing.start.tv_sec) * 1000000 +
                       (timing.end.tv_usec - timing.start.tv_usec);
    printf("Thread creation time: %ld microseconds\n", microseconds);
}

int main() {
    measure_process_creation();
    measure_thread_creation();
    return 0;
}

3. 内存架构比较

内存布局分析

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>

// Shared data structure
typedef struct {
    int process_value;
    int thread_value;
} shared_data_t;

// Global variables
shared_data_t* shared_memory;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* thread_function(void* arg) {
    // Thread can access process memory directly
    shared_memory->thread_value++;
    return NULL;
}

int main() {
    // Create shared memory for processes
    shared_memory = mmap(NULL, sizeof(shared_data_t),
                        PROT_READ | PROT_WRITE,
                        MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
    shared_memory->process_value = 0;
    shared_memory->thread_value = 0;
    
    // Create process
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child process
        shared_memory->process_value++;
        exit(0);
    } else {
        // Parent process
        pthread_t thread;
        pthread_create(&thread, NULL, thread_function, NULL);
        pthread_join(thread, NULL);
        
        wait(NULL);
        
        printf("Process value: %d\n", shared_memory->process_value);
        printf("Thread value: %d\n", shared_memory->thread_value);
        
        munmap(shared_memory, sizeof(shared_data_t));
    }
    
    return 0;
}

4. 性能分析

CPU 利用情况

  1. 进程调度开销: 由于内存管理单元(MMU)更新和TLB刷新,需要更多的CPU时间来进行上下文切换。这里的基准测试如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>

#define NUM_ITERATIONS 1000000

void measure_context_switch_overhead() {
    int pipe_fd[2];
    pipe(pipe_fd);
    
    struct timeval start, end;
    char buffer[1];
    
    gettimeofday(&start, NULL);
    
    pid_t pid = fork();
    if (pid == 0) {
        // Child process
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            read(pipe_fd[0], buffer, 1);
            write(pipe_fd[1], "x", 1);
        }
        exit(0);
    } else {
        // Parent process
        for (int i = 0; i < NUM_ITERATIONS; i++) {
            write(pipe_fd[1], "x", 1);
            read(pipe_fd[0], buffer, 1);
        }
    }
    
    gettimeofday(&end, NULL);
    
    long microseconds = (end.tv_sec - start.tv_sec) * 1000000 +
                       (end.tv_usec - start.tv_usec);
    printf("Average context switch time: %f microseconds\n",
           (float)microseconds / (NUM_ITERATIONS * 2));
}

int main() {
    measure_context_switch_overhead();
    return 0;
}
  1. 内存访问模式: 线程共享相同的地址空间,从而提高了缓存利用率。 以下是一个演示:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>

#define ARRAY_SIZE 10000000
#define NUM_THREADS 4

double* shared_array;

void* thread_function(void* arg) {
    int thread_id = *(int*)arg;
    int chunk_size = ARRAY_SIZE / NUM_THREADS;
    int start = thread_id * chunk_size;
    int end = start + chunk_size;
    
    for (int i = start; i < end; i++) {
        shared_array[i] = shared_array[i] * 2.0;
    }
    
    return NULL;
}

int main() {
    shared_array = malloc(ARRAY_SIZE * sizeof(double));
    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];
    
    // Initialize array
    for (int i = 0; i < ARRAY_SIZE; i++) {
        shared_array[i] = (double)i;
    }
    
    struct timeval start, end;
    gettimeofday(&start, NULL);
    
    // Create threads
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);
    }
    
    // Wait for threads
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    gettimeofday(&end, NULL);
    
    long microseconds = (end.tv_sec - start.tv_sec) * 1000000 +
                       (end.tv_usec - start.tv_usec);
    printf("Thread processing time: %ld microseconds\n", microseconds);
    
    free(shared_array);
    return 0;
}

5.通信机制

进程间通信 (IPC)

  1. 管道和命名管道: 进程通过管道进行通信,管道需要系统调用和数据复制。 示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define BUFFER_SIZE 1024

void demonstrate_pipe_communication() {
    int pipe_fd[2];
    char buffer[BUFFER_SIZE];
    
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        exit(1);
    }
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child process
        close(pipe_fd[1]);  // Close write end
        
        ssize_t bytes_read = read(pipe_fd[0], buffer, BUFFER_SIZE);
        printf("Child received: %s\n", buffer);
        
        close(pipe_fd[0]);
        exit(0);
    } else {
        // Parent process
        close(pipe_fd[0]);  // Close read end
        
        const char* message = "Hello from parent!";
        write(pipe_fd[1], message, strlen(message) + 1);
        
        close(pipe_fd[1]);
        wait(NULL);
    }
}

int main() {
    demonstrate_pipe_communication();
    return 0;
}

2.共享内存: 进程可以共享内存区域以加快通信

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define SHM_NAME "/my_shm"
#define SHM_SIZE 1024

void demonstrate_shared_memory() {
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, SHM_SIZE);
    
    void* shm_ptr = mmap(NULL, SHM_SIZE,
                        PROT_READ | PROT_WRITE,
                        MAP_SHARED, shm_fd, 0);
    
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child process
        sleep(1);  // Ensure parent writes first
        printf("Child read: %s\n", (char*)shm_ptr);
        exit(0);
    } else {
        // Parent process
        sprintf(shm_ptr, "Hello from shared memory!");
        wait(NULL);
        
        munmap(shm_ptr, SHM_SIZE);
        shm_unlink(SHM_NAME);
    }
}

int main() {
    demonstrate_shared_memory();
    return 0;
}

线程间通信

线程可以通过共享内存直接进行通信:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0;
int data_ready = 0;

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    shared_data = 42;
    data_ready = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (!data_ready) {
        pthread_cond_wait(&cond, &mutex);
    }
    printf("Received: %d\n", shared_data);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t prod_thread, cons_thread;
    
    pthread_create(&cons_thread, NULL, consumer, NULL);
    pthread_create(&prod_thread, NULL, producer, NULL);
    
    pthread_join(prod_thread, NULL);
    pthread_join(cons_thread, NULL);
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    
    return 0;
}

6.资源管理

进程资源

每个进程都有自己的:

  • 文件描述符
  • 内存映射
  • 信号处理器
  • 进程 ID
  • 用户和组 ID

线程资源

线程共享的资源:

  • 地址空间
  • 文件描述符
  • 信号处理器
  • 当前工作目录

线程独有的资源:

  • 堆栈
  • 线程ID
  • 信号掩码
  • errno 变量

7.实现示例

这里有一个综合示例,展示了对于同一任务的进程和线程实现:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>

#define ARRAY_SIZE 10000000
#define NUM_WORKERS 4

// Shared data structure
typedef struct {
    double* array;
    int start;
    int end;
} work_unit_t;

// Thread worker function
void* thread_worker(void* arg) {
    work_unit_t* unit = (work_unit_t*)arg;
    
    for (int i = unit->start; i < unit->end; i++) {
        unit->array[i] = unit->array[i] * 2.0;
    }
    
    return NULL;
}

// Process worker function
void process_worker(double* array, int start, int end) {
    for (int i = start; i < end; i++) {
        array[i] = array[i] * 2.0;
    }
}

// Thread implementation
void thread_implementation() {
    double* array = malloc(ARRAY_SIZE * sizeof(double));
    pthread_t threads[NUM_WORKERS];
    work_unit_t units[NUM_WORKERS];
    
    // Initialize array
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = (double)i;
    }
    
    struct timeval start, end;
    gettimeofday(&start, NULL);
    
    // Create threads
    int chunk_size = ARRAY_SIZE / NUM_WORKERS;
    for (int i = 0; i < NUM_WORKERS; i++) {
        units[i].array = array;
        units[i].start = i * chunk_size;
        units[i].end = (i == NUM_WORKERS - 1) ? ARRAY_SIZE : (i + 1) * chunk_size;
        pthread_create(&threads[i], NULL, thread_worker, &units[i]);
    }
    
    // Wait for threads
    for (int i = 0; i < NUM_WORKERS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    gettimeofday(&end, NULL);
    
    long microseconds = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
    printf("Thread implementation time: %ld microseconds\n", microseconds);
    
    free(array);
}

// Process implementation
void process_implementation() {
    double* array = mmap(NULL, ARRAY_SIZE * sizeof(double),
                        PROT_READ | PROT_WRITE,
                        MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
    // Initialize array
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = (double)i;
    }
    
    struct timeval start, end;
    gettimeofday(&start, NULL);
    
    // Create processes
    int chunk_size = ARRAY_SIZE / NUM_WORKERS;
    for (int i = 0; i < NUM_WORKERS; i++) {
        pid_t pid = fork();
        
        if (pid == 0) {
            // Child process
            int start = i * chunk_size;
            int end = (i == NUM_WORKERS - 1) ? ARRAY_SIZE : (i + 1) * chunk_size;
            process_worker(array, start, end);
            exit(0);
        }
    }
    
    // Wait for all child processes
    for (int i = 0; i < NUM_WORKERS; i++) {
        wait(NULL);
    }
    
    gettimeofday(&end, NULL);
    
    long microseconds = (end.tv_sec - start.tv_sec) * 1000000 +
                       (end.tv_usec - start.tv_usec);
    printf("Process implementation time: %ld microseconds\n", microseconds);
    
    munmap(array, ARRAY_SIZE * sizeof(double));
}

int main() {
    printf("Running thread implementation...\n");
    thread_implementation();
    
    printf("\nRunning process implementation...\n");
    process_implementation();
    
    return 0;
}

8.使用案例分析

什么时候使用进程

  1. 隔离要求:进程在需要组件间强烈隔离时非常理想。例如,网页浏览器为每个标签页使用独立的进程,以防止一个标签页的崩溃影响其他标签页。 2.安全考虑:处理敏感数据时,通过内存隔离的过程提供更好的安全保障。举例用例:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

void secure_processing() {
    // Sensitive data in isolated process
    char* sensitive_data = mmap(NULL, 4096,
                              PROT_READ | PROT_WRITE,
                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    
    sprintf(sensitive_data, "SECRET_KEY=12345");
    
    // Process the data in isolation
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child process - isolated memory space
        printf("Child process handling sensitive data\n");
        // Process sensitive_data here
        munmap(sensitive_data, 4096);
        exit(0);
    } else {
        wait(NULL);
        munmap(sensitive_data, 4096);
    }
}

什么时候使用线程

1.共享资源访问: 线程在多个执行单元需要频繁共享资源时表现得更好

#include <pthread.h>
#include <stdio.h>

typedef struct {
    int* shared_counter;
    pthread_mutex_t* mutex;
} shared_resource_t;

void* increment_counter(void* arg) {
    shared_resource_t* resource = (shared_resource_t*)arg;
    
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(resource->mutex);
        (*resource->shared_counter)++;
        pthread_mutex_unlock(resource->mutex);
    }
    
    return NULL;
}

void demonstrate_shared_resource() {
    int counter = 0;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    shared_resource_t resource = {&counter, &mutex};
    
    pthread_t threads[4];
    
    for (int i = 0; i < 4; i++) {
        pthread_create(&threads[i], NULL, increment_counter, &resource);
    }
    
    for (int i = 0; i < 4; i++) {
        pthread_join(threads[i], NULL);
    }
    
    printf("Final counter value: %d\n", counter);
    pthread_mutex_destroy(&mutex);
}
  1. 实时通信: 线程在需要频繁通信的场景中非常高效:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t data_cond = PTHREAD_COND_INITIALIZER;
int data_ready = 0;
double shared_data = 0.0;

void* real_time_producer(void* arg) {
    for (int i = 0; i < 10; i++) {
        pthread_mutex_lock(&mutex);
        shared_data = i * 1.5;
        data_ready = 1;
        pthread_cond_signal(&data_cond);
        pthread_mutex_unlock(&mutex);
        usleep(100000);  // 100ms
    }
    return NULL;
}

void* real_time_consumer(void* arg) {
    while (1) {
        pthread_mutex_lock(&mutex);
        while (!data_ready) {
            pthread_cond_wait(&data_cond, &mutex);
        }
        printf("Received data: %f\n", shared_data);
        data_ready = 0;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

9. 实际演示

以下是矩阵乘法过程中进程和线程性能比较的真实示例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/time.h>

#define MATRIX_SIZE 1000
#define NUM_WORKERS 4

typedef struct {
    double* a;
    double* b;
    double* c;
    int start_row;
    int end_row;
} matrix_work_t;

void* thread_matrix_multiply(void* arg) {
    matrix_work_t* work = (matrix_work_t*)arg;
    
    for (int i = work->start_row; i < work->end_row; i++) {
        for (int j = 0; j < MATRIX_SIZE; j++) {
            double sum = 0.0;
            for (int k = 0; k < MATRIX_SIZE; k++) {
                sum += work->a[i * MATRIX_SIZE + k] * 
                       work->b[k * MATRIX_SIZE + j];
            }
            work->c[i * MATRIX_SIZE + j] = sum;
        }
    }
    
    return NULL;
}

void process_matrix_multiply(double* a, double* b, double* c,
                           int start_row, int end_row) {
    for (int i = start_row; i < end_row; i++) {
        for (int j = 0; j < MATRIX_SIZE; j++) {
            double sum = 0.0;
            for (int k = 0; k < MATRIX_SIZE; k++) {
                sum += a[i * MATRIX_SIZE + k] * 
                       b[k * MATRIX_SIZE + j];
            }
            c[i * MATRIX_SIZE + j] = sum;
        }
    }
}

void compare_performance() {
    // Allocate matrices
    size_t matrix_bytes = MATRIX_SIZE * MATRIX_SIZE * sizeof(double);
    
    double* a = mmap(NULL, matrix_bytes,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    double* b = mmap(NULL, matrix_bytes,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    double* c = mmap(NULL, matrix_bytes,
                    PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
    // Initialize matrices
    for (int i = 0; i < MATRIX_SIZE * MATRIX_SIZE; i++) {
        a[i] = (double)rand() / RAND_MAX;
        b[i] = (double)rand() / RAND_MAX;
    }
    
    struct timeval start, end;
    
    // Thread implementation
    gettimeofday(&start, NULL);
    
    pthread_t threads[NUM_WORKERS];
    matrix_work_t thread_work[NUM_WORKERS];
    int rows_per_worker = MATRIX_SIZE / NUM_WORKERS;
    
    for (int i = 0; i < NUM_WORKERS; i++) {
        thread_work[i].a = a;
        thread_work[i].b = b;
        thread_work[i].c = c;
        thread_work[i].start_row = i * rows_per_worker;
        thread_work[i].end_row = (i == NUM_WORKERS - 1) ? 
                                MATRIX_SIZE : (i + 1) * rows_per_worker;
        
        pthread_create(&threads[i], NULL,
                      thread_matrix_multiply, &thread_work[i]);
    }
    
    for (int i = 0; i < NUM_WORKERS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    gettimeofday(&end, NULL);
    long thread_time = (end.tv_sec - start.tv_sec) * 1000000 +
                      (end.tv_usec - start.tv_usec);
    
    // Process implementation
    gettimeofday(&start, NULL);
    
    for (int i = 0; i < NUM_WORKERS; i++) {
        pid_t pid = fork();
        
        if (pid == 0) {
            int start_row = i * rows_per_worker;
            int end_row = (i == NUM_WORKERS - 1) ?
                         MATRIX_SIZE : (i + 1) * rows_per_worker;
            
            process_matrix_multiply(a, b, c, start_row, end_row);
            exit(0);
        }
    }
    
    for (int i = 0; i < NUM_WORKERS; i++) {
        wait(NULL);
    }
    
    gettimeofday(&end, NULL);
    long process_time = (end.tv_sec - start.tv_sec) * 1000000 +
                       (end.tv_usec - start.tv_usec);
    
    printf("Thread implementation time: %ld microseconds\n", thread_time);
    printf("Process implementation time: %ld microseconds\n", process_time);
    
    // Clean up
    munmap(a, matrix_bytes);
    munmap(b, matrix_bytes);
    munmap(c, matrix_bytes);
}

int main() {
    compare_performance();
    return 0;
}

10. 基准测试

这里有一个简单的基准测试工具,用于比较线程和进程性能:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>

#define NUM_ITERATIONS 1000000
#define NUM_RUNS 10

typedef struct {
    long thread_times[NUM_RUNS];
    long process_times[NUM_RUNS];
} benchmark_results_t;

void* thread_work(void* arg) {
    // Simulate some work
    double result = 0.0;
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        result += i * 1.5;
    }
    return NULL;
}

void process_work() {
    double result = 0.0;
    for (int i = 0; i < NUM_ITERATIONS; i++) {
        result += i * 1.5;
    }
}

benchmark_results_t run_benchmarks() {
    benchmark_results_t results;
    struct timeval start, end;
    
    for (int run = 0; run < NUM_RUNS; run++) {
        // Thread benchmark
        gettimeofday(&start, NULL);
        
        pthread_t thread;
        pthread_create(&thread, NULL, thread_work, NULL);
        pthread_join(thread, NULL);
        
        gettimeofday(&end, NULL);
        results.thread_times[run] = (end.tv_sec - start.tv_sec) * 1000000 +
                                  (end.tv_usec - start.tv_usec);
        
        // Process benchmark
        gettimeofday(&start, NULL);
        
        pid_t pid = fork();
        if (pid == 0) {
            process_work();
            exit(0);
        } else {
            wait(NULL);
        }
        
        gettimeofday(&end, NULL);
        results.process_times[run] = (end.tv_sec - start.tv_sec) * 1000000 +
                                   (end.tv_usec - start.tv_usec);
    }
    
    return results;
}

void analyze_results(benchmark_results_t results) {
    double thread_avg = 0.0, process_avg = 0.0;
    long thread_min = results.thread_times[0];
    long thread_max = results.thread_times[0];
    long process_min = results.process_times[0];
    long process_max = results.process_times[0];
    
    for (int i = 0; i < NUM_RUNS; i++) {
        thread_avg += results.thread_times[i];
        process_avg += results.process_times[i];
        
        if (results.thread_times[i] < thread_min)
            thread_min = results.thread_times[i];
        if (results.thread_times[i] > thread_max)
            thread_max = results.thread_times[i];
        if (results.process_times[i] < process_min)
            process_min = results.process_times[i];
        if (results.process_times[i] > process_max)
            process_max = results.process_times[i];
    }
    
    thread_avg /= NUM_RUNS;
    process_avg /= NUM_RUNS;
    
    printf("Thread Performance:\n");
    printf("  Average: %.2f microseconds\n", thread_avg);
    printf("  Min: %ld microseconds\n", thread_min);
    printf("  Max: %ld microseconds\n", thread_max);
    printf("\nProcess Performance:\n");
    printf("  Average: %.2f microseconds\n", process_avg);
    printf("  Min: %ld microseconds\n", process_min);
    printf("  Max: %ld microseconds\n", process_max);
}

int main() {
    printf("Running benchmarks...\n");
    benchmark_results_t results = run_benchmarks();
    printf("\nBenchmark Results:\n");
    analyze_results(results);
    return 0;
}

以下是帮助你可视化线程和进程生命周期及其交互模式的图表:

进程交互模式: image.png

线程交互模式:

image.png

12.进一步阅读

  1. Books

    • "Operating System Concepts" by Silberschatz, Galvin, and Gagne
    • "Advanced Programming in the UNIX Environment" by W. Richard Stevens
    • "The Linux Programming Interface" by Michael Kerrisk
  2. Online Resources

13.结论

线程和进程之间的选择取决于各种因素:

  1. 内存使用
  • 由于独立的地址空间,进程具有更高的内存开销
  • 线程共享内存空间,使其更高效地使用内存
  1. 通信开销
  • 进程间通信更复杂,速度更慢
  • 线程共享内存,因此通信速度更快
  1. 隔离和安全性
  • 进程可以提供更好的隔离和安全性
  • 线程更容易受到攻击进而影响整个进程
  1. 开发复杂性
  • 基于进程的程序通常更易于调试
  • 基于线程的程序需要仔细的同步

以下是一个决策辅助函数:

typedef enum {
    REQUIREMENT_ISOLATION,
    REQUIREMENT_PERFORMANCE,
    REQUIREMENT_COMMUNICATION,
    REQUIREMENT_SECURITY
} requirement_t;

typedef struct {
    int isolation_weight;
    int performance_weight;
    int communication_weight;
    int security_weight;
} requirements_t;

const char* suggest_implementation(requirements_t reqs) {
    int process_score = 0;
    int thread_score = 0;
    
    // Calculate scores based on requirements
    process_score += reqs.isolation_weight * 9;    // Processes excel at isolation
    process_score += reqs.performance_weight * 5;  // Moderate performance
    process_score += reqs.communication_weight * 3; // Poor for communication
    process_score += reqs.security_weight * 9;     // Excellent security
    
    thread_score += reqs.isolation_weight * 3;     // Poor isolation
    thread_score += reqs.performance_weight * 8;   // Excellent performance
    thread_score += reqs.communication_weight * 9; // Excellent communication
    thread_score += reqs.security_weight * 4;      // Moderate security
    
    return (process_score > thread_score) ? 
           "Use Processes" : "Use Threads";
}

// Example usage:
void demonstrate_decision_making() {
    requirements_t reqs = {
        .isolation_weight = 8,    // High importance
        .performance_weight = 5,  // Medium importance
        .communication_weight = 3, // Low importance
        .security_weight = 9      // Critical importance
    };
    
    printf("Suggestion: %s\n", suggest_implementation(reqs));
}

14.参考资料

  1. IEEE POSIX Standard (IEEE Std 1003.1-2017)
  2. Linux Kernel Documentation (processes and threads)
  3. GNU C Library Manual
  4. "Modern Operating Systems" by Andrew S. Tanenbaum
  5. "Understanding the Linux Kernel" by Daniel P. Bovet and Marco Cesati

15. 最后思考

理解线程和进程之间的差异对于系统设计至关重要。以下是一个快速参考实现,展示了两种方法:

#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <sys/time.h>  
#include <string.h>  
#include <sys/mman.h>  
#include <fcntl.h>  

#define NUM_ITEMS 1000  
#define SHARED_MEM_NAME "/shared_mem_example"  

// Requirements structure for decision making  
typedef struct {  
    int isolation_weight;  
    int performance_weight;  
    int communication_weight;  
    int security_weight;  
} requirements_t;  

// Common data structure for both approaches  
typedef struct {  
    int* data;  
    int start_index;  
    int end_index;  
} work_data_t;  

// Common interface for both approaches  
typedef struct {  
    void (*initialize)(void* processor_data);  
    void (*process_data)(void* processor_data, work_data_t* work);  
    void (*cleanup)(void* processor_data);  
    void* processor_data;  
} concurrent_processor_t;  

// Thread-based implementation  
typedef struct {  
    pthread_t* threads;  
    int num_threads;  
    int* shared_data;  
    pthread_mutex_t mutex;  
    work_data_t* work_units;  
} thread_processor_t;  

// Process-based implementation  
typedef struct {  
    pid_t* processes;  
    int num_processes;  
    int* shared_memory;  
    int shm_fd;  
    work_data_t* work_units;  
} process_processor_t;  

// Decision making function  
const char* suggest_implementation(requirements_t reqs) {  
    int process_score = 0;  
    int thread_score = 0;  

    process_score += reqs.isolation_weight * 9;  
    process_score += reqs.performance_weight * 5;  
    process_score += reqs.communication_weight * 3;  
    process_score += reqs.security_weight * 9;  

    thread_score += reqs.isolation_weight * 3;  
    thread_score += reqs.performance_weight * 8;  
    thread_score += reqs.communication_weight * 9;  
    thread_score += reqs.security_weight * 4;  

    return (process_score > thread_score) ? "Use Processes" : "Use Threads";  
}  

// Thread worker function  
void* thread_worker(void* arg) {  
    work_data_t* work = (work_data_t*)arg;  

    for (int i = work->start_index; i < work->end_index; i++) {  
        work->data[i] *= 2;  // Simple data processing  
    }  

    return NULL;  
}  

// Thread implementation functions  
void thread_initialize(void* processor_data) {  
    thread_processor_t* tp = (thread_processor_t*)processor_data;  
    tp->shared_data = malloc(NUM_ITEMS * sizeof(int));  
    tp->work_units = malloc(tp->num_threads * sizeof(work_data_t));  

    // Initialize data  
    for (int i = 0; i < NUM_ITEMS; i++) {  
        tp->shared_data[i] = i;  
    }  

    pthread_mutex_init(&tp->mutex, NULL);  
}  

void thread_process_data(void* processor_data, work_data_t* work) {  
    thread_processor_t* tp = (thread_processor_t*)processor_data;  
    int items_per_thread = NUM_ITEMS / tp->num_threads;  

    // Create and start threads  
    for (int i = 0; i < tp->num_threads; i++) {  
        tp->work_units[i].data = tp->shared_data;  
        tp->work_units[i].start_index = i * items_per_thread;  
        tp->work_units[i].end_index = (i == tp->num_threads - 1) ?   
                                     NUM_ITEMS : (i + 1) * items_per_thread;  

        pthread_create(&tp->threads[i], NULL, thread_worker, &tp->work_units[i]);  
    }  

    // Wait for threads to complete  
    for (int i = 0; i < tp->num_threads; i++) {  
        pthread_join(tp->threads[i], NULL);  
    }  
}  

void thread_cleanup(void* processor_data) {  
    thread_processor_t* tp = (thread_processor_t*)processor_data;  
    pthread_mutex_destroy(&tp->mutex);  
    free(tp->shared_data);  
    free(tp->work_units);  
    free(tp->threads);  
    free(tp);  
}  

// Process worker function  
void process_worker(work_data_t* work) {  
    for (int i = work->start_index; i < work->end_index; i++) {  
        work->data[i] *= 2;  // Simple data processing  
    }  
    exit(0);  
}  

// Process implementation functions  
void process_initialize(void* processor_data) {  
    process_processor_t* pp = (process_processor_t*)processor_data;  

    // Create shared memory  
    pp->shm_fd = shm_open(SHARED_MEM_NAME, O_CREAT | O_RDWR, 0666);  
    ftruncate(pp->shm_fd, NUM_ITEMS * sizeof(int));  

    pp->shared_memory = mmap(NULL, NUM_ITEMS * sizeof(int),  
                            PROT_READ | PROT_WRITE,  
                            MAP_SHARED, pp->shm_fd, 0);  

    pp->work_units = malloc(pp->num_processes * sizeof(work_data_t));  

    // Initialize data  
    for (int i = 0; i < NUM_ITEMS; i++) {  
        pp->shared_memory[i] = i;  
    }  
}  

void process_process_data(void* processor_data, work_data_t* work) {  
    process_processor_t* pp = (process_processor_t*)processor_data;  
    int items_per_process = NUM_ITEMS / pp->num_processes;  

    // Create and start processes  
    for (int i = 0; i < pp->num_processes; i++) {  
        pp->work_units[i].data = pp->shared_memory;  
        pp->work_units[i].start_index = i * items_per_process;  
        pp->work_units[i].end_index = (i == pp->num_processes - 1) ?   
                                     NUM_ITEMS : (i + 1) * items_per_process;  

        pid_t pid = fork();  
        if (pid == 0) {  
            process_worker(&pp->work_units[i]);  
        }  
        pp->processes[i] = pid;  
    }  

    // Wait for processes to complete  
    for (int i = 0; i < pp->num_processes; i++) {  
        waitpid(pp->processes[i], NULL, 0);  
    }  
}  

void process_cleanup(void* processor_data) {  
    process_processor_t* pp = (process_processor_t*)processor_data;  
    munmap(pp->shared_memory, NUM_ITEMS * sizeof(int));  
    shm_unlink(SHARED_MEM_NAME);  
    free(pp->work_units);  
    free(pp->processes);  
    free(pp);  
}  

concurrent_processor_t* create_processor(int is_threaded, int num_workers) {  
    concurrent_processor_t* processor = malloc(sizeof(concurrent_processor_t));  

    if (is_threaded) {  
        thread_processor_t* tp = malloc(sizeof(thread_processor_t));  
        tp->threads = malloc(num_workers * sizeof(pthread_t));  
        tp->num_threads = num_workers;  

        processor->initialize = thread_initialize;  
        processor->process_data = thread_process_data;  
        processor->cleanup = thread_cleanup;  
        processor->processor_data = tp;  
    } else {  
        process_processor_t* pp = malloc(sizeof(process_processor_t));  
        pp->processes = malloc(num_workers * sizeof(pid_t));  
        pp->num_processes = num_workers;  

        processor->initialize = process_initialize;  
        processor->process_data = process_process_data;  
        processor->cleanup = process_cleanup;  
        processor->processor_data = pp;  
    }  

    return processor;  
}  

void print_results(int* data, int size) {  
    printf("First 10 results: ");  
    for (int i = 0; i < 10 && i < size; i++) {  
        printf("%d ", data[i]);  
    }  
    printf("\n");  
}  

int main() {  
    // Choose implementation based on requirements  
    requirements_t reqs = {  
        .isolation_weight = 5,  
        .performance_weight = 8,  
        .communication_weight = 7,  
        .security_weight = 4  
    };  

    const char* suggestion = suggest_implementation(reqs);  
    printf("Based on requirements, suggestion: %s\n", suggestion);  

    int use_threads = (strcmp(suggestion, "Use Threads") == 0);  
    concurrent_processor_t* processor = create_processor(use_threads, 4);  

    // Initialize the processor  
    processor->initialize(processor->processor_data);  

    // Process data  
    work_data_t work = {0}; // Dummy work data, actual work units are created inside  

    struct timeval start, end;  
    gettimeofday(&start, NULL);  

    processor->process_data(processor->processor_data, &work);  

    gettimeofday(&end, NULL);  
    long microseconds = (end.tv_sec - start.tv_sec) * 1000000 +   
                       (end.tv_usec - start.tv_usec);  

    // Print results  
    if (use_threads) {  
        thread_processor_t* tp = (thread_processor_t*)processor->processor_data;  
        print_results(tp->shared_data, NUM_ITEMS);  
    } else {  
        process_processor_t* pp = (process_processor_t*)processor->processor_data;  
        print_results(pp->shared_memory, NUM_ITEMS);  
    }  

    printf("Processing time: %ld microseconds\n", microseconds);  

    // Cleanup  
    processor->cleanup(processor->processor_data);  
    free(processor);  

    return 0;  
}