Linux内核---多线程
来源:互联网 发布:相册软件哪个好 编辑:程序博客网 时间:2024/06/07 03:12
A、 线程和进程的差别
在现代操作系统中,进程支持多线程。进程是资源管理及分配的最小单元;而线程是程序执行的最小单元。一个进程的组成实体可以分为两大部分:线程集和资源集。进程中的线程是动态的对象,代表了进程指令的执行过程。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。线程有自己的私有数据:程序计数器,栈空间以及寄存器。
现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发,即在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机。
如果采用多进程的方法,则有如下问题:
? fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用。
? 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。
线程推广了进程的概念,使一个进程可以包含多个活动(或者说执行序列等等)。多线程的优点和缺点实际上是对立统一的。使用线程的优点在于:
? a 改进程序的实时响应能力;
? b 更有效的使用多处理器,真正的并行(parallelism);
? c 改进程序结构,具备多个控制流;
? d 通讯方便,由于共享进程的代码和全局数据;
? e 减少对系统资源的使用。 对属于同一个进程的线程之间进行调度切换时不需要调用系统调用,因此将减少额外的消耗,往往一个进程可以启动上千个线程也没有什么问题。
缺点在于:
由于各线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。
B 、 内核线程与用户进程的关系
内核线程也可以叫内核任务,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。在Linux中,内核线程与普通进程有一些本质的区别,从以下几个方面可以看出二者之间的差异:
? 内核线程能够访问内核中数据,调用内核函数,而普通进程只有通过系统调用才能执行内核中的函数;
? 内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态;
? 因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用4GB的地址空间。
C、 创建内核线程最基本的两个接口函数是:
1、kthread_run(threadfn, data, namefmt, ...) 和
kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)
kthread_run 事实上是一个宏定义:
#define kthread_run(threadfn, data, namefmt, ...) ({ struct task_struct *__k = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); if (!IS_ERR(__k)) wake_up_process(__k); __k; })
kthread_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行。外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用kthread_create()创建线程,如果创建成功,再调用wake_up_process()唤醒新创建的线程。kthread_create()根据参数向kthread_create_list中发送一个请求,并唤醒kthread,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。
2、int kthread_stop(struct task_struct *k);
kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。在调用 kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过 kthread_run 返回的 task_stuct 是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,这是因为返回的是错误码,大致从0xfffff000~0xffffffff。
3、int kthread_should_stop(void);
kthread_should_stop()返回should_stop标志(参见 struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。
4、#define wait_event_interruptible(wq, condition) ({ int __ret = 0;
if (!(condition)) __wait_event_interruptible(wq, condition, __ret); __ret; })
函数作用:等待事件,置于休眠。成功地唤醒一个被wait_event_interruptible()的进程,需要满足: 在 1)condition为真的前提下,2) 调用wake_up()。
5、 void wake_up_interruptible (wait_queue_head_t *q); 说明:唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。线程不会主动的自己调度,需要显式的通过schedule 或者 schedule_timeout()来调度。
6、int waitqueue_active( &task_wait_queue);查看队列中是否有等待线程。
7、这里介绍另一种线程间通信的方式:completion机制。Completion机制是线程间通信的一种轻量级机制:允许一个线程告诉另一个线程工作已经完成。为使用 completion, 需要包含头文件 <linux/completion.h>。
struct completion my_completion;init_completion(&my_completion);等待 completion 是一个简单事来调用: void wait_for_completion(struct completion *c); 注意:这个函数进行一个不可打断的等待. 如果你的代码调用 wait_for_completion 并且
没有人完成这个任务, 结果会是一个不可杀死的进程。 wait_for_completion_timeout( &msg_completion, 4*HZ ); //timeout防止卸载模块时堵塞在这里等待 completion 事件可能通过调用下列之一来发出:void complete(struct completion *c); void complete_all(struct completion *c);
如果多于一个线程在等待同一个 completion 事件, 这 2 个函数做法不同.complete 只唤醒一个等待的线程, 而 complete_all 允许它们所有都继续。内核笔记:完成变量completion: http://blog.csdn.net/u013661873/article/details/19201561
8、Linux等待队列:http://www.cnblogs.com/zhuyp1015/archive/2012/06/09/2542882.html9、补充(各种平台下的多线程)http://www.cnblogs.com/zhuyp1015/archive/2012/06/14/2549973.html
C/C++指针相关 : http://www.cnblogs.com/zhuyp1015/archive/2012/06/06/2538959.html
支持kill命令,同时rmmod的时候也能杀死线程: http://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548494.html
epoll源码学习笔记(linux2.6.32):http://blog.csdn.net/martin_zy/article/details/7304130
Linux内核线程之深入浅出: http://blog.163.com/jiams_wang/blog/static/303391492012103010374038/
Linux内核多线程(一): http://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545624.html
说明:这个程序的目的就是,使用一个线程(thread_function_1)通知另外一个线程(thread_function)某个条件(tc == 10)满足(比如接收线程收到10帧然后通知处理线程处理接收到的数据)
运行结果:
程序加载并运行(tc 的值等于10 之后 就会唤醒另外一个线程,之后tc又从10开始计数):
#include <linux/init.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/wait.h>MODULE_LICENSE("Dual BSD/GPL"); static struct task_struct * _tsk; static struct task_struct * _tsk1;static int tc = 0;static wait_queue_head_t log_wait_queue;static int thread_function(void *data) { do { printk(KERN_INFO "IN thread_function thread_function: %d times \n", tc); wait_event_interruptible(log_wait_queue,tc == 10); tc = 0; ///必须加这一行,内核才会进行调度。内核线程不像应用程序会主动调度,我们需要显式的使用调度函数,想要在thread_function_1中去重置tc的值是不可能的,因为线程不会被调度,该线程会一直占用CPU printk(KERN_INFO "has been woke up !\n"); }while(!kthread_should_stop()); return tc; } static int thread_function_1(void *data) { do { printk(KERN_INFO "IN thread_function_1 thread_function: %d times\n", ++tc); if(tc == 10 && waitqueue_active(&log_wait_queue)) { wake_up_interruptible(&log_wait_queue); } msleep_interruptible(1000); }while(!kthread_should_stop()); return tc; } static int hello_init(void) { printk(KERN_INFO "Hello, world!\n"); init_waitqueue_head(&log_wait_queue); _tsk = kthread_run(thread_function, NULL, "mythread"); if (IS_ERR(_tsk)) { //需要使用IS_ERR()来判断线程是否有效,后面会有文章介绍IS_ERR() printk(KERN_INFO "first create kthread failed!\n"); } else { printk(KERN_INFO "first create ktrhead ok!\n"); } _tsk1 = kthread_run(thread_function_1,NULL, "mythread2"); if (IS_ERR(_tsk1)) { printk(KERN_INFO "second create kthread failed!\n"); } else { printk(KERN_INFO "second create ktrhead ok!\n"); } return 0; } static void hello_exit(void) { printk(KERN_INFO "Hello, exit!\n"); if (!IS_ERR(_tsk)){ int ret = kthread_stop(_tsk); printk(KERN_INFO "First thread function has stopped ,return %d\n", ret); } if(!IS_ERR(_tsk1)) { int ret = kthread_stop(_tsk1); printk(KERN_INFO "Second thread function_1 has stopped ,return %d\n",ret); }} module_init(hello_init); module_exit(hello_exit);
下面来看使用completion机制的实现代码:
#include <linux/init.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/wait.h>#include <linux/completion.h> MODULE_LICENSE("Dual BSD/GPL"); static struct completion comp; static struct task_struct * _tsk; static struct task_struct * _tsk1;static int tc = 0; static int thread_function(void *data) { do { printk(KERN_INFO "IN thread_function thread_function: %d times \n", tc); wait_for_completion(&comp); //tc = 0; ///在哪里都行 printk(KERN_INFO "has been woke up !\n"); }while(!kthread_should_stop()); return tc; } static int thread_function_1(void *data) { do { printk(KERN_INFO "IN thread_function_1 thread_function: %d times\n", ++tc); if(tc == 10) { complete(&comp); tc = 0; } msleep_interruptible(1000); }while(!kthread_should_stop()); return tc; } static int hello_init(void) { printk(KERN_INFO "Hello, world!\n"); init_completion(&comp); _tsk = kthread_run(thread_function, NULL, "mythread"); if (IS_ERR(_tsk)) { printk(KERN_INFO "first create kthread failed!\n"); } else { printk(KERN_INFO "first create ktrhead ok!\n"); } _tsk1 = kthread_run(thread_function_1,NULL, "mythread2"); if (IS_ERR(_tsk1)) { printk(KERN_INFO "second create kthread failed!\n"); } else { printk(KERN_INFO "second create ktrhead ok!\n"); } return 0; } static void hello_exit(void) { printk(KERN_INFO "Hello, exit!\n"); if (!IS_ERR(_tsk)){ int ret = kthread_stop(_tsk); printk(KERN_INFO "First thread function has stopped ,return %d\n", ret); } if(!IS_ERR(_tsk1)) { int ret = kthread_stop(_tsk1); printk(KERN_INFO "Second thread function_1 has stopped ,return %d\n",ret); }} module_init(hello_init); module_exit(hello_exit);
- linux内核多线程
- Linux内核多线程
- linux内核多线程
- Linux内核多线程
- linux 内核多线程编程
- Linux内核---多线程
- Linux内核多线程小结(一)
- Linux内核多线程(一)
- Linux内核多线程(二)
- Linux内核多线程(三)
- Linux内核多线程(四)
- Linux内核多线程(一)
- Linux内核多线程(二)
- Linux内核多线程(三)
- Linux内核多线程(四)
- Linux内核多线程(二)
- Linux内核多线程(三)
- Linux内核多线程(四)
- ubuntu下crontab 定时执行php脚本
- css3选择器
- 【Android开发细节】之【@id与@+id区别】
- Spring的IOC原理[通俗解释一下]
- vs2013快捷键的使用
- Linux内核---多线程
- Caused by: org.xml.sax.SAXParseException; systemId: file:/opt/apache-nutch-2.2.1/runtime/local/conf/
- leetcode:60. Permutation Sequence
- 一个 fork 的面试题
- asm基础——在mac下使用nasm进行汇编
- 元素淡入淡出效果
- openSUSE 安装oracle jdk
- Exception in thread "main" java.lang.RuntimeException: job failed: name=inject urls, jobid=job_local
- hdu3524 Perfect Squares