Linux 线程和信号 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
DavidGao
V2EX    Linux

Linux 线程和信号

  •  
  •   DavidGao 2017-07-28 17:01:44 +08:00 2489 次点击
    这是一个创建于 3001 天前的主题,其中的信息可能已经有所发展或是发生改变。

    什么是线程

    线程,有时被称为轻量级进程(Lightweight Process,LWP ),是程序执行流的最小单元。一个标准的线程由线程 ID,当前指令指针(PC ),寄存器集合和堆栈组成,每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

    同时线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

    一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。因此线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。

    什么是信号

    信号是一种 IPC 通信的形式,一般在 Unix,类 Unix 或 POSIX 兼容的系统中使用。信号是一种异步通知进程或同进程中某个指定线程的方式。 当信号被发送到进程的时候,操作系统会中断进程的控制流程,并且在执行非原子性的 CPU 指令时可以中断进程。

    信号使用的风险(新手坑)

    信号处理在存在竞态的,因为信号本身是异步的,在处理一个信号的过程中,令一个信号(甚至肯能是同类型的信号)会被直接发送到进程中请求进程处理。 信号是可以打断系统调用的,不谨慎处理会引起程序自身的混乱,所以进程的信号处理过程,尽量做到没有副作用,也不要使用不可重入的函数。

    Linux 的线程

    LinuxThreads

    在 Linux 的上古时代,Linux 的线程技术和 POSIX 的标准是不同的,它使用自己的 LinuxThreads 库。这会为我们带来什么影响呢?

    让我们来回顾一下 LinuxThreads 设计细节的一些基本理念:

    1. 系统必须能够响应终止信号并杀死整个进程。
    2. 以堆栈形式使用的内存回收必须在线程完成之后进行。因此,线程无法自行完成这个过程。
    3. 终止线程必须进行等待,这样它们才不会进入僵尸状态。
    4. 线程本地数据的回收需要对所有线程进行遍历;这必须由管理线程来进行。
    5. 如果主线程需要调用 pthread_exit(),那么这个线程就无法结束。主线程要进入睡眠状态,而管理线程的工作就是在所有线程都被杀死之后来唤醒这个主线程。

    为了维护线程本地数据和内存,LinuxThreads 使用了进程地址空间的高位内存(就在堆栈地址之下)。 同步元语是使用信号来实现的。例如,线程会一直阻塞,直到被信号唤醒为止。并且,LinuxThreads 将每个线程都是作为一个具有惟一进程 ID 的进程实现的。LinuxThreads 接收到终止信号之后,管理线程就会使用相同的信号杀死所有其他线程(进程)。 由于异步信号是内核以进程为单位分发的,而 LinuxThreads 的每个线程对内核来说都是一个进程,且没有实现"线程组",因此,某些语义不符合 POSIX 标准,比如没有实现向进程中所有线程发送信号。如果核心不提供实时信号,LinuxThreads 将使用 SIGUSR1 和 SIGUSR2 作为内部使用的 restart 和 cancel 信号,这样应用程序就不能使用这两个原本为用户保留的信号了。在 Linux kernel 2.1.60 以后的版本都支持扩展的实时信号(从_SIGRTMIN 到_SIGRTMAX ),因此不存在这个问题。根据 LinuxThreads 的设计,如果一个异步信号被发送了,那么管理线程就会将这个信号发送给一个线程,如果这个线程现在阻塞了这个信号,那么这个信号也就会被挂起,因此某些信号的缺省动作难以在现行体系上实现,比如 SIGSTOP 和 SIGCONT,LinuxThreads 只能将一个线程挂起,而无法挂起整个进程。

    LinuxThreads 带来了什么问题

    首先我们说下 POSIX 是如何定义多线程的:POSIX 下一个多线程的进程只有一个 PID。 根据上面我们对 LinuxThreads 的描述,我们可以总结出 LinuxThreads 有下面这些问题:

    1. 它使用管理线程来创建线程,并对每个进程所拥有的所有线程进行协调。这增加了创建和销毁线程所需要的开销。
    2. 由于它是围绕一个管理线程来设计的,因此会导致很多的上下文切换的开销,这可能会妨碍系统的可伸缩性和性能。
    3. 由于管理线程只能在一个 CPU 上运行,因此所执行的同步操作在 SMP 或 NUMA 系统上可能会产生可伸缩性的问题。
    4. 由于线程的管理方式,以及每个线程都使用了一个不同的进程 ID,因此 LinuxThreads 与其他与 POSIX 相关的线程库并不兼容。
    5. 信号用来实现同步原语,这会影响操作的响应时间。另外,将信号发送到主进程的概念也并不存在。因此,这并不遵守 POSIX 中处理信号的方法。

    我们在这里不关注性能如何只关注 POSIX 兼容和信号处理问题。

    NPTL

    LinuxThreads 的问题,特别是兼容性上的问题,严重阻碍了 Linux 上的跨平台应用(如 Apache )采用多线程设计,从而使得 Linux 上的线程应用一直保持在比较低的水平。在 Linux 社区中,已经有很多人在为改进线程性能而努力,其中既包括用户级线程库,也包括核心级和用户级配合改进的线程库。目前最为人看好的有两个项目,一个是 RedHat 公司牵头研发的 NPTL ( Native Posix Thread Library ),另一个则是 IBM 投资开发的 NGPT ( Next Generation Posix Threading ),二者都是围绕完全兼容 POSIX 1003.1c ,同时在核内和核外做工作以而实现多对多线程模型。这两种模型都在一定程度上弥补了 LinuxThreads 的缺点,且都是重起炉灶全新设计的。 NPTL 的设计目标归纳可归纳为以下几点:

    1. POSIX 兼容性
    2. SMP 结构的利用
    3. 低启动开销
    4. 低链接开销(即不使用线程的程序不应当受线程库的影响)
    5. 与 LinuxThreads 应用的二进制兼容性
    6. 软硬件的可扩展能力
    7. 多体系结构支持
    8. NUMA 支持

    在技术实现上,NPTL 仍然采用 1:1 的线程模型,并配合 glibc 和最新的 Linux Kernel2.5.x 开发版在信号处理、线程同步、存储管理等多方面进行了优化。和 LinuxThreads 不同,NPTL 没有使用管理线程,核心线程的管理直接放在核内进行,这也带了性能的优化。

    Linux 线程总结

    比较新的 Linux 都已经开始使用 NPTL 了,所以我们可以忽略 LinuxThreads 的存在了,介绍它主要是为了让诸位读者更深入的了解线程和信号的恩恩怨怨(不要丢鸡蛋)。

    Linux 的信号

    Linux 是如何处理信号的

    随着 Linux 的内核版本不断提升,Linux 的信号现在已经可以按照线程级别的触发了,换句话说就是,每个线程可以关注自己的信号了,并且可以区别性对待了。那我们需要注意什么呢?

    在多线程应用中,我们应当使用 sigaction 来代替 singal 函数,因为按 POSIX 的说法 singal 函数并没有明确定义自己在多线程应用中的行为。

    可以使用 pthread_sigmask 来为每个线程设置独立的信号掩码。同时在多线程应用中应当避免使用 sigprocmask 这个函数,原因也是 POSIX 中该函数并没有明确定义自己在多线程应用中的行为。

    这个时候,有人会产生疑问了,那么多线程下 kill 发出的进程级别的信号 A 怎么办? Linux 是这样解决的,它会把这个信号交付给任意一个没有屏蔽信号 A 的线程。如果这信号没有被任何线程设置 handler 进行处理,就会触发 POSIX 规定的默认动作。

    接着有人就会问,我怎么向某个线程发消息呢,POSIX 为我们准备了 pthread_kill 函数,我们可以直接向特定的线程发送消息。那么如果一个线程收到信号 A,但是自己没有安装 handler 会发生什么?其实和进程级别的信号处理方法一样,直接触发默认动作,同样会结束整个进程。

    如何避免新手坑

    在具有事件循环的应用中,在信号的的 handler 中,可以将信号直接放入程序的队列中,立刻返回。这样直到线程从程序的队列中取出这个信号为止,整个线程看起来就像没有“中断”。 如果不知道该怎么做,去看看著名的 libev 吧。

    总结

    多读读 POSIX 标准和 Intel 的 CPU 体系结构,会让自己在开发变的容易些。

    转自TTalkIM

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1225 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 23:37 PVG 07:37 LAX 16:37 JFK 19:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86