⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 multithread.txt

📁 Linux下的多线程编程
💻 TXT
📖 第 1 页 / 共 5 页
字号:
和闹钟在到时间时向线程发送信号。
    每个LWP有一个虚拟时间或一个配置定时器,绑定在该LWP上的线程可以使用它
们。如果虚拟定时器到时间,它向拥有定时器的LWP发送信号SIGVTALRM或SIGPROF,
发送哪一个视情况而定。
    你可以用profil(2)给每一个LWP进行预配置,给每个LWP私有的缓冲区或者一个
LWP共享的缓冲区。配置数据按LWP用户时间的每一个时钟单位更新。在创建LWP时配
置状态被继承。

4.3非本地跳转--setjmp(3C)和longjmp(3C)

    setjmp()和longjmp()的使用范围限制在一个线程里,在大多数情况下是合适的。
然而,只有setjmp()和longjmp()在同一个线程里,线程才能对一个信号执行longjmp()。

4.4资源限制

    资源限制在整个进程内,每个线程都可以给进程增加资源。如果一个线程超过
了软资源限制,它将发出相应的信号。进程内可用的资源总量可以由getrusage(3B)
获得。

4.5 LWP和调度类型

    Solaris 内核有3种进程调度类型。最高优先级的是实时(realtime RT)。其次
是系统(system)。系统调度类型不能在用户进程中使用。最低优先级的是分时
(timeshare TS),它也是缺省类型。
    调度类型在LWP内维护。如果一个进程被创建,初始LWP继承父进程的调度类型和
优先级。如果有跟多的LWP被创建来运行非绑定线程,它们也继承这些调度类型和优先
级。进程中的所有非绑定线程有相同的调度类型和优先级。
    每个调度类型按照调度类型的配置优先级,把LWP的优先级映射到一个全体的分配
优先级。???
    绑定线程拥有和它们绑定的LWP相同的调度类型和优先级。进程中的每个绑定线程
有一个内核可以看到的调度类型和优先级。系统按照LWP来调度绑定线程。
    调度类型用priocntl(2)来设置。前两个参数的指定决定了是只有调用的LWP还是
一个或多个进程所有的LWP都被影响。第三个参数是一个指令,它可以是以下值之一。
        · PC_GETCID--获得指定类型的类型号和类型属性
        · PC_GETCLINFO--获得指定类型的名称和类型属性
        · PC_GETPARMS--获得类型标识和进程中,LWP,或者一组进程的因类型而异
                的调度参数
        · PC_SETPARMS--设置类型标识和进程中,LWP,或者一组进程的因类型而异
                的调度参数
    用priocntl()仅限于绑定线程。为了设置非绑定线程的优先级,使用thr_setprio(3T)。

4.5.1分时调度

    分时调度将执行资源公平地分配给各进程。内核的其他部分可以在短时间内独占
处理器,而不会使用户感到响应时间延长。
    Priocntl(2)调用设置一个或多个线程的nice(2)级别。Priocntl()影响进程中所有
的分时类型的LWP的nice级别。普通拥护的nice()级别从0到20,而超级用户的进程从
-20到20。值越小,级别越高。
    分时LWP的分配优先级根据它的LWP的CPU使用率和它的nice()级别来确定。Nice()
级别指定了在进程内供分时调度器参考的相对优先级。LWP的nice()值越大,所得的执
行资源越少,但不会为0。一个执行的多的LWP会被赋予比执行的少的LWP更小的优先级。

4.5.2实时调度

    实时类型可以被整个进程或进程内部的一个或多个线程来使用。这需要超级用户
权限。与分时类型的nice(2)级别不同,标识为实时的LWP可以被独立或联合地分配优先
级。一个priocntl(2)调用影响进程中所有实时的LWP的属性。
    调度器总是分配最高优先级的实时LWP。如果一个高优先级的LWP可运行,它将打断
低优先级的LWP。一个有先行权(preempt)的LWP被放置在该等级队列的头上。一个实
时(RT)的LWP保持控制处理器,直到被其他线程中断时挂起,或者实时优先级改变。
RT类型的LWP对TS类型的进程有绝对的优先权。
    一个新的LWP继承父线程或LWP的调度类型。一个RT类型的LWP继承其父亲的时间片,
不管它是有限还是无限的。一个有有限时间片的LWP持续运行直到结束,阻塞(例如等
待一个I/O事件),被更高优先级的实时进程中断,或者时间片用完。一个拥有无限时
间片的进程则不存在第四种情况(即时间片用完)。

4.5.3 LWP调度和线程绑定
· 线程库自动调节缓冲池中LWP的数量来运行非绑定线程。其目标是:
    避免程序因为缺少没有阻塞的LWP而阻塞。
    例如,如果可运行的非绑定线程比LWP多而所有的活动线程在内核中处于无限等待
