[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介

目录

第1章 pthread简介

1.1 简介

1.2 线程查看命令

1.3 线程与进程的区别

1.4 并发性(concurrency)和并行性(parallel)是两个概念

第2章 核心数据结构

2.1 线程管理

2.2 线程同步

第3章 接口函数

3.1 线程控制函数(简介起见,省略参数):

3.2 线程属性函数:

3.3 互斥锁mutex函数:

3.4 条件变量函数:

3.5 线程私有存储(Thread-local storage):

3.6 同步屏障函数

3.7 其它多线程同步函数:

3.8 工具函数:

3.9 信号量函数,包含在semaphore.h中:

3.10 共享内存函数,包含在sys/mman.h中,链接时使用rt库:

4章 带pthread库的编译


第1章 pthread简介

1.1 简介

Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。Linux下pthread是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似于fork()。因此,线程又称为轻量级进程。

Pthreads定义了一套C语言的类型、函数与常量,它以pthread.h头文件和一个线程库实现。

Pthreads API中大致共有100个函数调用,全都以"pthread_"开头,并可以分为四类:

  • 线程管理,例如创建线程,等待(join)线程,查询线程状态等。

  • 互斥锁(Mutex):创建、摧毁、锁定、解锁、设置属性等操作

  • 条件变量(Condition Variable):创建、摧毁、等待、通知、设置与查询属性等操作

  • 使用了互斥锁的线程间的同步管理

POSIX的SemaphoreAPI可以和Pthreads协同工作,但这并不是Pthreads的标准。因而这部分API是以"sem_"打头,而非"pthread_"。

1.2 线程查看命令

方法一:PS

在ps命令中,“-T”选项可以开启线程查看。

下面的命令列出了由进程号为的进程创建的所有线程。

ps -T -p 

方法二: Top

top命令可以实时显示各个线程情况。

要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。

在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。

top -H

1.3 线程与进程的区别

进程是并发执行的程序在执行过程中分配和管理资源的基本单位。线程是进程的一个执行单元,是比进程还要小的独立运行的基本单位。一个程序至少有一个进程,一个进程至少有一个线程。

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第1张图片

(1)用途不同

进程是资源分配(内存空间资源)最小单位,线程是程序执行(CPU资源)的最小单位。 

计算机在执行程序时,会为程序创建相应的进程,进行内存资源分配时,是以进程为单位进行相应的分配。

每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第2张图片

(2)地址空间

进程有自己独立的地址空间,每启动一个进程,系统都会为其分配地址空间,建立数据表来维护代码段、堆栈段和数据段;

线程没有独立的地址空间,同一进程的所有线程共享本进程的地址空间。

(3)资源拥有

进程之间的内存资源是独立的;

同一进程内的线程共享本进程的内存资源。

(4)执行过程:

每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。

线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第3张图片

线程是处理机调度的基本单位,进程是资源分配的基本单位。

由于程序执行的过程其实是执行具体的线程,那么处理机处理的也是程序相应的线程,所以处理机调度的基本单位是线程

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第4张图片

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第5张图片

1.4 并发性(concurrency)和并行性(parallel)是两个概念

[架构之路-40]:目标系统 - 系统软件 - Linux OS的线程库pthread简介_第6张图片

并行(同时执行):并行是指在同一时刻,有多条指令在多个处理器上同时执行;在多核CPU中,可以实现并行性。

并发(同时发生):在单CPU的情形下,指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果。

第2章 核心数据结构

2.1 线程管理

  • pthread_t:线程句柄.出于移植目的,不能把它作为整数处理,应使用函数pthread_equal()对两个线程ID进行比较。获取自身所在线程id使用函数pthread_self()。

  • pthread_attr_t:线程属性。主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。主要属性的意义如下:

    • __detachstate,表示新线程是否与进程中其他线程脱离同步。如果设置为PTHREAD_CREATE_DETACHED,则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。可以在线程创建并运行以后用pthread_detach()来设置。一旦设置为PTHREAD_CREATE_DETACHED状态,不论是创建时设置还是运行时设置,则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

    • __schedpolicy,表示新线程的调度策略,包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。

    • __schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。系统支持的最大和最小的优先级值可以用函数sched_get_priority_max和sched_get_priority_min得到。

    • __inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。

    • __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

2.2 线程同步

  • pthread_barrier_t:同步屏障数据类型

  • pthread_mutex_t:mutex数据类型

  • pthread_cond_t:条件变量数据类型

第3章 接口函数

3.1 线程控制函数(简介起见,省略参数):

  • pthread_create():创建一个线程

  • pthread_exit():终止当前线程

  • pthread_cancel():请求中断另外一个线程的运行。被请求中断的线程会继续运行,直至到达某个取消点(Cancellation Point)。取消点是线程检查是否被取消并按照请求进行动作的一个位置。POSIX 的取消类型(Cancellation Type)有两种,一种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;另外一种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS),使用异步取消时,线程可以在任意时间取消。系统调用的取消点实际上是函数中取消类型被修改为异步取消至修改回延迟取消的时间段。几乎可以使线程挂起的库函数都会响应CANCEL信号,终止线程,包括sleep、delay等延时函数。

  • pthread_join():阻塞当前的线程,直到另外一个线程运行结束

  • pthread_kill():向指定ID的线程发送一个信号,如果线程不处理该信号,则按照信号默认的行为作用于整个进程。信号值0为保留信号,作用是根据函数的返回值判断线程是不是还活着。

  • pthread_cleanup_push():线程可以安排异常退出时需要调用的函数,这样的函数称为线程清理程序,线程可以建立多个清理程序。线程清理程序的入口地址使用栈保存,实行先进后处理原则。由pthread_cancel或pthread_exit引起的线程结束,会次序执行由pthread_cleanup_push压入的函数。线程函数执行return语句返回不会引起线程清理程序被执行。

  • pthread_cleanup_pop():以非0参数调用时,引起当前被弹出的线程清理程序执行。

  • pthread_setcancelstate():允许或禁止取消另外一个线程的运行。

  • pthread_setcanceltype():设置线程的取消类型为延迟取消或异步取消。

