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

1.介绍

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

2.核心概念

基本差异

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

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

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

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

以下是演示:

 

c

代码解读

复制代码

#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. 内存架构比较

内存布局分析

 

c

代码解读

复制代码

#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时间来进行上下文切换。这里的基准测试如下:
 

c

代码解读

复制代码

#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. 内存访问模式: 线程共享相同的地址空间,从而提高了缓存利用率。 以下是一个演示:
 

c

代码解读

复制代码

#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. 管道和命名管道: 进程通过管道进行通信,管道需要系统调用和数据复制。 示例:
 

c

代码解读

复制代码

#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 cl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值