的阻塞状态,进程不能继续,知道一个等待的线程返回。
· 有效利用LWP
    例如,如果线程库给每个线程创建一个LWP,许多LWP通常处于闲置状态,而操作
系统反而被没用的LWP耗尽资源。
    要记住,LWP是按时间片运行的,而不是线程。这意味着如果只有一个LWP,则进程
内部没有时间片--现成运行直到阻塞(通过线程间同步),被中断,或者运行结束。
    可以用thr_setprio(3T)来为线程分配优先级:只有在没有高优先级的非绑定线程
可用时,LWP才会被分配给低优先级的线程。当然,绑定线程不会参与这种竞争,因为
它们有自己的LWP。
    把线程绑定到LWP上可以精确地控制调度。???但这种控制在很多非绑定线程竞
争一个LWP是不可能的。
    实时线程可以对外部事件有更快的反应。考虑一个用于鼠标控制的线程,它必须对
鼠标事件及时作出反应。通过绑定一个线程到LWP上,保证了在需要时会有LWP可用。通
过将LWP设定为实时调度类型,可以保证LWP对LWP事件作出快速响应。

4.5.4信号等待(SIGWAITING)--给等待线程创建LWP
    线程库通常保证在缓冲池内有足够的LWP保证程序运行。如果进程中所有的LWP处
于无限等待的阻塞状态(例如在读中断或网络时阻塞),操作系统给进程发送一个新的
信号,SIGWAITING。这个信号由线程库来控制。如果进程中有一个等待运行的线程,一
个新的LWP被创建并被赋予适当的线程使之执行。
    SIGWAITING机制在一个或多个线程处于计算绑定并且有新线程可以执行的情况下。
一个计算绑定线程可以阻止在缺少LWP的情况下有多个可运行的线程启动运行。这可以
通过调用thr_setconcurrency(3T)或者在调用thr_create(3T)时使用THR_NEW_LWP标志。

4.5.5确定LWP的已空闲时间

    如果活动线程的数量减少,LWP池中的一些LWP将不再被需要。如果LWP的数量比活动
的线程多,线程库破坏那些没有用的LWP。线程库确定LWP的空闲的时间--如果线程在
足够长的时间内没有被使用(现在的设置是5分钟),它们将被删除。

4.6扩展传统的信号

    为了适应多线程,UNIX的信号模型以一种相当自然的方式被扩展。信号的分布是用
传统机制建立在进程内部的(signal(2),sigaction(2), 等等)。
    如果一个信号控制器被标志为SIG_DFL或者SIG_IGN,在收到信号后所采取的行动
(exit, core dump, stop, continue, or ignore)在整个接收进程中有效,将影响到
进程中的所有线程。关于信号的基本信息请参见signal(5)。
    每个线程有它自己的信号掩模。如果线程使用的内存或其他状态也在被信号控制
器使用,线程会关于一些信号阻塞。进程中所有的线程共享由sigaction(2)和其变量
建立的信号控制器,???象通常那样。
    进程中的一个线程不能给另一个进程中的线程发送信号。一个由
kill(2)和sigsend(2)送出的信号是在进程内部有效的,将被进程中的任何一个接收态
的线程接收并处理。
    非绑定线程不能使用交互的信号栈。一个绑定线程可以使用交互信号栈,因为其
状态是和执行资源连接的。一个交互信号栈必须通过sigaction(2) ,以及
sigaltstack(2)来声明并使能。
    一个应用程序给每个进程一个信号控制器,在它的基础上,每个线程都有线程信
号控制器。一种办法是给在一张表中给每个线程控制器建立一个索引,由进程信号控制
器来通过这张表实现线程控制器。这里没有零线程。
    信号被分为两类:陷阱(traps)和意外(exceptions,同步信号)和中断
(interrupts,异步信号)。
    在传统的UNIX中,如果一个信号处于挂起状态(即等待接收),发生的其他同样的
信号将没有效果--挂起信号由一位来表示,而不是一个计数器。
    就象在单线程的进程里那样,如果一个线程在关于系统调用阻塞时收到一个信号,
线程将提前返回,或者带有一个EINTR错误代码,或者带有比请求少的字节数(如果阻
塞在I/O状态)。
    对于多线程编程有特殊意义的是作用在cond_wait(3T)上的信号的效果。这个调用
通常在其他线程调用cond_signal(3T)和cond_broadcast(3T),但是,如果等待线程收
到一个UNIX信号,将返回一个EINTR错误代码。更多的信息参见"对于条件变量的等待中断"。

