一、进程回收 waitpid
c
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:回收指定子进程的空间
参数
pid:要回收的子进程 PIDwstatus:保存子进程结束状态空间的首地址options0:阻塞回收WNOHANG:非阻塞回收
返回值
- 成功:返回回收子进程 ID
- 失败:返回
-1 - 设置
WNOHANG非阻塞,如果子进程没有结束返回0
💡 对比:wait 只能阻塞随机回收子进程;waitpid 可以指定进程,支持非阻塞,实际开发用的更多。
二、exec 函数族
c
extern char **environ;
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, ..., (char *) NULL, char * const envp[] );
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *file, char *const argv[], char *const envp[]);
功能 :利用进程空间执行另一段代码,调用成功之后,原代码后面全部不会执行。
后缀记忆口诀:
l:参数以列表形式传递,逐个写参数,以NULL结尾v:参数以指针数组形式传递,传字符串数组p:在系统环境变量PATH下查找文件名,可以不用写完整路径e:执行新代码时更新进程环境变量,传入自定义环境变量表
返回值
- 成功:没有返回,直接运行新程序
- 失败:返回
-1
⚠️ exec 只是替换进程地址空间,PID 不变,不会创建新进程,一般搭配 fork 使用。
三、环境变量
进程在执行代码过程中临时存放信息的变量,称为环境变量,全局所有子进程都可以继承。
setenv
c
int setenv(const char *name, const char *value, int overwrite);
功能:设置环境变量的值
name:环境变量名称value:设置环境变量的值overwrite:非 0 覆盖旧值;0 不覆盖旧值
返回:成功返回 0,失败返回 -1
四、线程
1. 什么是线程
线程是一个轻量级的进程,一个任务创建、调度、消亡的过程。
进程和线程核心区别
- 进程:操作系统资源分配最小单元
- 进程独占 0-4G 虚拟内存空间:文本段、数据段、堆段、系统数据段。每个进程完全独立。
- 线程:操作系统任务调度最小单元
- 线程存在于进程空间内部;只有自己独立栈空间,其余堆、全局、文本段全部和进程内其他线程共享。
✅ 内存共享总结
- 独享:线程栈空间(默认 8M),局部变量
- 共享:全局变量、静态变量、字符串常量、堆区
线程调度
宏观并行,微观串行;CPU 时间片轮转调度。
线程消亡
线程结束必须回收线程空间,否则产生僵尸线程。
进程 vs 线程接口对照表
| 进程 | 线程 |
|---|---|
| fork | pthread_create |
| exit | pthread_exit |
| wait | pthread_join |
2. 线程核心 API
① pthread_create 创建线程
c
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread:存放线程 ID(TID) 空间首地址attr:线程属性,栈大小、优先级等,填NULL使用默认属性start_routine:线程函数指针,线程要执行的函数arg:传给线程函数的参数
返回:成功返回 0;失败返回错误码。
② pthread_exit 结束线程
c
void pthread_exit(void *retval);
retval:线程结束传出的状态,可被pthread_join接收。
💡 注意:线程函数里面不要直接 return,优先用
pthread_exit;main 函数 return 会直接结束整个进程,杀死所有子线程。
③ pthread_join 回收线程
c
int pthread_join(pthread_t thread, void **retval);
thread:待回收线程 TIDretval:接收线程退出状态的二级指针,可以传NULL不接收
返回:成功返回 0;失败返回错误码。
特性:阻塞等待线程结束,线程没跑完函数就卡在 join 这里。
3. 开发练习思路
- 创建 N 个线程,每个线程打印自己 TID,模拟多任务并发
- 多线程任务:采集、存储、显示、日志四个任务,各自 sleep 不同时间模拟业务耗时
- 主线程监听终端输入,控制子线程启停
⚠️ 高频踩坑点
- 共享全局变量多线程同时读写,会出现数据竞争,需要后续加锁。
- 传给线程函数的参数不要传局部变量,线程调度晚,局部变量已经销毁,会野指针。
- 忘记
pthread_join,产生僵尸线程,内存泄漏。 - main 函数直接 return,所有子线程直接被杀死,没有机会执行完。
补充学习思维导图总结
- 进程:fork/waitpid + exec 替换程序 + 环境变量
- 线程:轻量级,共享地址空间,私有栈;
pthread_create / pthread_exit / pthread_join - 区别:进程资源独立开销大;线程共享资源开销小,但是有并发数据竞争问题。