Linux/C++ 进程与线程核心概念
进程是程序在数据集合上的执行实例,是系统资源分配与调度的基本单位。
每个进程对应一个进程控制块(PCB),它是进程存在的唯一标志。PCB 存储了以下信息:
- 进程描述信息:进程标识符、用户标识符
- 控制与管理信息:进程状态、优先级
- 资源清单:内存地址空间、打开文件列表、I/O 设备信息
- CPU 上下文:进程切换时,CPU 寄存器的值保存在 PCB 中,以便恢复断点执行
进程的创建
使用 fork() 创建子进程,子进程会复制父进程的内存空间。
pid_t pid = fork();
if (pid < 0) {
// 创建失败
} else if (pid == 0) {
// 子进程
} else {
// 父进程
}
getpid() 返回当前进程 PID,getppid() 返回父进程 PID。
执行新程序
execve() 系列函数用于替换当前进程映像,执行新程序。成功时不返回,失败返回 -1。
char *args[] = {"/bin/ls", "-l", NULL};
execve("/bin/ls", args, environ);
僵尸进程
子进程已结束但父进程未回收其状态信息,子进程变为僵尸进程。
危害:占用内核数据结构,消耗进程号,导致系统无法创建新进程。
解决方法:
- 父进程调用
wait()或waitpid()阻塞等待 - 注册
SIGCHLD信号处理函数
孤儿进程
父进程先于子进程结束,子进程被 init 进程(PID=1)领养。
进程间通信
匿名管道
内核中的缓冲区,用于有亲缘关系的进程间通信。
int fd[2];
pipe(fd);
if (fork() == 0) {
close(fd[1]);
read(fd[0], buf, sizeof(buf));
} else {
close(fd[0]);
write(fd[1], "hello", 5);
}
有名管道
通过文件系统路径标识,可用于任意进程间通信。
mkfifo("/tmp/myfifo", 0664);
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "data", 4);
close(fd);
unlink("/tmp/myfifo");
共享内存
映射一块内存区域,多个进程直接读写。
int shm_fd = shm_open("/myshm", O_CREAT | O_RDWR, 0664);
ftruncate(shm_fd, 1024);
void *ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
// 直接读写 ptr
munmap(ptr, 1024);
shm_unlink("/myshm");
消息队列
独立于发送和接收进程,由内核维护消息链表。
struct mq_attr attr = { .mq_maxmsg = 10, .mq_msgsize = 256 };
mqd_t mq = mq_open("/myqueue", O_RDWR | O_CREAT, 0664, &attr);
mq_send(mq, "msg", 3, 0);
char buf[256];
mq_receive(mq, buf, 256, NULL);
mq_close(mq);
mq_unlink("/myqueue");
信号
用于通知进程事件发生,不传递数据。
常用信号:
SIGINT(2): Ctrl+CSIGKILL(9): 强制终止SIGTERM(15): 终止信号SIGCHLD(17): 子进程状态变化
signal(SIGINT, [](int sig) {
cout << "捕获中断信号" << endl;
exit(0);
});
进程终止
正常终止:return、exit()、_exit() 等。异常终止:abort()、信号、线程取消。
exit() 会执行清理(刷新缓冲区、关闭文件),_exit() 不做清理直接退出。
线程
线程是进程内的执行单元,同一进程的线程共享代码段、数据段、文件资源,但各有独立栈和寄存器。
创建线程
Linux POSIX 线程:
pthread_t tid;
pthread_create(&tid, NULL, [](void*) -> void* {
cout << "线程执行" << endl;
return nullptr;
}, NULL);
C++11 std::thread:
#include <thread>
thread t1([](string s) { cout << s; }, "hello");
thread t2; // 无关联线程
t1.join(); // 等待结束
t2 = thread(foo);
等待与分离
join() 阻塞当前线程直到目标线程结束。detach() 分离线程,使其自行运行。
线程终止
POSIX:pthread_exit() 或 pthread_cancel()。C++ 线程函数返回后自动终止。
同步机制
互斥锁
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mtx);
// 临界区
pthread_mutex_unlock(&mtx);
C++ std::mutex + std::lock_guard 提供 RAII 风格。
mutex m;
{
lock_guard<mutex> lock(m);
// 自动加解锁
}
读写锁
允许多个读线程同时访问,写线程独占。
#include <shared_mutex>
shared_mutex rw;
{
shared_lock<shared_mutex> read_lock(rw);
// 读操作
}
{
unique_lock<shared_mutex> write_lock(rw);
// 写操作
}
自旋锁
忙等待,不阻塞线程。适用于短时间锁定。
atomic_flag flag = ATOMIC_FLAG_INIT;
void lock() { while(flag.test_and_set()); }
void unlock() { flag.clear(); }
条件变量
用于线程间等待某个条件满足。
condition_variable cv;
mutex cv_m;
bool ready = false;
// 等待线程
unique_lock<mutex> lk(cv_m);
cv.wait(lk, []{ return ready; });
// 通知线程
{
lock_guard<mutex> lk(cv_m);
ready = true;
}
cv.notify_one();
信号量
#include <semaphore.h>
sem_t sem;
sem_init(&sem, 0, 1);
sem_wait(&sem);
sem_post(&sem);
sem_destroy(&sem);
C++ 异步操作
std::future 与 std::async
future<int>> f = async(launch::async, [](int x) {
this_thread::sleep_for(1s);
return x * x;
}, 5);
cout << f.get() << endl; // 阻塞直到结果就绪
std::promise
手动设置异步结果。
promise<int> p;
future<int> f = p.get_future();
thread t([](promise<int> p) { p.set_value(42); }, move(p));
cout << f.get() << endl;
t.join();
std::packaged_task
封装可调用对象为异步任务。
packaged_task<int(int, int)> task([](int a, int b) { return a + b; });
future<int> f = task.get_future();
thread(move(task), 3, 4).detach();
cout << f.get() << endl;
