一、什么是并发
所谓并发,就是多个进程同时、并行执行。在单处理器的情况下,并发只是宏观上的,用户会感觉多个程序共同执行,其实只不过是多个进程轮流占用处理器运行,只有在多处理器的情况下才会实现真正的同时执行。
但是,不管单处理器还是多处理器,内核中的并发都会引起共享资源的并发访问。举个简单的例子,有两个相同代码的进程并发执行,它们都是要修改存在与内核中个一个数据data。
情况一:没有出错:
上面的举例是在单处理器的情况下,内核调度AB进程分别在处理器上运行,并且运行完A再运行B,情况相当理想,并没有出错。
情况二:出错了。
同样是单处理器的情况下,但是却出现问题了,进程A执行到一半,内核调度进程B执行,等进程B执行完后再回来执行A。细心一想就会发现,不对劲了,进程B等于白干了!我最后保存的只是执行进程B前的data。
上面只是想说明,在并发执行的情况下,我们根本不能预料到进程什么时候会被调度,一些内核中的共享资源就要有一些相应的保护。白干还没什么大不了,如果搞到系统崩溃就糟糕了。
二、什么是临界区和竞争条件
临界区就是访问和操作共享数据的代码段。之前说过,进程并发访问共享资源是不安全的,那是因为它访问临界区的数据。如果两个进程同时出去临界区,那就会发生资源的抢夺,这个情况就叫做竞争条件。避免并发和防止竞争条件被成为同步,这将是接下来要重点讨论的内容。
三、什么会造成内核中的并发
注意,进程不管是在用户空间还是内核空间都是并发执行的,但是这里主要的是讨论内核中的并发,用户空间中的并发应该是在系统编程时了解的内容,如多进程共享文件等。
在以下情况下内核会造成并发执行:
1)中断:中断是随时可以产生,内核一旦接收到中断,就会放下手头上的工作,优先执行中断。如果中断代码中修改了之前运行进程的共享资源,这样就会出现bug。
2)内核抢占:前一节已经介绍,支持内核抢占的情况下,正在执行的进程随时都有可能被抢占。
3)睡眠:当在内核中执行的进程睡眠,此时就唤醒调度程序,调度新的进程执行。
4)多处理器:多个处理器就能同时执行多个进程。这是真正的同时执行,、。
既然知道了在什么情况下会造成并发,在编写代码时,就要考虑到临界区的保护,在临界区中,避免上述情况的发生会可以避免并发,从而保护共享资源,这就是内核同步。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、单处理器不支持内核抢占的情况下
我现在使用的内核版本2.6.29就是不支持内核抢占。分析两种情况。
情况一,两个进程之间:
在单处理器不支持抢占的情况下,运行在内核的两个内核线程,并不会产生并发。
情况二,中断与进程之间:
中断上下文与普通内核线程之间,会产生并发。在内核线程正在执行时,随时会有可能被中断打断。所以,在临界区的代码,可以通过关闭中断来避免并发。
先写个程序看看在没关中断的情况下,中断可以打断正在执行的内核线程。
例子源代码在:4th_mutex_4/1st
首先看一下驱动的代码怎么写:
/*1st/test.c*/
41 ssize_t test_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
42 {
43 int ret;
44 unsigned long flag = 0;
45
46 for(; ;)
47 {
48 printk("[%s]task pid[%d], context [%s]\n", __FUNCTION__, current->pid, current->comm);
49 mdelay(2000);
50 }
51