kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

内核线程使用

http://blog.csdn.net/newnewman80/article/details/7050090

kthread_create:创建线程。
1
struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,const char *namefmt, ...);

线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。

kthread_run :创建并启动线程的函数:
1
struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt, ...);
kthread_stop:通过发送信号给线程,使之退出。
1
int kthread_stop(struct task_struct *thread);

线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。
但如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。

1. 头文件

1
2
3
#include <linux/sched.h>       //wake_up_process()
#include <linux/kthread.h>      //kthread_create()、kthread_run()   
#include <err.h>                //IS_ERR()、PTR_ERR()  

2. 实现

2.1创建线程

kernel thread可以用kernel_thread创建,但是在执行函数里面必须用daemonize释放资源并挂到init下,还需要用completion等待这一过程的完成。为了简化操作kthread_create闪亮登场。 在模块初始化时,可以进行线程的创建。使用下面的函数和宏定义:

1
2
3
struct task_struct *kthread_create(int (*threadfn)(void *data),     
					void *data,  
					const char namefmt[], ...);  
1
2
3
4
5
6
7
8
#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;                                                               \
})  

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static struct task_struct *test_task;  
static int test_init_module(void)  
{  
	int err;  
	test_task = kthread_create(test_thread, NULL, "test_task");  
	if (IS_ERR(test_task)) {  
		printk("Unable to start kernel thread./n");  
		err = PTR_ERR(test_task);  
		test_task = NULL;  
		return err;  
	}  
	wake_up_process(test_task);  
	return 0;  
}  
module_init(test_init_module);  
2.2线程函数

在线程函数里,完成所需的业务逻辑工作。主要框架如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int threadfunc(void *data) {
	...        
	while(1) {
		set_current_state(TASK_UNINTERRUPTIBLE);
		if (kthread_should_stop()) break;
		if () { //条件为真
			//进行业务处理
		} else { //条件为假
			//让出CPU运行其他线程,并在指定的时间内重新被调度
			schedule_timeout(HZ);
		}
	}
	...
	return 0;
}
2.3结束线程

在模块卸载时,可以结束线程的运行。使用下面的函数:

1
int kthread_stop(struct task_struct *k);

例如:

1
2
3
4
5
6
7
8
static void test_cleanup_module(void)  
{  
	if (test_task) {  
		kthread_stop(test_task);  
		test_task = NULL;  
	}  
}  
module_exit(test_cleanup_module);  

设置普通线程优先级

1
2
void set_user_nice(struct task_struct *p, long nice);
// -20 <= nice < 20

将线程设置为实时线程并设置优先级

1
2
3
4
int sched_setscheduler(struct task_struct *p, int policy, struct sched_param *param);
struct sched_param {
	int sched_priority; // 实时线程对应区间[1, 99]
};

CFS 调度模块(在 kernel/sched_fair.c 中实现)用于以下调度策略:SCHED_NORMAL、SCHED_BATCH 和 SCHED_IDLE。
对于 SCHED_RR 和 SCHED_FIFO 策略,将使用实时调度模块(该模块在 kernel/sched_rt.c 中实现)。

top中NI, PR

NI,nice,动态修正CPU调度。范围(-20~19)。越大,cpu调度越一般,越小,cpu调度越偏向它。一般用于后台进程,调整也是往大了调,用来给前台进程让出CPU资源。命令行下可以用renice设置。

PR:优先级,会有两种格式,一种是数字(默认20),一种是RT字符串。

PR默认是20,越小,优先级越高。修改nice可以同时修改PR,测试过程:先开一个窗口,运行wc,另开一个窗口运行top,按N按照PID倒序排,按r输入要renice的PID,然后输入-19~20之间的值,可以看到NI变成输入的值,PR=PR+NI。修改NI得到PR的范围是0~39。优先级由高到低

RT是real-time。只能用chrt -p (1~99) pid来修改。chrt -p 1 1234会将1234的PR改成-2,chrt -p 98 1234变成-99。chrt -p 99 1234会变成RT……只要chrt过,修改nice后PR不会再更改。修改chrt得到的PR范围是RT~-2。优先级由高到低