一、wait函数
1.wait函数
#include <sys/wait.h>
pid_t wait(int *status);
wait函数有两个作用:
1.获取子进程 的退出状态
当父进程要获取子进程的退出状态时,子进程里需要使用exit函数(exit(退出状态值)退出状态值只有低八位有效,有效值的范围为0~255;
父进程通过wait或者waitpid函数来获取到推出状态值
wait函数中有系统定义的宏:
WIFEXITED(status)
: 如果子进程正常退出,则返回非零值。WEXITSTATUS(status)
: 在子进程正常退出时,提取子进程的退出状态。-
WIFSIGNALED(status)
- 说明:如果子进程是因为接收到一个未捕获的信号而终止的(即异常终止),则返回非零值。
- 用途:检查子进程是否因为信号而异常终止。
-
WTERMSIG(status)
- 说明:在子进程异常终止的情况下(即
WIFSIGNALED(status)
为真),此宏返回导致子进程终止的信号编号。 - 用途:获取导致子进程异常终止的信号编号。
- 说明:在子进程异常终止的情况下(即
2.回收资源
注意点:wait本身是一个阻塞操作,会使调用者阻塞。
2.waitpid函数
waitpid函数与wait函数有类似的作用。
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
参数说明
-
pid
:pid > 0
: 等待进程ID等于pid
的那个子进程。pid == 0
: 等待进程组ID与调用进程相同的任意子进程。pid < -1
: 等待进程组ID等于|pid|
(绝对值)的任意子进程。pid == -1
: 等待任意子进程(相当于调用wait
函数)。
-
status
:- 指向一个整数,用于存储子进程的退出状态。与
wait
函数相同,若不关心状态,可以传入NULL
。
- 指向一个整数,用于存储子进程的退出状态。与
-
options
:- 提供额外的选项来控制函数的行为。常见的选项有:
WNOHANG
: 如果没有子进程退出,则立即返回,不等待,即非阻塞状态。
- 提供额外的选项来控制函数的行为。常见的选项有:
返回值
- 成功时返回子进程的进程ID(PID)。
- 如果没有子进程符合指定的条件,
waitpid
返回0
(当options
包含WNOHANG
时)。 - 如果调用失败(例如,
pid
不合法),则返回-1
并设置errno
。
阻塞和非阻塞
阻塞状态下,父进程会等待子进程的状态发生改变,才会执行父进程后面的逻辑
非阻塞状态下,父进程会去查看子进程的状态是否发生改变,父进程不阻塞,整个父进程的逻辑继续往下执行。
非阻塞必须套在循环中,确保可以一直轮询子进程的状态。
二、线程
1.线程的概念
“线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它
与属于同一进程的其他线程共享代码段、数据段和其他操作系统资源,如打开文件和信号。
一个传统重量级(heavyweight)的进程只有单个控制线程。如果进程有多个控制线程,那
么它能同时做多个任务。”
线程又称为轻量级进程(light weight process)它是进程(Process)中的一个子任务.
线程和进程的关系:
进程与线程之间的关系可以总结为:进程包含线程,一个进程至少包含一个线程(即主线程),多个线程在同一进程中并发执行任务。
线程结束但是 进程不一定结束。
线程和进程的区别:
进程是资源分配的基本单位:每个进程都有自己独立的内存空间、文件描述符、全局变量、代码段等资源。操作系统为每个进程分配这些资源,确保进程之间的隔离和独立性。
线程是CPU调度和执行的基本单位:线程是运行在进程内部的执行单元。一个进程可以包含一个或多个线程,所有线程共享进程的资源,但它们可以独立调度和执行。每个线程有自己的栈空间、寄存器和程序计数器,但它们共享同一进程的内存地址空间和其他资源。
为什么要用线程:使用线程可以大幅度的提高程序的运行速度和减少空间占用,使用多线程创建线程和调度都占用的资源都比多进程占用的资源少
2.线程的组成
线程上下文包含线程在执行时所需的所有信息。它通常包括:
- 线程ID(Thread ID):线程唯一的标识符
- 程序计数器(Program Counter, PC):指示当前线程执行的指令地址。每个线程都有自己的程序计数器,以确保线程能从上次停止的位置继续执行。
- 栈(Stack):用于存储线程的局部变量、函数调用信息(如返回地址和调用者的上下文)等。每个线程有自己的栈,以支持线程的函数调用和局部变量存储。
- 寄存器(Registers):线程的寄存器用于存储计算结果、地址等信息。每个线程有独立的寄存器集合。
3.线程编程
a. 线程创建
pthread_create
- 功能:创建一个新线程。
-
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
- 参数:
thread
:指向pthread_t
类型的变量,用于接收新线程的ID。attr
:线程的属性,通常为NULL
使用默认属性(可结合性)(可结合性和分离属性)- 默认属性 结束后需要手动进行回收
- 分离属性 结束后自动回收
start_routine
:线程执行的函数, 函数指针 必须接受一个void*
类型的参数并返回void*
。本质上是一个函数的名称,该函数也被成为线程回调函数,通常需要调用者自己实现。arg
:传递给start_routine
的参数。
- 返回值:0表示成功,其他值表示错误代码。
线程创建成功后,关系如图所示
主函数所在的执行流称为主线程,其他的线程执行流称为子线程 。
各个线程间的地位是对等的。
pthread_self()函数可以获取到调用该函数的线程的tid。
b.线程的执行体现在线程的执行函数中(回调函数)
b.线程的退出
pthread_exit
- 功能:终止调用线程,并返回一个值给线程的调用者。
-
void pthread_exit(void *retval);
-
- 参数:
retval
:线程的返回值,可以是任何void*
类型的指针。
- 返回值:无返回值。调用
pthread_exit
后线程立即结束。
- 参数:
如果用在main函数中 表示结束主线程
主线程结束不代表进程结束,
当主线程结束后,进程会在其余线程都结束后才会结束。
-
pthread_join
- 功能:等待指定线程结束,并获取线程的返回值。
-
int pthread_join(pthread_t thread, void **retval);
- 参数:
thread
:要等待的线程ID。retval
:指向void*
类型的指针,用于接收线程的返回值。
- 返回值:0表示成功,其他值表示错误代码。
- 注意:
线程退出时,可以带出退出状态值,
但是传的是,退出状态值对应空间的地址