码迷,mamicode.com
首页 > 编程语言 > 详细

Linux内核---多线程

时间:2016-06-12 02:06:57      阅读:328      评论:0      收藏:0      [点我收藏+]

标签:

A、     线程和进程的差别

    在现代操作系统中,进程支持多线程。进程是资源管理及分配的最小单元;而线程是程序执行的最小单元。一个进程的组成实体可以分为两大部分:线程集和资源集。进程中的线程是动态的对象,代表了进程指令的执行过程。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。线程有自己的私有数据:程序计数器,栈空间以及寄存器。

    现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发,即在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机

 

如果采用多进程的方法,则有如下问题:

?      fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用。

?      各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。

 

线程推广了进程的概念,使一个进程可以包含多个活动(或者说执行序列等等)。多线程的优点和缺点实际上是对立统一的。使用线程的优点在于:

?      a 改进程序的实时响应能力;

?      b 更有效的使用多处理器,真正的并行(parallelism)

?      c 改进程序结构,具备多个控制流;

?      d 通讯方便,由于共享进程的代码和全局数据;

?      e 减少对系统资源的使用。                                                    对属于同一个进程的线程之间进行调度切换时不需要调用系统调用,因此将减少额外的消耗,往往一个进程可以启动上千个线程也没有什么问题。 

缺点在于:

由于各线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。


 B 、  内核线程与用户进程的关系

内核线程也可以叫内核任务,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。在Linux中,内核线程与普通进程有一些本质的区别,从以下几个方面可以看出二者之间的差异:

?      内核线程能够访问内核中数据,调用内核函数,而普通进程只有通过系统调用才能执行内核中的函数;

?      内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态;

?      因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET3G的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用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.html

9、补充(各种平台下的多线程)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);  


Makefile:

ifeq ($(KERNELRELEASE),)

KERNELDIR ?=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order

.PHONY: modules modules_install clean

else
    kernel_thread-objs := thread_func.o kernel_thread_main.o
    obj-m := kernel_thread.o
endif

kernel_thread_main.c:

/***************************************
------文件名称:kernel_thread_main.c------ 
***************************************/
//kernel_thread_main.c

//建立内核模块必须包含的头文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include "thread_func.h"

MODULE_LICENSE("Dual BSD/GPL");//内核模块的许可权限
MODULE_AUTHOR("SunTianYu");   //作者 
MODULE_DESCRIPTION("Kernel Thread Create!/n");//描述

static struct task_struct* kernel_thread_tsk;
static struct task_struct* business_thread_tsk_A;
static struct task_struct* business_thread_tsk_B;
static struct task_struct* produce_thread_tsk;
/*加载函数*/
static int __init kernel_thread_init( void )
{
	printk( KERN_INFO "/*********---Start Creat Kernel Module!---*********/" );
	//创建内核线程
	kernel_thread_tsk = kthread_run( kernel_thread_main, NULL, " kernel_thread " );

	//创建A业务线程
	business_thread_tsk_A = kthread_run( business_thread_A, NULL, " a_business_thread " );

	//创建B业务线程
	business_thread_tsk_B = kthread_run( business_thread_B, NULL, " b_business_thread " );

	//创建生产者线程
	produce_thread_tsk = kthread_run( kthread_produce_msg, NULL, " produce_thread " );
	return 0;
}

/*卸载函数*/
static void __exit kernel_thread_cleanup( void )
{
	printk( KERN_INFO "/*********Exit Kernel Module!*********/" );
	
	//释放内核线程	
	if( !IS_ERR( kernel_thread_tsk ) )
	{
		int ret = kthread_stop( kernel_thread_tsk );
		printk( KERN_INFO " exit kernel thread return %d\n", ret );
	}
	//释放A业务线程
	if( !IS_ERR( business_thread_tsk_A ) )
	{
		int ret = kthread_stop( business_thread_tsk_A );
		printk( KERN_INFO " exit a business thread return %d\n", ret );
	}
	//释放B业务线程
	if( !IS_ERR( business_thread_tsk_B ) )
	{
		int ret = kthread_stop( business_thread_tsk_B );
		printk( KERN_INFO " exit b busness thread return %d\n", ret );
	}
	//释放生产者线程
	if( !IS_ERR( produce_thread_tsk ) )
	{
		int ret = kthread_stop( produce_thread_tsk );
		printk( KERN_INFO " exit produce thread return %d\n", ret );
	}
}

//加载内核模块
module_init( kernel_thread_init );
//卸载内核模块
module_exit( kernel_thread_cleanup );

thread_func.h:

/***************************************
------文件名称:thread_func.h-----  
***************************************/


#ifndef _THREAD_FUNC_H
#define _THREAD_FUNC_H

#include <linux/kthread.h>   //for kthread_run
#include <linux/init.h>	     //for module_init and module_exit
#include <linux/module.h>    //for kernel module 
#include <linux/wait.h>      //for wait_queue
#include <linux/completion.h>//for completion
#include <linux/slab.h>      //for kmalloc
#include <linux/delay.h>     //for msleep_interruptible

#define MSG_TYPES 3	     // 3种报文类型

//内核线程生产报文的类型
typedef enum 
{
	P = 0,
	A,
	B
}MSG_TYPE;

