免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2299 | 回复: 1
打印 上一主题 下一主题

Solaris 线程编程 3................................. [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-22 16:21 |只看该作者 |倒序浏览
Solaris 线程编程 3...........................













进程内条件变量
#include <thread.h>cond_t cv;int ret;/* to be used within this process only */ret = cond_init(cv, USYNC_THREAD, 0);
进程间条件变量
#include <thread.h>cond_t cv;int ret;/* to be used among all processes */ret = cond_init(&cv, USYNC_PROCESS, 0);
cond_init 返回值
cond_init() 在成功运行后返回 0。如果检测到以下任一情况,cond_init() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

EINVAL

描述:
type 不是可以识别的类型。

销毁条件变量
使用 cond_destroy(3C) 可以销毁与 cv 所指向的条件变量相关联的状态。用来存储该条件变量的空间不会释放。对于 POSIX 线程,请参见pthread_condattr_destroy 语法。

cond_destroy 语法
#include <thread.h>int cond_destroy(cond_t *cv);
cond_destroy 返回值
cond_destroy() 在成功运行后返回 0。如果检测到以下任一情况,cond_destroy() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

EBUSY

描述:
系统检测到销毁活动条件变量的尝试。

等待条件
使用 cond_wait(3C) 可以原子方式释放 mp 所指向的互斥锁,并导致调用线程基于 cv 所指向的条件变量阻塞。阻塞的线程可以由 cond_signal() 或 cond_broadcast() 唤醒,也可以在信号传送或 fork() 将其中断时唤醒。

cond_wait() 每次返回时,互斥锁均处于锁定状态并由调用线程拥有,即使返回错误时也是如此。

cond_wait 语法
#include <thread.h>int cond_wait(cond_t *cv, mutex_t *mp);
cond_wait 返回值
cond_wait() 在成功运行后返回 0。如果检测到以下任一情况,cond_wait() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

EBUSY

描述:
等待过程已被信号或 fork() 中断。

等待绝对时间
cond_timedwait(3C) 与 cond_wait() 非常相似,区别在于 cond_timedwait() 经过 abstime 指定的时间之后不会阻塞。对于 POSIX 线程,请参见pthread_cond_timedwait 语法。

cond_timedwait 语法
#include <thread.h>int cond_timedwait(cond_t *cv, mutex_t *mp, timestruct_t abstime);
cond_timedwait() 每次返回时,互斥锁均会锁定并由调用线程拥有,即使返回错误时也是如此。

cond_timedwait() 函数会一直阻塞,直到该条件获得信号,或者经过最后一个参数所指定的时间为止。超时以具体的时间指定,这样即可在不重新计算超时值的情况下高效地重新测试条件。

cond_timedwait 返回值
cond_timedwait() 在成功运行后返回 0。如果检测到以下任一情况,cond_timedwait() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

ETIME

描述:
由 abstime 指定的时间已过期。

EINVAL

描述:
abstime 无效。

等待时间间隔
cond_reltimedwait(3C) 与 cond_timedwait() 非常相似,区别在于第三个参数的值不同。cond_reltimedwait() 的第三个参数采用相对时间间隔值,而不是绝对时间值。对于 POSIX 线程,请参见 pthread_cond_reltimedwait_np(3C) 手册页。

cond_reltimedwait() 每次返回时,互斥锁均会锁定并由调用线程拥有,即使返回错误时也是如此。cond_reltimedwait() 函数一直阻塞,直到该条件获得信号,或者经过最后一个参数所指定的时间间隔为止。

cond_reltimedwait 语法
#include <thread.h>int cond_reltimedwait(cond_t *cv, mutex_t *mp,    timestruct_t reltime);
cond_reltimedwait 返回值
cond_reltimedwait() 在成功运行后返回 0。如果检测到以下任一情况,cond_reltimedwait() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

ETIME

描述:
由 reltime 指定的时间已过期。

解除阻塞一个线程
对于基于 cv 所指向的条件变量阻塞的线程,使用 cond_signal(3C) 可以解除阻塞该线程。如果没有线程基于该条件变量阻塞,则调用 cond_signal() 不起任何作用。

cond_signal 语法
#include <thread.h>int cond_signal(cond_t *cv);
cond_signal 返回值
cond_signal() 在成功运行后返回 0。如果检测到以下情况,cond_signal() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

解除阻塞所有线程
对于基于 cv 所指向的条件变量阻塞的全部线程,使用 cond_broadcast(3C) 可以解除阻塞这些线程。如果没有线程基于该条件变量阻塞,则调用 cond_broadcast() 不起任何作用。

cond_broadcast 语法
#include <thread.h>int cond_broadcast(cond_t *cv);
cond_broadcast 返回值
cond_broadcast() 在成功运行后返回 0。如果检测到以下情况,cond_broadcast() 将失败并返回对应的值。

EFAULT

描述:
cv 指向的地址非法。

相似的同步函数:信号
信号操作在 Solaris 操作环境和 POSIX 环境中均相同。 Solaris 操作环境中的函数名 sema_ 在 pthread 中会更改为 sem_。本节讨论了以下主题:

初始化信号

增加信号

基于信号计数阻塞

减小信号计数

销毁信号状态

初始化信号
使用 sema_init(3C) 可以通过 count 值来初始化 sp 所指向的信号变量。

sema_init 语法
#include <thread.h>int sema_init(sema_t *sp, unsigned int count, int type,    void *arg);
type 可以是以下值之一:

USYNC_PROCESS。信号可用来同步此进程和其他进程中的线程。信号只能由一个进程来初始化。arg 会被忽略。

USYNC_THREAD。信号可用来仅同步此进程中的线程。arg 会被忽略。

多个线程决不能同时初始化同一个信号。不得对其他线程正在使用的信号重新初始化。

进程内信号
#include <thread.h>sema_t sp;int ret;int count;count = 4;/* to be used within this process only */ret = sema_init(&sp, count, USYNC_THREAD, 0);
进程间信号
#include <thread.h>sema_t sp;int ret;int count;count = 4;/* to be used among all the processes */ret = sema_init (&sp, count, USYNC_PROCESS, 0);
sema_init 返回值
sema_init() 在成功运行后返回 0。如果检测到以下任一情况,sema_init() 将失败并返回对应的值。

EINVAL

描述:
sp 引用的信号无效。

EFAULT

描述:
sp 或 arg 指向的地址非法。

增加信号
使用 sema_post(3C) 可以原子方式增加 sp 所指向的信号。如果多个线程基于该信号阻塞,则系统会解除阻塞其中一个线程。

sema_post 语法
#include <thread.h>int sema_post(sema_t *sp);
sema_post 返回值
sema_post() 在成功运行后返回 0。如果检测到以下任一情况,sema_post() 将失败并返回对应的值。

EINVAL

描述:
sp 引用的信号无效。

EFAULT

描述:
sp 指向的地址非法。

EOVERFLOW

描述:
sp 指向的信号值超过了 SEM_VALUE_MAX。

基于信号计数阻塞
使用 sema_wait(3C) 可以一直阻塞调用线程,直到 sp 所指向的信号的计数变得大于零为止。计数变得大于零时,系统会以原子方式减小计数。

sema_wait 语法
#include <thread.h>int sema_wait(sema_t *sp);
sema_wait 返回值
sema_wait() 在成功运行后返回 0。如果检测到以下任一情况,sema_wait() 将失败并返回对应的值。

EINVAL

描述:
sp 引用的信号无效。

EINTR

描述:
等待过程已被信号或 fork() 中断。

减小信号计数
使用 sema_trywait(3C) 可以在计数大于零时,以原子方式减小 sp 所指向的信号的计数。此函数是 sema_wait() 的非阻塞版本。

sema_trywait 语法
#include <thread.h>int sema_trywait(sema_t *sp);
sema_trywait 返回值
sema_trywait() 在成功运行后返回 0。如果检测到以下任一情况,sema_trywait() 将失败并返回对应的值。

EINVAL

描述:
sp 指向的信号无效。

EBUSY

描述:
sp 所指向的信号的计数为零。

销毁信号状态
使用 sema_destroy(3C) 可以销毁与 sp 所指向的信号相关联的任何状态。不会释放用来存储该信号的空间。

sema_destroy(3C) 语法
#include <thread.h>int sema_destroy(sema_t *sp);
sema_destroy(3C) 返回值
sema_destroy() 在成功运行后返回 0。如果检测到以下情况,sema_destroy() 将失败并返回对应的值。

EINVAL

描述:
sp 指向的信号无效。

跨进程边界同步
每个同步元语都可以设置为跨进程边界使用。可通过以下方法来设置跨边界同步:确保同步变量位于共享内存段中,并在 type 设置为 USYNC_PROCESS 的情况下调用相应的 init 例程。

如果 type 设置为 USYNC_PROCESS,则针对同步变量执行的操作与 type 为 USYNC_THREAD 时针对变量执行的操作相同。

mutex_init(&m, USYNC_PROCESS, 0);rwlock_init(&rw, USYNC_PROCESS, 0);cond_init(&cv, USYNC_PROCESS, 0);sema_init(&s, count, USYNC_PROCESS, 0);
生成方和使用者问题示例
示例 8–2 说明了生成方和使用者位于不同进程时的生成方和使用者问题。主例程将与其子进程共享的全零内存段映射到其地址空间。请注意,必须调用 mutex_init() 和 cond_init(),因为同步变量的 type 为 USYNC_PROCESS。

创建子进程是为了运行使用者,父进程则运行生成方。

此示例还说明了生成方和使用者的驱动程序。producer_driver 从 stdin 读取字符并调用 producer。consumer_driver 通过调用 consumer 来获取字符并将这些字符写入 stdout 中。

示例 8–2 的数据结构与用于解析条件变量的数据结构相同。请参见嵌套锁定和单链接列表的结合使用示例。


--------------------------------------------------------------------------------

示例 8–2 使用 USYNC_PROCESS 时的生成方和使用者问题

  1. main() {    int zfd;    buffer_t *buffer;    zfd = open(“/dev/zero”, O_RDWR);    buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t),        PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0);    buffer->occupied = buffer->nextin = buffer->nextout = 0;    mutex_init(&buffer->lock, USYNC_PROCESS, 0);    cond_init(&buffer->less, USYNC_PROCESS, 0);    cond_init(&buffer->more, USYNC_PROCESS, 0);    if (fork() == 0)        consumer_driver(buffer);    else        producer_driver(buffer);}void producer_driver(buffer_t *b) {    int item;    while (1) {        item = getchar();        if (item == EOF) {            producer(b, `\0');            break;        } else            producer(b, (char)item);    }}void consumer_driver(buffer_t *b) {    char item;    while (1) {        if ((item = consumer(b)) == '\0')            break;        putchar(item);    }}
复制代码
--------------------------------------------------------------------------------

创建子进程是为了运行使用者,父进程则运行生成方。

fork() 和 Solaris 线程的特殊问题
在 Solaris 10 发行版之前,Solaris 线程和 POSIX 线程以不同的方式定义 fork() 的行为。有关 fork() 问题的详细讨论,请参见进程创建:exec 和 exit 问题。

Solaris libthread 同时支持 fork() 和 fork1()。fork() 调用具有 "Fork-All" 语义。fork() 可用来复制进程中的所有内容(包括线程和 LWP),从而创建父进程的准确克隆。fork1() 调用所创建的克隆中仅有一个线程,它可复制进程状态和地址空间,但是仅克隆调用线程。

POSIX libpthread 仅支持 fork(),该函数与 Solaris 线程中的 fork1() 具有相同语义。

fork() 具有 "Fork-All" 语义还是 "Fork-One"语义取决于所使用的库。使用 -lthread 进行链接可以赋予 fork() "Fork-All" 语义;使用 -lpthread 进行链接可以赋予 fork() "Fork-One"语义。

从 Solaris 10 发行版开始,fork() 在 Solaris 线程和 POSIX 线程中具有相同的语义。具体来说,fork1() 语义仅复制调用方。对于需要使用“复制全部”语义的应用程序,提供了一个新函数 forkall()。

EFAULT

描述:
rwlp 指向的地址非法。

相似的 Solaris 线程函数
表 8–3 相似的 Solaris 线程函数
操作
相关函数说明

创建线程
thr_create 语法

获取最小的栈大小
thr_min_stack 语法

获取线程标识符
thr_self 语法

停止执行线程
thr_yield 语法

向线程发送信号
thr_kill 语法

访问调用线程的信号掩码
thr_sigsetmask 语法

终止线程
thr_exit 语法

等待线程终止
thr_join 语法

创建线程特定的数据键
thr_keycreate 语法

设置线程特定数据
thr_setspecific 语法

获取线程特定数据
thr_getspecific 语法

设置线程优先级
thr_setprio 语法

获取线程优先级
thr_getprio 语法


创建线程
thr_create(3C) 例程是 Solaris 线程接口中最详细的所有例程其中之一。

使用 thr_create(3C) 可以向当前的进程中增加新的受控线程。对于 POSIX 线程,请参见pthread_create 语法。

thr_create 语法
#include <thread.h>int thr_create(void *stack_base, size_t stack_size,    void *(*start_routine) (void *), void *arg, long flags,    thread_t *new_thread);size_t thr_min_stack(void);
请注意,新线程不会继承暂挂的信号,但确实会继承优先级和信号掩码。

stack_base。包含新线程所使用的栈的地址。如果 stack_base 为 NULL,则 thr_create() 会为新线程分配一个至少为 stack_size 字节的栈。

stack_size。包含新线程所使用的栈的大小(以字节数表示)。如果 stack_size 为零,则使用缺省大小。在大多数情况下,零值最适合。如果 stack_size 不为零,则 stack_size 必须大于 thr_min_stack() 返回的值。

通常,无需为线程分配栈空间。系统会为每个线程的没有保留交换空间的栈分配 1 MB 的虚拟内存。系统使用 mmap(2) 的 -MAP_NORESERVE 选项来进行分配。

start_routine。包含用以开始执行新线程的函数。start_routine() 返回时,该线程将退出,并且其退出状态会设置为 start_routine 返回的值。请参见thr_exit 语法。

arg。可以是 void 所描述的任何变量,通常是任何大小为 4 字节的值。对于较大的值,必须通过将该参数指向对应的变量来间接传递。

请注意,仅可以提供一个参数。要在程序中采用多个参数,请将多个参数编码为单个参数,如通过将这些参数放在一个结构中。

flags。指定所创建的线程的属性。在大多数情况下,最适合使用零值。

flags 中的值是通过对以下参数执行按位或运算(包含边界值)来构造的:

THR_SUSPENDED。暂停新线程,并且不执行 start_routine,直到 thr_continue() 启动该线程为止。使用 THR_SUSPENDED 可以在运行该线程之前对其执行操作,如更改其优先级。

THR_DETACHED。分离新线程,以便在该线程终止之后,可以立即重用其线程 ID 和其他资源。如果不想等待线程终止,可以设置 THR_DETACHED。


--------------------------------------------------------------------------------
注 –
如果没有明确分配同步,则未暂停的分离线程会失败。如果失败,则从 thr_create() 返回该线程的创建者之前,会将该线程 ID 重新指定给另一个新线程。


--------------------------------------------------------------------------------

THR_BOUND。将新线程永久绑定到 LWP。新线程是绑定线程。从 Solaris 9 发行版开始,系统不再区分绑定线程和非绑定线程,所有的线程均视为绑定线程。

THR_DAEMON。将新线程标记为守护进程。守护进程线程始终处于分离状态。THR_DAEMON 表示 THR_DETACHED。所有非守护进程线程退出时,该进程也会随之退出。守护进程线程不会影响进程的退出状态,并且在退出对线程数进行计数时会被忽略。

进程的退出方法有两种,一是调用 exit(),二是使用进程中不是通过 THR_DAEMON 标志创建的每个线程来调用 thr_exit(3C)。 进程所调用的应用程序或库可以创建一个或多个线程,在确定是否退出时应当忽略(不计数)这些线程。THR_DAEMON 标志可标识在进程退出条件中不计数的线程。

new_thread。如果 new_thread 不为 NULL,则它将指向 thr_create() 成功时用来存储新线程 ID 的位置。调用方负责提供该参数所指向的存储空间。线程 ID 仅在调用进程中有效。

如果对该标识符不感兴趣,请向 new_thread 提供一个 NULL 值。

thr_create 返回值
thr_create() 函数在成功完成之后返回零。其他任何返回值都表示出现了错误。如果检测到以下情况之一,thr_create() 将失败并返回对应的值。

EAGAIN

描述:
超出了系统限制,如创建的 LWP 过多。

ENOMEM

描述:
可用内存不足,无法创建新线程。

EINVAL

描述:
stack_base 不为 NULL,并且 stack_size 小于 thr_min_stack() 返回的值。

获取最小栈大小
使用 thr_min_stack(3C) 可以获取线程的最小栈大小。

Solaris 线程中的栈行为通常与 pthread 中的栈行为相同。有关设置和操作栈的更多信息,请参见关于栈。

thr_min_stack 语法
#include <thread.h>size_t thr_min_stack(void);
thr_min_stack() 会返回执行空线程所需的空间量。创建空线程的目的是执行空过程。由于有用线程需要的栈要大于绝对最小栈,因此在减小栈大小时请务必小心。

执行非空过程的线程所分配的栈大小应大于 thr_min_stack() 的大小。

如果某个线程是借助于用户提供的栈创建的,则用户必须保留足够的空间才能运行该线程。动态链接的执行环境会增加确定线程最小栈要求的难度。

可以通过两种方法来指定自定义栈。第一种方法是为栈位置提供 NULL,从而要求运行时库为该栈分配空间,但是向 thr_create() 提供 stacksize 参数中所需的大小。

另一种方法是全面负责栈管理的各个方面,并向 thr_create() 提供一个指向该栈的指针。这意味着不但需要负责分配栈,还需要负责取消分配栈。线程终止时,必须安排对该线程的栈进行处理。

当您分配自己的栈时,请确保通过调用 mprotect(2) 在该栈末尾附加一个红色区域。

大多数用户都不应当通过用户提供的栈来创建线程。用户提供的栈之所以存在,只是为了支持要求对其执行环境进行完全控制的应用程序。

相反,用户应当由系统来管理对栈的分配。系统提供的缺省栈应当能够满足所创建的任何线程的要求。

thr_min_stack 返回值
未定义任何错误。

获取线程标识符
使用 thr_self(3C) 可以获取调用线程的 ID。对于 POSIX 线程,请参见pthread_self 语法。

thr_self 语法
#include <thread.h>thread_t thr_self(void);
thr_self 返回值
未定义任何错误。

停止执行线程
thr_yield(3C) 会导致当前的线程停止执行,以便执行另一个具有相同或更高优先级的线程。 否则,thr_yield() 不起任何作用。但是,调用 thr_yield() 无法保证当前的线程会停止执行。

thr_yield 语法
#include <thread.h>void thr_yield(void);
thr_yield 返回值
thr_yield() 不会返回任何内容并且不会设置 errno。

向线程发送信号
thr_kill(3C) 可用来向线程发送信号。对于 POSIX 线程,请参见pthread_self 语法。

thr_kill 语法
#include <thread.h>#include <signal.h>int thr_kill(thread_t target_thread, int sig);
thr_kill 返回值
如果成功完成,thr_kill() 将返回 0。如果检测到以下任一情况,thr_kill() 将失败并返回对应的值。如果失败,则不发送任何信号。

ESRCH

描述:
未找到与 thread ID 所指定的线程相关联的线程。

EINVAL

描述:
sig 参数值不为零。sig 无效或者是不支持的信号编号。

访问调用线程的信号掩码
使用 thr_sigsetmask(3C) 可以更改或检查调用线程的信号掩码。

thr_sigsetmask 语法
#include <thread.h>#include <signal.h>int thr_sigsetmask(int how, const sigset_t *set, sigset_t *oset);
thr_sigsetmask() 可用来更改或检查调用线程的信号掩码。每个线程都有自已的信号掩码。新线程会继承调用线程的信号掩码和优先级,但不会继承暂挂的信号。新线程的暂挂信号将为空。

如果参数 set 的值不为 NULL,则 set 会指向一组信号,这组信号可用来修改当前阻塞的信号组。如果 set 的值为 NULL,则 how 的值没有意义,并且不会修改线程的信号掩码。使用此行为可了解有关当前阻塞的信号的情况。

how 的值可指定更改信号组的方法。how 可以为以下值之一。

SIG_BLOCK。set 对应于一组要阻塞的信号。这些信号会添加到当前的信号掩码中。

SIG_UNBLOCK。set 对应于一组要解除阻塞的信号。这些信号会从当前的信号掩码中删除。

SIG_SETMASK。set 对应于新的信号掩码。当前的信号掩码将替换为 set。

thr_sigsetmask 返回值
如果成功完成,thr_sigsetmask() 将返回 0。如果检测到以下任一情况,thr_sigsetmask() 将失败并返回对应的值。

论坛徽章:
0
2 [报告]
发表于 2011-12-22 16:34 |只看该作者
学习了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP