linux系统中wait()和waitpaid()函数

一、进程的结束

在 Linux 系统中,进程的结束需要通过适当的系统调用来回收资源,防止僵尸进程的产生。常用的两个函数是 `wait` 和 `waitpid`,它们的主要功能是等待子进程状态发生变化,并回收子进程所占用的资源。

1.1 `wait()` 函数


pid_t wait(int *wstatus);
 

`wait()` 函数会导致调用进程进入阻塞状态,直到其一个子进程终止或状态发生变化。它的主要功能是获取子进程的退出状态并回收资源。子进程结束后,系统会将该进程的退出状态传递给父进程,并释放子进程的资源,从而避免僵尸进程的产生。

注意事项:
1. `wait()` 是一个阻塞操作,调用它的进程将等待,直到有一个子进程结束。
2. 子进程的退出状态值只有最低8位有效,可以通过宏 `WEXITSTATUS()` 获取。
3. 如果父进程有多个子进程,`wait()` 会等待任意一个子进程结束,然后返回其 PID。

1.2 获取退出状态

在子进程调用 `exit()` 结束时,它会返回一个退出状态值。这个值只有最低的8位有效,表示范围为 0 到 255。父进程可以使用 `wait()` 或 `waitpid()` 来获取这个状态值。


int status;
wait(&status);
 

要获取子进程的退出状态,父进程可以使用以下宏:
- `WIFEXITED(status)`:检查子进程是否正常退出。
- `WEXITSTATUS(status)`:获取子进程的退出状态值。

1.3 `waitpid()` 函数


pid_t waitpid(pid_t pid, int *wstatus, int options);
 

`waitpid()` 提供了更灵活的方式来等待子进程结束。你可以指定等待的子进程,也可以选择是否阻塞。`pid` 参数可以是以下之一:
- `pid > 0`:等待指定的子进程。
- `pid = -1`:等待任意一个子进程(类似 `wait()`)。
- `pid = 0`:等待与调用进程在同一进程组中的任意子进程。
- `pid < -1`:等待特定进程组中的任意子进程。

`options` 参数可以是以下值:
- `0`:阻塞等待。
- `WNOHANG`:非阻塞等待,如果没有子进程退出,则立即返回。

注意:
- 使用 `WNOHANG` 时,如果没有子进程结束,`waitpid()` 会立即返回,不会阻塞调用进程。因此,通常需要将其放在循环中,轮询检查子进程的状态。

 二、线程

 2.1 什么是线程?

线程是进程中的一个执行单元,它是操作系统调度的最小单位。与进程相比,线程被称为“轻量级进程”(Lightweight Process),因为线程的创建和切换比进程更高效,资源开销更小。

2.2 为什么需要线程?

线程的引入是为了解决多任务并发处理的问题。与进程相比,线程具有以下优点:
1. 创建和销毁开销小:线程共享进程的资源,创建和销毁的开销较小。
2. 上下文切换快:线程之间的上下文切换比进程更快。
3. 资源共享:同一进程内的线程共享代码段、数据段和操作系统资源,如文件描述符和信号处理程序。

2.3 线程的组成

线程包括以下几个主要部分:
- 线程 ID (TID):线程的唯一标识符。
- 程序计数器:指向当前执行的指令。
- 寄存器集合:保存当前任务的状态。
- 栈:线程的私有栈,用于函数调用和局部变量存储。

2.4 进程与线程的关系

线程是进程中的执行单元,同一进程中的多个线程共享进程的资源,如代码段、数据段、打开的文件描述符和信号处理程序。线程之间的关系特点如下:
- 线程是存在于进程中的,属于同一进程的线程共享资源。
- 一个线程结束并不意味着进程结束,除非进程内的所有线程都结束。

三、线程编程

3.1 创建线程

使用 `pthread_create()` 函数可以创建一个新线程:


int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
 

- `thread`:返回创建的线程 ID。
- `attr`:线程属性,通常为 `NULL`,表示默认属性。
- `start_routine`:线程执行的函数,线程启动后将执行这个函数。
- `arg`:传递给 `start_routine` 的参数。

注意: 编译时需要链接 pthread 库 (`-lpthread`)。

 3.2 线程退出

1. `pthread_exit()`:结束线程并返回一个退出状态值。


void pthread_exit(void *retval);
 

- `retval`:退出状态值,其他线程可以通过 `pthread_join()` 获取。

2. `pthread_join()`:等待指定线程结束,并获取其退出状态值。


int pthread_join(pthread_t thread, void **retval);
 

- `thread`:要等待的线程 ID。
- `retval`:用于保存线程的退出状态值。

注意: 如果主线程调用了 `pthread_exit()`,它只会结束主线程本身,而不是整个进程,其他线程将继续运行,直到它们自行结束或被 `pthread_join()` 等待。

3.3 线程锁与同步

线程之间的共享资源访问可能会导致数据竞争和不一致性问题。为了解决这些问题,需要使用线程锁(如 `pthread_mutex_t`)来同步线程操作。典型的线程锁函数包括:
- `pthread_mutex_lock()`:加锁,阻止其他线程访问被锁定的资源。
- `pthread_mutex_unlock()`:解锁,允许其他线程访问资源。
- `pthread_mutex_trylock()`:尝试加锁,如果锁已经被占用,则立即返回。

四、死锁的发生原理与应对方法

4.1 死锁的定义

死锁是指两个或多个线程因争夺资源而互相等待,导致它们都无法继续执行。常见的死锁场景包括两个线程分别持有对方所需的资源,并且都在等待对方释放资源。

4.2 死锁的条件

根据 Coffman 的死锁四个必要条件,一个系统中如果同时满足以下四个条件,可能会发生死锁:
1. 互斥:资源在同一时间只能被一个线程占用。
2. 持有并等待:线程持有一个资源,同时等待获取其他资源。
3. 不剥夺:资源不能被强制剥夺,必须由线程主动释放。
4. 循环等待:存在一个线程集合,其中的每个线程都在等待另一个线程已经持有的资源。

4.3 预防和避免死锁的方法

为了防止死锁的发生,可以采取以下措施:
- 资源有序分配:规定资源的获取顺序,所有线程必须按照相同的顺序申请资源,避免循环等待。
- 尝试锁定:使用 `pthread_mutex_trylock()` 尝试获取锁,如果失败则释放已持有的资源并重新尝试。
- 超时等待:设定资源申请的超时时间,如果超过时间未获取资源则放弃申请。
- 死锁检测与恢复:周期性地检查系统中是否存在死锁,并在检测到死锁时强制剥夺资源或终止线程以解除死锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值