此文章为本人学习笔记,若有错误求指正,自学推荐书籍《Linux/UNIX系统编程手册》,需要该书籍pdf文档可无偿分享。
1.进程和线程概述
在Linux系统编程中,进程和线程是两个至关重要的概念。进程是操作系统资源分配的基本单位,而线程是CPU调度的基本单位。理解进程和线程的概念、创建和管理方法,对于开发高效、稳定的应用程序至关重要。
2. 进程(Process)
2.1 进程的定义
进程是一个正在运行的程序的实例。每个进程都有自己独立的地址空间,包含代码段、数据段、堆和栈等。
2.2 进程的生命周期
一个进程的生命周期通常包括以下几个阶段:
- 创建:使用fork()或vfork()系统调用创建一个新进程。
- 执行:进程被调度到CPU上执行。
- 等待:进程等待某个条件(如I/O操作)完成。
- 终止:进程完成执行或被终止。
2.3 创建进程
在Linux中,fork()系统调用用于创建一个新进程。fork()会创建一个子进程,子进程是父进程的副本。子进程从父进程中继承了地址空间、文件描述符等资源。
int main() {
pid_t pid;
pid = fork();
if (pid < 0) { // 出错
fprintf(stderr, "Fork failed");
return 1;
} else if (pid == 0) { // 子进程
printf("This is the child process, PID: %d\n", getpid());
} else { // 父进程
printf("This is the parent process, PID: %d\n", getpid());
wait(NULL); // 等待子进程结束
}
return 0;
}
vfork()是fork()的一个变种,用于创建新进程。与fork()不同的是,vfork()创建的子进程与父进程共享地址空间,直到调用exec()或exit()为止。
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pid_t pid;
pid = vfork();
if (pid < 0) { // 出错
fprintf(stderr, "vfork failed");
return 1;
} else if (pid == 0) { // 子进程
printf("This is the child process, PID: %d\n", getpid());
_exit(0);
} else { // 父进程
printf("This is the parent process, PID: %d\n", getpid());
}
return 0;
}
2.4 进程间通信(IPC)
进程间通信是指在不同进程间传递数据或信号。常见的进程间通信方法包括:
- 管道(Pipe)
- 信号(Signal):
- 共享内存(Shared Memory)
- 消息队列(Message Queue)
- 套接字(Socket)
管道(Pipe):
用于在父子进程间通信。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int fd[2];
pid_t pid;
char write_msg[] = "Hello, world!";
char read_msg[20];
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
if (pid > 0) { // 父进程
close(fd[0]); // 关闭读端
write(fd[1], write_msg, strlen(write_msg) + 1);
close(fd[1]); // 关闭写端
} else { // 子进程
close(fd[1]); // 关闭写端
read(fd[0], read_msg, sizeof(read_msg));
printf("Read from pipe: %s\n", read_msg);
close(fd[0]); // 关闭读端
}
return 0;
}
信号(Signal):
用于通知进程某些事件的发生。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handle_signal(int sig) {
printf("Received signal %d\n", sig);
}
int main() {
signal(SIGINT, handle_signal); // 注册信号处理程序
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
共享内存(Shared Memory):
允许多个进程访问同一块内存区域。
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
key_t key = 1234;
int shmid;
char *data;
shmid = shmget(key, 1024, 0666|IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(1);
}
data = (char *)shmat(shmid, (void *)0, 0);
if (data == (char *)(-1)) {
perror("shmat");
exit(1);
}
strcpy(data, "Hello, Shared Memory!");
printf("Data written in memory: %s\n", data);
if (shmdt(data) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
消息队列(Message Queue):
允许进程发送和接收消息。
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct msg_buffer {
long msg_type;
char msg_text[100];
} message;
int main() {
key_t key;
int msgid;
key = ftok("progfile", 65);
msgid = msgget(key, 0666 | IPC_CREAT);
message.msg_type = 1;
strcpy(message.msg_text, "Hello, Message Queue!");
msgsnd(msgid, &message, sizeof(message), 0);
printf("Data send is : %s \n", message.msg_text);
msgrcv(msgid, &message, sizeof(message), 1, 0);
printf("Data received is : %s \n", message.msg_text);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
套接字(Socket):
用于网络通信。
2.5 进程调度
Linux使用多级反馈队列调度算法(Multilevel Feedback Queue Scheduling)来管理进程的调度。每个进程都有一个优先级,调度程序根据优先级决定哪个进程可以使用CPU。
3. 线程(Thread)
3.1 线程的定义
线程是进程中的一个执行单元,一个进程可以包含多个线程。线程共享进程的地址空间和资源,但有自己的栈和寄存器。
3.2 线程的创建
在Linux中,线程的创建通常使用POSIX线程库(pthread)。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* threadFunc(void* arg) {
printf("This is a thread\n");
pthread_exit(NULL);
}
int main() {
pthread_t thread;
int rc;
rc = pthread_create(&thread, NULL, threadFunc, NULL);
if (rc) {
printf("Error:unable to create thread, %d\n", rc);
exit(-1);
}
pthread_join(thread, NULL); // 等待线程结束
return 0;
}
3.3 线程同步
由于线程共享同一进程的资源,因此需要同步机制来避免资源竞争和数据不一致。常见的线程同步机制包括:
- 互斥锁(Mutex)
- 读写锁(Read-Write Lock)
- 条件变量(Condition Variable)
- 信号量(Semaphore)
互斥锁(Mutex)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t lock;
void* threadFunc(void* arg) {
pthread_mutex_lock(&lock);
printf("Thread %ld has entered critical section\n", pthread_self());
sleep(1);
printf("Thread %ld is leaving critical section\n", pthread_self());
pthread_mutex_unlock(&lock);
pthread_exit(NULL);
}
int main() {
pthread_t thread1, thread2;
pthread_mutex_init(&lock, NULL);
pthread_create(&thread1, NULL, threadFunc, NULL);
pthread_create(&thread2, NULL, threadFunc, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
读写锁(Read-Write Lock)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_rwlock_t rwlock;
void* readFunc(void* arg) {
pthread_rwlock_rdlock(&rwlock);
printf("Reader %ld has entered critical section\n", pthread_self());
sleep(1);
printf("Reader %ld is leaving critical section\n", pthread_self());
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);
}
void* writeFunc(void* arg) {
pthread_rwlock_wrlock(&rwlock);
printf("Writer %ld has entered critical section\n", pthread_self());
sleep(2);
printf("Writer %ld is leaving critical section\n", pthread_self());
pthread_rwlock_unlock(&rwlock);
pthread_exit(NULL);
}
int main() {
pthread_t reader1, reader2, writer1;
pthread_rwlock_init(&rwlock, NULL);
pthread_create(&reader1, NULL, readFunc, NULL);
pthread_create(&reader2, NULL, readFunc, NULL);
pthread_create(&writer1, NULL, writeFunc, NULL);
pthread_join(reader1, NULL);
pthread_join(reader2, NULL);
pthread_join(writer1, NULL);
pthread_rwlock_destroy(&rwlock);
return 0;
}
条件变量(Condition Variable)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
int count = 0;
void* threadFunc(void* arg) {
pthread_mutex_lock(&mutex);
count++;
if (count == 2) {
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main() {
pthread_t thread1, thread2;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&thread1, NULL, threadFunc, NULL);
pthread_create(&thread2, NULL, threadFunc, NULL);
pthread_mutex_lock(&mutex);
while (count < 2) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
printf("Both threads have finished execution\n");
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
信号量(Semaphore)
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
sem_t sem;
void* threadFunc(void* arg) {
sem_wait(&sem);
printf("Thread %ld has entered critical section\n", pthread_self());
sleep(1);
printf("Thread %ld is leaving critical section\n", pthread_self());
sem_post(&sem);
pthread_exit(NULL);
}
int main() {
pthread_t thread1, thread2;
sem_init(&sem, 0, 1);
pthread_create(&thread1, NULL, threadFunc, NULL);
pthread_create(&thread2, NULL, threadFunc, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
sem_destroy(&sem);
return 0;
}
3.4 线程的调度
Linux中的线程调度与进程调度类似,都是基于优先级和时间片的轮转调度(Round-Robin Scheduling)。调度器根据线程的优先级和时间片分配CPU时间。
4. 进程与线程的对比
特性 | 进程 | 线程 |
内存空间 | 独立 | 共享 |
创建开销 | 高 | 低 |
通信开销 | 高 | 低 |
资源共享 | 不共享 | 共享 |
调度 | 独立调度 | 独立调度 |
5. 总结
进程和线程在Linux系统编程中扮演着重要角色。进程是资源分配的基本单位,线程是CPU调度的基本单位。理解它们的基本概念、创建与管理方法以及它们之间的区别和联系,是编写高效稳定的Linux应用程序的关键。在实际开发中,根据具体需求选择合适的并发模型可以显著提高程序的性能和响应速度。通过合理使用进程和线程的同步机制,可以避免资源竞争和数据不一致的问题,从而提高程序的可靠性。