一、用户态线程VS内核态线程
用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。
优点如下:
1. 当线程产生本地阻塞后,运行时系统检测线程表中的就绪线程,线程切换无须涉及用户态和核心态的切换,这样的线程切换比陷入内核快一个数量级。这样的线程切换不需要TRAP系统调用,不需要刷新内存高速缓存,这就使得线程调度非常快捷。
2.自己定义调度算法啊,不用担心垃圾回收机制的应用程序在不合适的时刻停止线程,
3扩展性好,内核级线程需要固定的堆栈空间和表格空间,内核线程数量非常大的时候会出现问题。
缺点为当一个进程内的线程调用阻塞系统调用,则整个进程内的线程也跟着阻塞,
内核线程与运行时系统过程相比,代价相当可观
调度程序激活机制
调度程序激活工作的目标是模拟内核线程的功能,但是为线程包提供通常在用户空间中才能实现的更好的性能和更大的灵活性。
内核给每个进程安排一定数量的虚拟机,进程可以拥有更多的虚拟机并且在不用时退回
上行调用:当内核了解到一个线程被阻塞后,通知该进程的运行时系统,并且以堆栈的形式传递阻塞的线程编号和所发生的事件。内核通过在已知的起始地址启动运行时系统从而发出通知,
一旦如此激活运行时系统,就重新调度线程。
二、单线程代码多线程化
考虑问题 1: 线程1访问某个文件,出现错误,将错误编码放入errno的变量里,当该线程的CPU线程时间用完,线程同时也访问文件,出现错误,错误编码同时放入errno中,此时线程1执行接下去的错误,就是不正确的。
所以解决方案是创建属于线程的私有全局变量
下面为创建线程的私有全局变量
create_global("bufptr")
set_global("bufptr",&buf)
bufptr = read_global("bufptr")
这三个函数分别创建 设置和读取 属于线程的寄存器
考虑问题2:UNiX中的malloc函数,它维护着内存使用的关键表格。在malloc更新表格的同时,有可能处于一种不一致的状态,指针的指向不定,而此时发生了线程切换,而另一个线程来了一个线程调用,此时由于使用了一个无效的指针从而导致程序崩溃。
用一个二进制位,表示当前的库正处使用中,任何试图使用该库的其他线程都会被阻塞。尽管可以工作,但是它会极大地降低系统潜在的并行性。
考虑问题3:有些信号逻辑上是线程专用的,但是另一些不是。当线程完全在用户空间中实现时,内核根本不知道有线程存在,因此很难将信号发送给正确的线程。
有些信号不是线程专用的,谁应该捕捉它们?
考虑问题4:一个进程又多个线程,则有多个堆栈,内核不了解所有的堆栈,就不能使它们自动增长,直到造成堆栈出错
三、进程间通信
屏蔽中断
在单处理器系统中,最简单的方法是使每个进程刚刚进入临界区后立即屏蔽所有中断,并在就要离开时打开中断。屏蔽中断后,时钟中断也被屏蔽,CPU只有在发生时钟中断或者其他中断时才能切换进程,在屏蔽中断之后,CPU将不会被切换到其他进程。
锁变量
当进程要进入临界区时,测试锁变量是否为0,如果为0,则将锁变量设置为1,并且进入临界区,否则,等待锁变量为0,才进入临界区。
当一个进车检测为0,还未设置为1的同时,另一个进程被调度,进入缓冲区,此时两个进程进入缓冲区。
TSL指令
当进程进入缓冲区时锁住内存总线,这样即使是多处理器系统,也能实现临界区的互斥访问
以上的方法,在进程进入不了缓冲区时,则选择忙等待。
考虑一台计算机有两个进程,H优先级高,L优先级低。调度规则规定,只要H处于就绪态它就可以运行。在某一时刻,L处于临界区中,此时H变到就绪态,准备运行。现在H开始忙等待,但由于H处于就绪态,L不会被调度,也无法离开临界区,所以H将永远忙等待下去,这种问题称之为优先级反转问题。
睡眠与唤醒
sleep() 和 wakeup()函数,
sleep
进入sleep过程后,核心首先保存进入睡眠时的处理机运行级,再提高处理机的运行优先级,来屏蔽所有的中断,接着将该进程置为“睡眠”状态,将睡眠地址保存在进程表项中,并将该进程放入睡眠队列中。如果进程的睡眠是不可中断的,做了进程上下文的切换后,进程便可安稳地睡眠。当进程被唤醒并被调度执行时,将恢复处理机的运行级为进入睡眠时的值,此时允许中断处理机。
wakeup
该过程的主要功能,是唤醒在指定事件队列上睡眠的所有进程,并将它们放入可被调度的进程队列中。如果进程尚未被装入内存,应唤醒对换进程;如果被唤醒进程的优先级高于当前进程的优先级,则应重置调度标志。最后,在恢复处理机的运行级后返回。
信号量
消息传递
#define N 100
void producer() {
int item;
message m;
while(TRUE){
item = produce_item();
receive(consumer, &m); /*等待接收一个空消息 */
build_message(&m, item); /*将消息与生产的对象绑定*/
send(consumer, &m); /*把消息发给消费者*/
}
}
void consumer(){
int item;
message m;
for(i = 0; i < N; i++)send(producer, &m); /*发送N条空消息,让生产者开始生产*/
while(TRUE){
receive(producer,&m); /*接收到包含数据的消息*/
item = exract_item(&m); /*解出元素*/
send(producer, &m); /*回送空消息作为应答 */
consume_item(item);
}
}