3.2 线程属性函数:

  • pthread_attr_init():初始化线程属性变量。运行后,pthread_attr_t结构所包含的内容是操作系统支持的线程的所有属性的默认值。

  • pthread_attr_setdetachstate():设置线程属性变量的detachstate属性(决定线程在终止时是否可以被joinable)

  • pthread_attr_getdetachstate():获取脱离状态的属性

  • pthread_attr_setscope():设置线程属性变量的__scope属性

  • pthread_attr_setschedparam():设置线程属性变量的schedparam属性,即调用的优先级。

  • pthread_attr_getschedparam():获取线程属性变量的schedparam属性,即调用的优先级。

  • pthread_attr_destroy():删除线程的属性,用无效值覆盖

3.3 互斥锁mutex函数:

  • pthread_mutex_init()初始化互斥锁

  • pthread_mutex_destroy()删除互斥锁

  • pthread_mutex_lock():占有互斥锁(阻塞操作)

  • pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。

  • pthread_mutex_unlock(): 释放互斥锁

  • pthread_mutexattr_(): 互斥锁属性相关的函数

3.4 条件变量函数:

  • pthread_cond_init():初始化条件变量

  • pthread_cond_destroy():销毁条件变量

  • pthread_cond_signal(): 发送一个信号给正在当前条件变量的线程队列中处于阻塞等待状态的线程,使其脱离阻塞状态,唤醒后继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。一般只给一个阻塞状态的线程发信号。假如有多个线程正在阻塞等待当前条件变量,则根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但pthread_cond_signal在多处理器上可能同时唤醒多个线程,当只能让一个被唤醒的线程处理某个任务时,其它被唤醒的线程就需要继续wait。POSIX规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,有些实现为了简便,在单处理器上也会唤醒多个线程。所以最好对pthread_cond_wait()使用while循环对条件变量是否满足做条件判断。

  • pthread_cond_wait(): 等待条件变量的特殊条件发生;pthread_cond_wait() 必须与一个pthread_mutex配套使用。该函数调用实际上依次做了3件事:对当前pthread_mutex解锁、把当前线程挂起到当前条件变量的线程队列、被其它线程的信号唤醒后对当前pthread_mutex申请加锁。如果线程收到一个信号被唤醒,将被配套的互斥锁重新锁住,pthread_cond_wait() 函数将不返回直到线程获得配套的互斥锁。需要注意的是,一个条件变量不应该与多个互斥锁配套使用。

  • pthread_cond_broadcast(): 某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.

  • pthread_condattr_(): 条件变量属性相关的函数