//业务线程AB读取的报文数量
typedef struct 
{
	int count_a;
	int count_b;
}MESSAGE_COUNT;

//当前是哪个业务线程
typedef enum 
{
	THREAD_A,
	THREAD_B
}THREAD_TYPE;

//获取随机数序列
void get_random_bytes( void *buf, int nbytes );

int kernel_thread_main( void* data );	 //创建的内核线程
int business_thread_A( void* data );
int business_thread_B( void* data );
int kthread_produce_msg( void* data );

extern int read_msg_bthread_count;
extern wait_queue_head_t task_wait_queue;
extern struct completion msg_completion; //completion完成量
extern int* message;

#endif //thread_func.h

thread_func.c:

#include "thread_func.h"

int read_msg_bthread_count;
wait_queue_head_t task_wait_queue;
struct completion msg_completion; //completion完成量
int* message;

//存放已处理的报文数量
static MESSAGE_COUNT message_count = { 0, 0 };
//互斥锁
static DEFINE_MUTEX(p_mutex);

// 函数功能:内核线程生产报文
static int create_message( void )
{
	unsigned long msg;
	// 获取随机数序列
	get_random_bytes( &msg, sizeof( unsigned long ) );
	msg = msg % MSG_TYPES;
	// 申请报文的内存空间
	message = ( int * )kmalloc( sizeof( int ), GFP_KERNEL );
	//判断报文类型
	if ( 0 == msg )
	{
		*message = P;
		printk(KERN_INFO "\nkernel thread creat 'P' message!\n");
	}
	else if ( 1 == msg )
	{
		*message = A;
		printk(KERN_INFO "\nkernel thread creat 'A' message!\n");
	}
	else
	{
		*message = B;
		printk(KERN_INFO "\nkernel thread creat 'B' message!\n");
	}
	return 0;	
}

int kernel_thread_main( void* data )	 //创建的内核线程
{
	int ret;
	//初始化completion完成量
	init_completion(&msg_completion);
	//初始化等待队列
	init_waitqueue_head(&task_wait_queue);
	while( !kthread_should_stop( ) ) 
	{ 
		//内核线程生产报文
		ret = create_message( );
		if ( 0 == ret )
		{
			//唤醒所有等待的线程
			complete_all( &msg_completion );
			//隔1秒产生报文
			msleep_interruptible(1000);
			//反复检查报文是否被两个业务线程都读到了,如果是则继续该线程,否则挂起直至满足条件,满足条件就跳到下一个报文处理
			wait_event_interruptible( task_wait_queue, read_msg_bthread_count==2 );
			read_msg_bthread_count = 0;  	
		}
	}  
	return 0;  
}


//函数功能:不同类型的报文处理
static void bisthread_msg_deal( int thread_type )
{
	//业务线程A操作
	if ( THREAD_A == thread_type )
	{
		if ( P == *message )
		{
			printk( "business thread A has deal %d message!\n", message_count.count_a );
			message_count.count_a = 0;
		}
		else if ( A == *message )
		{
			message_count.count_a++;
		}
	}
	//业务线程B操作
	else
	{
		if ( P == *message )
		{
			printk( "business_thread B has deal %d message!\n", message_count.count_b );
			message_count.count_b = 0;
		}
		else if ( B == *message )
		{
			message_count.count_b++;
		}
	}

	//防止产生同时写入的情况
	mutex_lock( &p_mutex );

	read_msg_bthread_count++;

	//只有当业务线程A与B都对同一个报文进行反应后才激活内核线程
	if( ( 2 == read_msg_bthread_count ) && waitqueue_active( &task_wait_queue ) ) 
	{
		//释放内存
		kfree( message );

		//激活内核线程
		wake_up_interruptible( &task_wait_queue );
	}
	mutex_unlock( &p_mutex );
	//空点时间给其他线程
	msleep_interruptible(500);
}

int business_thread_A( void* data )
{
	//先让内核线程进行报文产生的处理
	msleep_interruptible( 100 );
	while( !kthread_should_stop( ) ) 
	{
	       	//当我们使用的complete_all接口时,如果要重复使用一个
		//completion结构,则必须执行 INIT_COMPLETION
		//(struct completion c)来重新初始化它
		INIT_COMPLETION( msg_completion );
		//timeout防止卸载模块时堵塞在这里等待
		wait_for_completion_timeout( &msg_completion, 4*HZ ); 
		printk( KERN_INFO "business thread A read message!\n" );
		//开始处理报文
		bisthread_msg_deal( THREAD_A );
	} 
	return 0;
}

int business_thread_B( void* data )
{
	msleep_interruptible( 100 );
	while( !kthread_should_stop( ) ) 
	{ 
		INIT_COMPLETION( msg_completion );
		wait_for_completion_timeout( &msg_completion, 4*HZ );		
		printk( KERN_INFO "business thread B read message!\n" );
		//开始处理报文
		bisthread_msg_deal( THREAD_B );
	} 
	return 0;
}

int kthread_produce_msg( void* msg)
{
	printk( KERN_INFO "produce message----------!!!\n" );
	return 0;
}




Linux内核---多线程

标签:

原文地址:http://blog.csdn.net/sty23122555/article/details/51629142

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!