1. 线程限制
在应用服务器中打流量,发现存在命令超时问题
分析:线程已经被唤醒,但是得不到调度。
操作系统是Linux-2.6.17,内核被设置为非抢占式内核,因此一旦一个内核线程得到调度,且该线程总是获得资源而没有阻塞,那么该线程将一直运行下去,其它线程得不到调度。
超时问题解决的核心是清除线程长期霸占CPU的情况。由于每个用户连接对应多个内核线程,也需要消除一个阵列霸占CPU的情况,从而避免其他阵列线程得不到调度。
2. 时间片
普通线程的时间片时根据动态优先级计算出来的,其更新时机是在时钟中断中进行,具体函数是schedule_tick(),该函数会递减时间片计数器,检查当前线程时间片是否用完,如果是,则是根据动态优先级重新赋值,并设置TIF_NEED_RESCHED标志。
在许多执行长迭代任务的程序中需要直接调用调度程序,具体函数是schedule(),每次迭代循环时,程序需要检查TIF_NEED_RESCHED标志,如果需要就调用schedule()自动放弃CPU,示例代码如下:
while(1)
{
do something;
if(test_thread_flag(TIF_NEED_RESCHED))
schedule();//或者调用yield()
}
在linux-2.6.17内核中,普通线程的基本时间片是100ms,另外内核提供yield()函数允许线程在不被挂起的情况下自愿放弃CPU,线程仍处于TASK_RUNNING状态。
当然,在抢占式内核中,中断处理程序返回内核空间的时候,如果当前线程的时间片已经耗尽且其正在执行的代码不在锁中,则会发生线程切换。
3. 改进的处理方式
1) 修改线程的优先级别,让所有线程都是普通线程优先级别。
2) 当一个线程处理一定的命令数后(或者在while(1)中循环一定数后),主动调用yield()函数释放CPU。
原因:所有线程优先级相同,防止高优先级线程被反复调用,同时线程处理一定命令后,通过yield()函数主动释放CPU,防止同一线程被反复调用。虽然这种调控方法不精确,但是粗略地调和了非抢占式内核的线程调度。
4. 当前处理方式
内核态的几种方式:
1) kernel_thread 启动一个内核线程,其优先级别为普通优先级别,在进入while循环之前调用daemonize()函数去掉用户空间资源。
2)kthread_create,kthread_run启用一个工作队列,其优先级别高于普通线程
3)采用工作队列work_queue方式,这种方式的线程优先级别为SW<,表示该线程优先级别高于普通线程,工作队列属于软中断的一种方式,但可以阻塞。
4)软中断,中断 中断服务例程是系统中优先级最高的任务,会中断线程的处理。
用户态创建线程的方式
1)pthread_create 创建普通优先级别的线程
5. 线程释放CPU的方式
1) 采用定时睡眠方式
2) 采用信号量或wait_event
3) 内核调度模式
__set_current_state(TASK_INTERRUPLIBLE);
if(资源获取)
schedule();
__set_current_state(TASK_RUNNING);
4) 工作队列 work_queue
执行完毕后,由OS执行CPU切换。
5) 信号sigwait select
这种出现在用户态中线程,sigwait使用的是信号,select类似信号量。
6. 总结
在确定需要创建一个线程后,那么就需要考虑创建线程的方式,调用内核中不同函数就会创建不同优先级别的线程。大多数情况下,不需要创建高级别的线程,应用系统中的线程优先级分配是需要根据应用而确定的。
在线程的运行过程中,什么时候阻塞也需要加以考虑,防止类似死循环的情况发生。至于如何阻塞线程,也就是释放CPU的形式不是关键。