3.5 线程私有存储(Thread-local storage):

  • pthread_key_create(): 分配用于标识进程中线程特定数据的pthread_key_t类型的键

  • pthread_key_delete(): 销毁现有线程特定数据键

  • pthread_setspecific(): 为指定线程的特定数据键设置绑定的值

  • pthread_getspecific(): 获取调用线程的键绑定值,并将该绑定存储在 value 指向的位置中

3.6 同步屏障函数

  • pthread_barrier_init(): 同步屏障初始化

  • pthread_barrier_wait():

  • pthread_barrier_destory():

3.7 其它多线程同步函数:

  • pthread_rwlock_*(): 读写锁

3.8 工具函数:

  • pthread_equal(): 对两个线程的线程标识号进行比较

  • pthread_detach(): 分离线程

  • pthread_self(): 查询线程自身线程标识号

  • pthread_once(): 某些需要仅执行一次的函数。其中第一个参数为pthread_once_t类型,是内部实现的互斥锁,保证在程序全局仅执行一次。

3.9 信号量函数,包含在semaphore.h中:

  • sem_open:创建或者打开已有的命名信号量。可分为二值信号量与计数信号量。命名信号量可以在进程间共享使用。

  • sem_close:关闭一个信号灯,但没有将它从系统中删除。命名信号灯是随内核持续的,即使当前没有进程打开着某个信号灯,它的值仍然保持。

  • sem_unlink:从系统中删除信号灯。

  • sem_getvalue:返回所指定信号灯的当前值。如果该信号灯当前已上锁,那么返回值或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。

  • sem_wait:申请共享资源,所指定信号灯的值如果大于0,那就将它减1并立即返回,就可以使用申请来的共享资源了。如果该值等于0,调用线程就被进入睡眠状态,直到该值变为大于0,这时再将它减1,函数随后返回。sem_wait操作必须是原子操作。

  • sem_trywait:申请共享资源,当所指定信号灯的值已经是0时,后者并不将调用线程投入睡眠。相反,它返回一个EAGAIN错误。

  • sem_post:释放共享资源。与sem_wait恰相反。

  • sem_init:初始化非命名(内存)信号量

  • sem_destroy:摧毁非命名信号量

3.10 共享内存函数,包含在sys/mman.h中,链接时使用rt库:

  • mmap:把一个文件或一个POSIX共享内存区对象映射到调用进程的地址空间。使用该函数的目的: 1.使用普通文件以提供内存映射I/O 2.使用特殊文件以提供匿名内存映射。 3.使用shm_open以提供无亲缘关系进程间的Posix共享内存区。

  • munmap: 删除一个映射关系

  • msync:文件与内存同步函数

  • shm_open:创建或打开共享内存区

  • shm_unlink:删除一个共享内存区对象的名字,删除一个名字仅仅防止后续的open,msq_open或sem_open调用取得成功。

  • ftruncate:调整文件或共享内存区大小

  • fstat来获取有关该对象的信息 [1]

4章 带pthread库的编译

g++ test.c -o test -lpthread


作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:[Linux用户空间编程-6]:pThread线程库简介_文火冰糖的硅基工坊的博客-CSDN博客_linux pthread库

你可能感兴趣的:(架构之路,pthread,线程,Linux,OS,架构)