面向对象程序设计:基于面向对象操作系统开发平台(OSKit)的分析与程序设计(3)来源: 发布时间:星期四, 2009年2月12日 浏览:36次 评论:0
作者:汤海京 线程分析篇的线程通信 本文是基于面向对象操作系统开发平台(OSKIT)分析和设计第 3篇作者将对线程通讯进行分析 众所周知在Linux中进程为了能在同项任务上协调工作彼此的间必须能够进行通信例如在个shell管道中第个进程输出必须传输到第 2个进程这样沿着管道传递下去因此在需要通信进程的间应该使用种结构较好通信方式 Linux支持许多区别形式进程间通信机制在特定情况下它们各有自己优缺点如利用管道进行通信最大缺点就是只有成为系统pipe进程子孙后代进程才可以使用管道因此相互无关进程不能通过管道通信 OSKit开发人员们充分考虑了各种通信方式利弊扬长避短采用了如下两种线程间通信方式:消息队列和信号量虽然这还有很多不足的处但较的 V以有所改进 线程间通信 3.1.1消息队列 OSKit提供消息队列机制于UNIX十分相似个和多个线程可向消息队列写入消息而个或多个线程可从消息队列中读取消息这种线程间通信机制就像客户机/样客户机向发送请求消息服务器读取消息并执行相应请求 OSKit将消息描述成在内核地址空间个内部链表每个消息队列由个 IPC标识号唯标识 3.1.2信号 尽管大多数线程间通信是计划好但是同时还需要处理不可预知通信问题例如用户使用文本编辑器要求列出个大文件全部内容但随即他认识到该操作并不重要这是就需要种思路方法来中止编辑器工作比如说我们可以按DEL键来实现按DEL键实际上是向编辑器发送个信号编辑器收到此信号便立即停止打印文件内容信号还可以用来报告硬件捕获到特定中断如非法指令和浮点运算溢出、超时也通过信号实现 在OSKit中信号是种重要线程间通信机制线程通过信号知道系统中正在出现事件信号个特点就是它异步性这表示线程在它执行期间任何时候都可以接受到信号甚至可能当线程正在执行系统时候接到信号因此线程必须随时为相应信号作好准备 下面我们再谈谈信号生成和交付当引起信号事件首次出现时就认为信号为线程而生成也就是说信号被发送到线程这类事件例子包括: 时间片到时终端激活硬件故障检测kill( )在某些情况下个事件可以为多个线程生成信号每个线程采用相应动作来响应系统定义每种信号(见信号量表3.1) 在信号生成和交付的间该信号被挂起但在用户级是检测不到这个间隔每个线程都拥有个信号掩码(sig_mask)它规定了当前被阻塞而不能交付给线程信号集线程信号掩码是从父线程继承而来我前面提到sigaction( ),sigprocmask( )和sigsuspend( )都是对信号掩码操作 信号动作和信号有关动作分为 3种:SIG_DFL,SIG_IGN或指向指针在最开始进入( )的前所有信号都将被置成SIG_DFL或SIG_IGN SIG_DFL信号专用默认动作: 如果默认动作是暂停线程则该线程执行被暂时挂起当线程暂停期间发送给线程任何附加信号都不交付直到该线程开始执行但是SIGKILL除外 把挂起信号信号动作设置成SIG_DFL且其默认动作是忽略信号 (SIGCHLD) SIG_IGN忽略信号 该信号交付对线程没有影响 系统不允许把SIGKILL或SIGTOP信号动作设置为SIG_DFL 指向指针--捕获信号 信号经交付接收线程就在指定地址上执行信号捕获在信号捕获返回后接受线程必须在被中断点恢复执行 用C语言思路方法进入信号捕捉: void func (signo) signo; func( )是指定信号捕捉signo是正被交付信号编码 如果SIGFPE,SIGILL或SIGSEGV信号不是由C标准定义kill( )或raise( )所生成则从信号SIGFPE,SIGILL,SIGSEGV信号捕获正常返回后线程行为是未定义 系统不允许线程捕获SIGKILL和SIGSTOP信号 如果线程为SIGCHLD信号建立信号捕获而该线程有未被等待以终止子线程时没有规定是否要生成SIGCHLD信号来指明那个子线程 每种信号都被OSKit给予了个符号名对于32位i386平台而言个字32位因而信号有32种下面表给出了常用符号名、描述和它们信号值 符号名 信号值 描述 是否符合POSIX SIGHUP 1 在控制终端上检测到挂断或控制线程死亡 是 SIGINT 2 交互注意信号 是 SIGQUIT 3 交互中止信号 是 SIGILL 4 检测到非法硬件指令 是 SIGTRAP 5 从陷阱中回朔 否 SIGABRT 6 异常终止信号 是 SIGEMT 7 EMT 指令 否 SIGFPE 8 不正确算术操作信号 是 SIGKILL 9 终止信号 是 SIGBUS 10 总线 否 SIGSEGV 11 检测到非法内存 是 SIGSYS 12 系统call参数 否 SIGPIPE 13 在无读者管道上写 是 SIGALRM 14 报时信号 是 SIGTERM 15 终止信号 是 SIGURG 16 IO信道紧急信号 否 SIGSTOP 17 暂停信号 是 SIGTSTP 18 交互暂停信号 是 SIGCONT 19 如果暂停则继续 是 SIGCHLD 20 子线程终止或暂停 是 SIGTTIN 21 后台线程组成员试图从控制终端上读出 是 SIGTTOU 22 后台线程组成员试图写到控制终端上 是 SIGIO 23 允许I/O信号 否 SIGXCPU 24 超出CPU时限 否 SIGXFSZ 25 超出文件大小限制 否 SIGVTALRM 26 虚时间警报器 否 SIGPROF 27 侧面时间警报器 否 SIGWINCH 28 窗口大小更改 否 SIGINFO 29 消息请求 否 SIGUSR1 30 保留作为用户自定义信号1 是 SIGUSR2 31 保留作为用户自定义信号 是 请求按默认规则进行信号处理 SIG_DFL (void (*)()) 0 请求忽略信号 SIG_IGN (void (*)()) 1 注意: 信号队列中最多允许有64个信号 3.2 pthreads/pthread_ipc.c 此源码文件包括了套完整消息队列型线程通信机制消息队列是线程通信主要手段通过阅读下面分析将使读者了解OSKit到底是用什么来具体实现信号队列从而进行线程通信 3.2.1 消息发送: 介绍说明:当个线程要向另个线程发送消息时候OSKit使用下面定义来实现个线程向另个线程传递消息 oskit_error_t oskit_ipc_send ( pthread_t dst,void *msg, oskit_size_t msg_size, oskit_s32_t timeout ) dst:目标地址 *msg:指向消息指针 msg_size:消息大小(32位无符号整形) timeout:超时 3.2.2 send算法 发送个同步消息,直到接收者发送终止消息或超时才结束为避免死锁,给没个人加个锁,并关中断 assert_errupts_enabled( ); disable_errupts( ); pthread_lock (&target->waitlock); 通过条件,检测目标线程是否在等待接受: a:目标线程等待标志 & THREAD_WS_IPCRECV_WAIT(线程接收等待位) b:目标线程号存在 c:目标线程等于任意个线程 d:消息大小不为0 条件成立: a && ( b || c) && d 1 才可以发送 (target->waitflags & THREAD_WS_IPCRECV_WAIT &&(target->ipc_state.tid pthread->tid||target->ipc_state.tid ANYTID)) { (msg_size) { Memcpy(target->ipc_state.msg, msg,MIN(msg_size,target->ipc_state.msg_size)); (msg_size > target->ipc_state.msg_size) err = OSKIT_ERANGE; } } 把数据大小和发送者ID告诉接受线程: target->ipc_state.msg_size = msg_size; target->ipc_state.tid = pthread->tid; 清除接受位: target->waitflags &= ~THREAD_WS_IPCRECV_WAIT; pthread_unlock ( &target->waitlock ); 注意: flagA & = ~flagB 实际上就是将flagB中等于1位在flagB中清0 发送队列: queue_enter ( &target->ipc_state.senders,pthread,pthread_thread_t*, ipc_state.senders_chain) &target->ipc_state.senders:发送线程ID pthread: pthread_thread_t *:指向消息指针是pthread_thread_t类型 ipc_state.senders_chain:发送队列指针 队列间参数传递: pthread->ipc_state.msg = msg; /* 消息 */ pthread->ipc_state.msg_size = msg_size; /* 消息长度 */ pthread->ipc_state.tid = dst; /* 消息目地址 */ pthread->waitflags = THREAD_WS_IPCSEND_WAIT; /* 0
相关文章
读者评论发表评论 |