博文

目前显示的是标签为“kthread”的博文

内核线程之User-Mode Helpers

这次学习下如何在Linux内核态执行用户态程序,这就要用到User-Mode Helpers,为什么要这么“逆操作”呢?有些与平常用户态系统调用内核态反着来,其实在U盘热插拔时,就需要用到该功能了,当U盘插入时,驱动识别到U盘设备,最终需要调用用户态的程序和设定好的规则来将其挂载起来,还有其他的应用场景也需要这样的操作,自己好好探索下吧。 接下来说说关于User-Mode Helpers,下面是相关的函数(在kernel/kmod.c文件中有定义,下面只给出函数头和函数体较少的代码): 1.call_usermodehelper_setup struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask, int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *info), void *data) ; 2.call_usermodehelper_exec int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) ; 3.call_usermodehelper int call_usermodehelper(char *path, char **argv, char **envp, int wait) { struct subprocess_info *info; gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; info = call_usermodehelper_setup(path, argv, envp, gfp_mask, NULL, NULL, NULL); if (info == NULL) re...

内核线程同步之signal

对于内核线程,尤其是在同一模块里有多个时,我们想终止其中一个,而又不想卸载模块,此时signal就帮上我们一个大忙了,在设计时让线程接收信号并处理,想停止时发信号给它,而在内核里需要用到如下信号相关函数: 1.signal_pending 在Linux内核源码include/linux/sched.h文件里,有如下函数定义: static inline int signal_pending(struct task_struct *p) { return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING)); } 该函数用于判断指定线程有没有接收到信号,即信号有没有被阻塞。 上面的参数我们一般是判断当前线程,故而会乃至include/asm-generic/current.h里的current宏,定义如下: #define current get_current() 2.allow_signal 在Linux内核源码kernel/exit.c有该函数的实现,而在include/linux/sched.h文件里有如下声明: extern int allow_signal(int); 其参数是SIGKILL等宏,在include/uapi/asm-generic/signal.h文件中有相应的宏定义。 内核线程允许接收信号后,还需要确保该线程是可打断的,需要用到如下宏: 1.set_current_state 在include/linux/sched.h文件中有如下宏定义: #define set_current_state(state_value) \ set_mb(current->state, (state_value)) 2.TASK_INTERRUPTIBLE 在同一头文件中如下宏定义: #define TASK_INTERRUPTIBLE 1 上面两个宏结合使用用于配置当前线程的状态是可被中断的。 接下来上代码吧: #include <linux/module.h> #include <linux/kthread.h> #include <linux/delay.h> static struct task_...

内核线程同步之信号量、互斥量、自旋锁、原子量

本次主要是学习线程同步时保护数据的锁机制,对于多个线程同时对一个变量进行赋值操作时,会造成该变量的赋值不确定,出现了竞争状态,这时需要有个锁来保护下,即加个锁,同一时间只能有一个线程对其操作,当锁释放了,另一线程才能对其操作,而处在加锁和解锁之间的区域叫做临界区,对临界区的操作要保证其原子性,即一次性搞定,不能被中断,而内核也提供了原子量,用于轻量级操作。 接下来说说内核里相关的内容吧: 1.原子量 1).结构体 在include/linux/types.h文件中有如下结构体(分别对应32位和64位系统): typedef struct { int counter; } atomic_t; #ifdef CONFIG_64BIT typedef struct { long counter; } atomic64_t; #endif 2).整型原子操作 在include/asm-generic/atomic.h文件中有如下操作宏和函数定义: a.创建及初始化 #define ATOMIC_INIT(i) { (i) } #define atomic_set(v, i) (((v)->counter) = (i)) b.读取原子量 #define atomic_read(v) (*(volatile int *)&(v)->counter) c.原子量常用运算(加、减、自加一、自减一) static inline void atomic_add(int i, atomic_t *v) ; static inline void atomic_sub(int i, atomic_t *v) ; static inline void atomic_inc(atomic_t *v) ; static inline void atomic_dec(atomic_t *v) ; d.原子量运算并检测运算后是否等于0 #define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0) #define atomic_dec_and_test(v) (atomic_dec_return(v) == 0) #define atomic_i...

内核线程同步之wait_queue

在《内核线程同步之completion》一文中说到completion完成量也是基于wait_queue等待队列机制实现(这些机制不仅仅用于内核线程的同步,也可用于其他相关场景,这里用线程演示是其比较方便感受到效果),那么接下来就来了解下这一机制的相关内容,其在Linux Kernel源码include/linux/wait.h文件中有如下内容: typedef struct __wait_queue wait_queue_t; struct __wait_queue { unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 void *private; wait_queue_func_t func; struct list_head task_list; }; struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t; 上面包含了等待队列和等待队列头部类型的结构体声明。其对应有如下静态初始化宏: #define DECLARE_WAITQUEUE(name, tsk) \ wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk) #define DECLARE_WAIT_QUEUE_HEAD(name) \ wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name) 还有如下动态初始化宏和函数: #define init_waitqueue_head(q) \ do { \ static struct lock_class_key __key; \ \ __init_waitqueue_head((q), #q, &__key); \ ...

内核线程同步之completion

Completion,完成量,用于多线程间同步,即线程A要往下执行需要等待线程B执行到指定代码后才继续执行,这时就可以使用该机制,用于一个线程告诉另一个线程指定工作已完成。 在Linux Kernel源码include/linux/completion.h文件里有相关的结构体和接口函数的定义和声明,从结构体可以看到其是基于等待队列机制实现的,该机制后期再了解,下面是completion相关内容: struct completion { unsigned int done; wait_queue_head_t wait; }; static inline void init_completion(struct completion *x) ; static inline void reinit_completion(struct completion *x); extern void wait_for_completion(struct completion *); extern int wait_for_completion_interruptible(struct completion *x); extern int wait_for_completion_killable(struct completion *x); extern unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout); extern long wait_for_completion_interruptible_timeout( struct completion *x, unsigned long timeout); extern long wait_for_completion_killable_timeout( struct completion *x, unsigned long timeout); extern bool try_wait_for_completion(struct completion *x); extern bool completion_done(struct c...

内核线程

有时候我们需要有个程序来监听和处理某些指定事件,此时经常会做个服务,让其在后台执行,这在应用程序这样的用户态很经常用到,而在Linux Kernel里也会有类似的经历,此时同样可以使用线程来实现,不过它叫内核线程,只能在内核中由其他的线程来创建,而所有的内核线程由kthreadd创建,故而使用ps -ef命令看到所有被[]括起来的内核线程(守护进程)对应的PPID均为2。内核线程与普通用户态线程除了内核线程没有独立地址空间(其mm成员指向NULL)外,其他的可被调度和被抢占均支持。 我们还是以实践为主,下面是这次会使用到跟内核线程相关的API函数和宏,在include/linux/kthread.h文件中均有定义: 1.kthread_create(),创建内核线程 #define kthread_create(threadfn, data, namefmt, arg...) \ kthread_create_on_node(threadfn, data, -1, namefmt, ##arg) 2.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_create()的区别了吧,多了个wake_up_process()操作。 3.kthread_stop(),停止指定内核线程 int kthread_stop(struct task_struct *k); 4.kthread_should_stop(),线程该停止吗? bool kthread_should_stop(void); 5.kthread_bind(),绑定创建好的线程在执行CPU核心上运行 void kthread_bind(struct ...