4.6.1同步信号

    陷阱(例如SIGILL, SIGFPE, SIGSEGV)发生在线程自身的操作之后,例如除零
错误或者显式地发信号给自身。一个陷阱仅仅被导致它的线程类控制。进程中的几个
线程可以同时产生和控制同类陷阱。
    扩展信号到独立线程的主张对于同步信号来说是容易的--信号被导致问题的线程
来处理。然而,如果一个线程没有处理这个问题,例如通过sigaction(2)建立一个信号
控制器,整个进程将终止。
    因为一个同步信号通常意味着整个进程的严重错误,而不只是一个线程,终止进程
通常是一个明智的做法。

4.6.2异步信号

    中断(例如SIGINT和SIGIO)是与任何线程异步的,它来自于进程外部的一些操作。
它们也许是显式地送到其他线程的信号,或者是例如Control-c的外部操作,处理异步
信号不处理同步信号要复杂的多。
    一个中断被任何线程来处理,如果线程的信号掩模允许的话。如果有多个线程可以
接收中断,只有一个被选中。
    如果并发的多个同样的信号被送到一个进程,每一个将被不同的线程处理,如果
线程的信号掩模允许的话。如果所有的线程都屏蔽该信号,则这些信号挂起,直到有信
号解除屏蔽来处理它们。

4.6.3连续语义(Continuation Semantics)

    连续语义(Continuation Semantics)是处理信号的传统方法。其思想是当一个
信号控制器返回,控制恢复到中断前的状态。这非常适用于单线程进程的异步信号,如
同在示例4-1中的那样。在某些程序设计语言里(例如PL/1),这也被用于意外
(exception)处理机制。

        Code Example 4-1 连续语义
        Unsigned int nestcocunt;
        Unsigned int A(int i, int j) {
                Nestcount++;
                If(i==0)
                        Return (j+1);
                Else if (j==0)
                        Return (A(I-1,1));
                Else 
                        Return (A(I-1,A(I, j-1)));
        }
        void sig(int i){
                printf("nestcount=%d\n",nestcount);
        }
        main(){
                sigset(SIGINT, sig);
                A(4,4);
        }

4.6.4对于信号的新操作

    对于多线程编程的几个新的信号操作被加入操作系统。
        Thr_sigsetmask(3T)
    Thr_sigsetmask(3T)针对线程而sigprocmask(2)针对进程--它设置(线程)的
信号掩模。如果一个新线程被创建,它的初始信号掩模从父线程那里继承。
    在多线程编程中避免使用sigprocmask(),因为它设置LWP的信号掩模,被这个
操作影响的线程可以在一段时间后改变。???
    不象sigprocmask(),thr_sigsetmask()是一种代价相对低廉的调用,因为它不
产生系统调用。
        Thr_kill(3T)
        Thr_kill是kill(2)的线程版本--它发送信号给特定的线程。
    当然,这与发送信号给进程不同。如果一个信号被发送给进程,信号可以被进
程中的任何线程所控制。一个由thr_kill()发出的信号只能被指定的线程处理。
    注意,你只能用thr_kill()给当前进程里的线程发信号。这是因为线程标识符
是本地的--不可能给其他进程内的线程命名。
        Sigwait(2)
    Sigwait(2)导致调用线程阻塞直到收到set参数指定的所有信号。线程在等待时,
被set标识的信号应当被解除屏蔽,但最初的信号掩模在调用返回时将恢复。
    用sigwait()来从异步信号当中把线程分开。你可以创建一个线程来监听异步信
号,而其它线程被创建来关于指定的异步信号阻塞。
    如果信号被发送,sigwait()清除挂起的信号,返回一个数。许多线程可以同时
调用sigwait(),但每个信号被收到后只有相关的一个线程返回。
    通过sigwait()你可以同时处理异步信号--一个线程通过简单的sigwait()调用
来处理信号,在信号一旦被受到就返回。如果保证所有的线程(包括调用sigwait()
的线程)屏蔽这样的信号,你可以保证这样的信号被你指定的线程安全地处理。
    通常,用sigwait()创建一个或多个线程来等待信号。因为sigwait()可以接收
被屏蔽的信号,应当保证其它线程对这样的信号不感兴趣,以免信号被偶然地发送给
这样的线程。如果信号到达,一个线程从sigwait()返回,处理该信号,等待其它的
信号。处理信号的线程不限于使用异步安全函数,可以和其它线程以通常的方式同
步(异步安全函数类型被定义为"安全等级的MT界面MT Interface Safety Levels)。
---------------------------------------
注意-sigwait()不能用于同步信号
---------------------------------------
        sigtimedwait(2)
        sigtimedwait(2)类似于

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -