linux驱动之内核多线程(一)

本文摘自http://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545624.html

Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。

内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。

创建内核线程最基本的两个接口函数是:

kthread_run(threadfn, data, namefmt, ...)

kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)

这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。

kthread_run 事实上是一个宏定义:

/**

 * kthread_run - create and wake a thread.

 * @threadfn: the function to run until signal_pending(current).

 * @data: data ptr for @threadfn.

 * @namefmt: printf-style name for the thread.

 *

 * Description: Convenient wrapper for kthread_create() followed by

 * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM).

 */

#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中发送一个请求,并唤醒kthreadd,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。

在介绍完如何创建线程之后,下面来介绍另外两个基本的函数:

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。在调用 kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过 kthread_run 返回的 task_stuct 是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,这是因为返回的是错误码,大致从0xfffff000~0xffffffff。

kthread_should_stop()返回should_stop标志(参见 struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。

kthread() (注:原型为:static int kthread(void *_create) )的实现在kernel/kthread.c中,头文件是include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()中,不断检查一个kthread_create_list链表。kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链表,并调用create_kthread()创建相应的线程。create_kthread()则进一步调用更深层的kernel_thread()创建线程,入口函数设在kthread()中。

外界调用kthread_stop()删除线程。kthread_stop首先设置结束标志should_stop,然后调用wake_for_completion(&kthread->exited)上,这个其实是新线程task_struct上的vfork_done,会在线程结束调用do_exit()时设置。

struct kthread {

       int should_stop;

       struct completion exited;

};

int kthreadd(void *unused)
{

       struct task_struct *tsk = current;

       /* Setup a clean context for our children to inherit. */

       set_task_comm(tsk, "kthreadd");

       ignore_signals(tsk);

       set_cpus_allowed_ptr(tsk, cpu_all_mask);

       set_mems_allowed(node_states[N_HIGH_MEMORY]);

       current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;

       for (;;) {

              set_current_state(TASK_INTERRUPTIBLE);

              if (list_empty(&kthread_create_list))

                     schedule();

              __set_current_state(TASK_RUNNING);

              spin_lock(&kthread_create_lock);

              while (!list_empty(&kthread_create_list)) {

                     struct kthread_create_info *create;

                     create = list_entry(kthread_create_list.next,

                                       struct kthread_create_info, list);

                     list_del_init(&create->list);

                     spin_unlock(&kthread_create_lock);

                     create_kthread(create);

                     spin_lock(&kthread_create_lock);

              }

              spin_unlock(&kthread_create_lock);

       }

       return 0;
}

/**

 * kthread_stop - stop a thread created by kthread_create().

 * @k: thread created by kthread_create().

 *

 * Sets kthread_should_stop() for @k to return true, wakes it, and

 * waits for it to exit. This can also be called after kthread_create()

 * instead of calling wake_up_process(): the thread will exit without

 * calling threadfn().

 *

 * If threadfn() may call do_exit() itself, the caller must ensure

 * task_struct can‘t go away.

 *

 * Returns the result of threadfn(), or %-EINTR if wake_up_process()

 * was never called.

 */

int kthread_stop(struct task_struct *k)
{

       struct kthread *kthread;

       int ret;

       trace_sched_kthread_stop(k);

       get_task_struct(k);

       kthread = to_kthread(k);

       barrier(); /* it might have exited */

       if (k->vfork_done != NULL) {

              kthread->should_stop = 1;

              wake_up_process(k);

              wait_for_completion(&kthread->exited);

       }

       ret = k->exit_code;

       put_task_struct(k);

       trace_sched_kthread_stop_ret(ret);

       return ret;

}
时间: 2024-10-20 03:03:25

linux驱动之内核多线程(一)的相关文章

linux驱动之内核多线程(二)

本文摘自http://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545702.html 内核多线程是在项目中使用到,自己也不熟悉,遇到一个很囧的问题,导致cpu运行100%. 这是写的第一个内核线程程序,通过全局变量来实现两个内核线程之间的通信.但是这里遇到致命错误,就是:每当 wait_event_interruptible()被wake_up_interruptible 唤醒之后线程就进入死循环.后面发现是线程不会主动的自己调度,需要显式的通

linux驱动之内核多线程(三)

本文摘自 http://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548458.html 接上 一篇文章 ,这里介绍另一种线程间通信的方式:completion机制.Completion机制是线程间通信的一种轻量级机制:允许一个线程告诉另一个线程工作已经完成.为使用 completion, 需要包含头文件 <linux/completion.h>. 可以通过以下方式来创建一个 completion : DECLARE_COMPLETION(my

Linux 驱动之内核定时器

1.定时器 之前说过两类跟时间相关的内核结构. 1.延时:通过忙等待或者睡眠机制实现延时. 2.tasklet和工作队列,通过某种机制使工作推后执行,但不知道执行的具体时间. 接下来要介绍的定时器,能够使工作在指定的时间点上执行,而且不需要使用忙等待这类的延时方法.通过定义一个定时器,告之内核在哪个时间需要执行什么函数就可以了,等时间一到,内核会就执行指定的函数. 2.使用定时器 定时器的使用很简单,只需要三部: 1.定义定时器结构体timer_list. 2.设置超时时间,定义定时器处理函数和

Linux 驱动开发索引

1.嵌入开发环境搭建 Telnet 在 mini2440 上的移植 Opencv-2.4.9 在 mini2440 上的移植 搭建嵌入式开发环境总结 2.Linux 设备驱动 Linux 驱动程序头文件 一步一步学习Linux驱动之驱动模块MakeFile解析 一步一步学习 Linux 驱动之(Kconfig.Makefile) 一步一步学习 Linux 驱动之字符设备 LED 静态编译进 Linux 内核 内核怎么通过主设备号找驱动.次设备号找设备 Linux 驱动之内核空间分配内存 一步一步

disk磁盘管理与Linux驱动编写

磁盘管理 一.关于硬盘接口 安装linux red hat系统,到分区时发现硬盘驱动器设备 /dev/sda             #sata接口设备名 /dev/sda1 #sda对应的物理分区 /dev/sda2 /dev/sda3 而又的安装时硬盘驱动设备名为 /dev/hda #IDE接口设备目录 /dev/hda1 sda和hda有什么区别那? HDA是使用了ide接口的硬盘的名称.SDA是sata接口的硬盘的名称.在最新的2.6.19内核里,所有的硬盘都叫SDA了. GERUB里填

Linux内核---多线程

A.     线程和进程的差别 在现代操作系统中,进程支持多线程.进程是资源管理及分配的最小单元:而线程是程序执行的最小单元.一个进程的组成实体可以分为两大部分:线程集和资源集.进程中的线程是动态的对象,代表了进程指令的执行过程.资源,包括地址空间.打开的文件.用户信息等等,由进程内的线程共享.线程有自己的私有数据:程序计数器,栈空间以及寄存器. 现实中有很多需要并发处理的任务,如数据库的服务器端.网络服务器.大容量计算等.传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发,

Linux驱动 - 多线程

1.内核多线程相关内容 1.1 头文件#include <linux/kthread.h> 1.2 定义/初始化变量 struct mutex SPI_work; /*定义互斥体*/ mutex_init(&SPI_work); /*初始化*/ mutex_lock(&SPI_work); func(); mutex_unlock(&SPI_work);

在linux内核文件中添加自己的驱动,添加自己的linux驱动,编译自己的linux驱动程序方法和例子图文

此文章为done原创,转载请写明出处,尊重原创. 写这个文章,我参考了网上的一些博客: http://bbs.chinaunix.net/thread-3634524-1-1.html http://www.bkjia.com/gjrj/800182.html 也参考了参考了 <linux驱动开发详解>3.4章节,实现建立自己驱动目录. 文章中的驱动范例是:linux设备驱动开发详解第二版 的 global mem tow,宋宝华主编的书本光盘源码 在我们学习了linux的驱动之后,我们都想编

Linux驱动学习之Linux-2.6.20.4内核移植

最近一段时间一直在学习向TQ2440开发板移植内核.移植驱动.真心觉得这方面的知识有很大的难度.但是从另一角度去看,难度越大,能力提升的空间就越大!! 1.解压源码 从网上下载一个Linux 内核,我是用的是Linux-2.6.20.4.然后用命令解压.建议解压到"/home/用户名"目录下.我的内核源码存放在: 2.添加对ARM的支持 因为所用的是TQ2440开发板,属于ARM9.因此要在系统中添加对ARM的支持. 方法:进入内核源码目录, 修改"Makefile"