一、进程的结束
wait 阻塞方式 ---调用进程 一般不做额外的事情
waitpid 非阻塞的方式 --- 调用进程 逻辑一般不受影响
waitpid 想要处理到子进程必须套在循环
1.1 、wait()
pid_t wait(int *wstatus)
wait() 等待进程改变状态,在调用进程的子进程中,获得子进程改变状态改变的相关信息,进程结束、信号暂停、恢复。它允许系统释放子进程相关的进程。相当于子进程进入僵尸态,进行释放。
功能: 获取子进程的退出状态
回收资源//会让僵尸态的子进程销毁
注意:
1、wait的本身是一个阻塞操作,会使调用进程操作阻塞
1.2、获取退出状态
子进程eixt (退出状态值),只有最低8位有效[0~ 255]。
stauts & 377 返回给父进程,父进程接收,是因为exit函数使其正常结束,而exit 执行后的返回值会传递给父进程,父进程接收,而这个接收对于wait来说,刚好接收到了他的状态发生改变,将其状态值接收到,接收到,我们放到变量值里面。wait这个函数带出的值除了我们所想的值,还有其他的值。wait和waitpid会将数据保存在所指定的int数据中。他指定的该数据可以被宏检查
父进程
wait(& status)
获取到退出状态值
WIFEXITTED() 先判断是否为正常退出,正常返回真
WEXITSTATUS()获取到exit传递的退出状态值,就是带出来的最低有效八位
1.3、waitpid()
pid_t waitpid(pid_t pid,int *wstatus,int options);
功能:等待子进程状态发生变化
参数:pid 表示等待pid号的子进程结束
@pid > 0 //等待指定的子进程发生状态改变
pid = -1
- -1等待所有子进程的结束(只要一个进程结束了,他就进行回收,自己理解是,如果都在等,但是如果多个里面有一个结束,那么其也进行回收,结束了这次的等待),如果想等所有都结束,调用相同的次数。
option // ----- 0默认阻塞
------- WNOHANG 非阻塞
注意
- 阻塞,是指子进程没有结束之前,wait之后的都不做,等待子进程结束。对父进程的处理有影响
- 非阻塞:是指子进程没有结束之前,自己不等待,该做啥就做啥,对父进程的处理逻辑没有影响。父进程会去查看子进程状态改变,但是,如果没有发生改变,父进程不阻塞,整个程序继续往下。
- 非阻塞必须套在循环中处理 //轮询,因为如果不套在循环中的话,他进行判断一次之后,要是没有,就正常执行他的进程,而后面他进程改变了之后,我们已经结束了父进程的操作,不会对其进行操作。
- 在回收的时候注意是父进程获得的pid号,我们在子进程调用的函数获得的pid号如果放在子进程里面,但是注意两者的数据是独立的。
二、线程
2.1、什么是线程
线程 ------轻量级的进程(light weight)
2.2、为什么需要线程?
进程的产生 ----------fork
线程 -------------轻量级的进程
进程 --------------重量级的进程
轻重体现(创建)
1、资源线程比较少的资源,开辟空间的时候消耗比较少,执行效率块。
线程 称为 cpu 执行的最小单位 (侧重的是干活)
进程 称为 资源分配的基本单位
线程创建和调度时空开销都比进程小
2.3、线程的组成
- 线程tid //thread
- 程序计数器
- 寄存器集合
- 栈
2.4、进线程之间的关系
他与属于同一进程的其他线程共享代码段、数据段和其他操作系统资源,如打开文件和信号。
进程获取资源,为了执行多个程序,又在里面创建了线程、线程共享了进程所获取的资源,线程在里面又需要自己的东西。
a、线程是存在于进程中的
b、线程共享了进程的资源(数据段,代码段、打开的信号)
c、线程结束,不一定导致进程结束。
三、线程的编程
3.1、pthread_create()
ptread_t long int (本质)
- 新的线程的执行,是通过一个执行的函数,
- 功能:该函数可以创建指定的一个线程。
- 参数: @thread 线程id,需要实现定义并由该数返回。
- atrr 线程属性,一般是NULL,表示默认属性。(可结合性+分离属性)这两种属性决定线程是怎么回收。
- 默认--需要手动收
- 分离属性 ----系统自动回收
5.start_routine ---------线程执行函数(线程回调函数)(线程干活在该函数里面写着、自己写
6.回调(本来是调用系统的函数,但是系统又调用了自己写的函数,称为回调函数(通过指针可以实现))
- //线程回调函数----完成线程任务功能的函数
- //需要调用者 自己完成
- 指向[指针函数] 的函数指针。
7.arg 是传递给回掉函数的参数,即参数的指针函数参数,要是没有填写NULL
本质上是一个函数的名称即可,称为th 回调函数,是线程的执行空间。
返回值
- 成功 0
- 失败-1
注意
- 编译的时候注意加上-lpthread
- 注意在进程结束的时候,线程也会因为他依附的空间结束了,他自己也没有办法拿到。
- 把一个执行称为一个执行流
- 把主函数所在的执行流 ------ 主线程
- 其他的线程 ---------- 子线程(次线程)
- 主子没有关系,各个线程间 地位是平等(对等)
- 主线程的pid号可以通过pthread_self()//在那个线程中调用,获得的就是那个线程的pid号
3.2、线程退出
1、pthread_exit()
指定一个退出状态值,在相同的进程中,另一个线程可以获得其退出状态值
是可以进行传值进去——
功能:
结束调用的线程
参数:retval //退出状态值 // 传的是退出状态值的地址
注意
- pthread_exit 本身表示结束线程
- 如果用在main函数中 表示结束主线程
- 主线程结束并不表示进程结束;此时执行逻辑,主线程执行流结束,进程会在其余线程都结束后,结束
2、pyhread_join()
通过函数pthread_join 获得,相当于wait,等待一个线程的结束
功能:等待线程结束
参数
thread --- tid,线程pid 传递那个pid 表示等待那个线程
,要求这个线程必须是可结合性(默认的就是可结合性)
retval ---用来保存退出状态值所在空间的地址
返回值
成功返回0
失败返回error number