Linux(内核剖析):06---进程之线程的实现

一、线程在Linux中的实现

  • 线程机制是现代编程技术中常用的一种抽象概念。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。线程机制支持并发程序设计技术(concurrent programming),在多处理器系统上,它也能保证真正的并行处理(parallelism)
  • Linux实现线程的机制非常独特
    • 从内核的角度来说,它并没有线程这个概念。Linux把所有的线程都当做进程来实现
    • 内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程
    • 每个线程都拥有唯一隶属于自己的task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共享某些资源,如地址空间)

与其他操作系统实现线程的区别

  • 上述线程机制的实现与Microsoft Windows或是Sun Solaris等操作系统的实现差异非常大。这些系统都在内核中提供了专门支持线程的机制(这些系统常常把线程称作轻量级进程 (lightweight processes))
  • “轻量级进程”这种叫法本身就概括了Linux在此处与其他系统的差异。在其他的系统中,相较于重量级的进程,线程被抽象成一种耗费较少资源,运行迅速的执行单元。而对Linu来说,它只是一种进程间共享资源的手段(Linux的进程本身就够轻量级了
  • 举个例子来说,假如我们有一个包含四个线程的进程,在提供专门线程支持的系统中,通常会有一个包含指向四个不同线程的指针的进程描述符。该描述符负责描述像地址空间、打开的文件这样的共享资源。线程本身再去描述它独占的资源。相反,Linux仅仅创建四个进程并分配四个普通的task_sturct结构。建立这四个进程时指定他们共享某些资源,这是相当高雅的做法
  • 总结:
    • 1.Linux内核中没有线程的概念,线程在内核中也是以线程实现的
    • 2.Linux的线程相当于是一种轻量级的进程

二、线程的创建

clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
  • 传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共亨的资源种类。 下标列举这些clone()用到的参数标志以及它们的作用,这些是在<linux/sched.h>中定义的:

三、内核线程

内核线程概述

  • 内核经常需要在后台执行一些操作。这种任务可以通过内核线程(kernel thread)完成——独立运行在内核空间的标准进程。
  • 内核线程和普通的进程间的区别在于:
    • 内核线程没有独立的地址空间(实际上指向地址空间的mm指针被设置为NULL)。它们只在内核空间运行,从来不切换到用户空间去
    • 内核进程和普通进程一样,可以被调度,也可以被抢占
  • Linux确实会把一些任务交给内核线程去做,像flush和ksofirqd这些任务就是明显的例子。 在装有Linux系统的机子上运行ps -ef命令,你可以看到内核线程,有很多!

内核线程的创建

  • 这些线程在系统启动时由另外一些内核线程创建。实际上,内核线程也只能由其他内核线程创建。内核是通过从kthreadd内核进程中衍生出所有新的内核线程来自动处理这一点的
  • 在<linux/kthread.h>中申明有许多关于内核线程的接口
  • kthread_create函数:从现有内核线程中创建一个新的内核线程,参数如下
    • threadfn参数:新的任务是由kthread内核进程通过clone()系统调用而创建的。新的进程将运行threadfn函数
    • data:传递给threadfn函数的参数
    • namefmt:进程会被命名为namefmt。namefmt接受可变参数列表类似于printf()的格式化参数
//Linux 2.6.22/linux/kthread.h
/**
 * kthread_create - create a kthread.
 * @threadfn: the function to run until signal_pending(current).
 * @data: data ptr for @threadfn.
 * @namefmt: printf-style name for the thread.
 *
 * Description: This helper function creates and names a kernel
 * thread.  The thread will be stopped: use wake_up_process() to start
 * it.  See also kthread_run(), kthread_create_on_cpu().
 *
 * When woken, the thread will run @threadfn() with @data as its
 * argument. @threadfn() can either call do_exit() directly if it is a
 * standalone thread for which noone will call kthread_stop(), or
 * return when 'kthread_should_stop()' is true (which means
 * kthread_stop() has been called).  The return value should be zero
 * or a negative error number; it will be passed to kthread_stop().
 *
 * Returns a task_struct or ERR_PTR(-ENOMEM).
 */
struct task_struct *kthread_create(int (*threadfn)(void *data),
				   void *data,
				   const char namefmt[],
				   ...)
{
	struct kthread_create_info create;

	create.threadfn = threadfn;
	create.data = data;
	init_completion(&create.started);
	init_completion(&create.done);

	spin_lock(&kthread_create_lock);
	list_add_tail(&create.list, &kthread_create_list);
	wake_up_process(kthreadd_task);
	spin_unlock(&kthread_create_lock);

	wait_for_completion(&create.done);

	if (!IS_ERR(create.result)) {
		va_list args;
		va_start(args, namefmt);
		vsnprintf(create.result->comm, sizeof(create.result->comm),
			  namefmt, args);
		va_end(args);
	}
	return create.result;
}
  • kthread_run宏:新创建的进程处于不可运行状态,如果不通过调用wake_up_process()明确地唤醒它,它不会主动运行。创建一个进程并让它运行起来,可以通过调用kthread_run来达到:
struct task_struct *kthread_run(int (*threadfn)(void *data),
				   void *data,
				   const char namefmt[],
				   ...)

//这个例程是以宏实现的,只是简单地调用了kthread_create()和wake_up_process():
#define kthread_run(threadfn, data, namefmt,...)                 \
({                                                               \
    struct task_struct *k;                                      \
                                                                 \
    k = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    if(!IS_ERR(k))                                              \
        wake_up_process(k);                                      \
    k;                                                           \
}) 
  • 内核线程启动后就一直运行直到调用do_exit()退出,或者内核的其他部分调用kthread_stop()退出,传递给kthread_stop()的参数为kthread_create()的参数返回的task_truct结构的地址:
//Linux 2.6.22/linux/kthread.h
/**
 * 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.  Your threadfn() must not call do_exit()
 * itself if you use this function!  This can also be called after
 * kthread_create() instead of calling wake_up_process(): the thread
 * will exit without calling threadfn().
 *
 * Returns the result of threadfn(), or %-EINTR if wake_up_process()
 * was never called.
 */
int kthread_stop(struct task_struct *k)
{
	int ret;

	mutex_lock(&kthread_stop_lock);

	/* It could exit after stop_info.k set, but before wake_up_process. */
	get_task_struct(k);

	/* Must init completion *before* thread sees kthread_stop_info.k */
	init_completion(&kthread_stop_info.done);
	smp_wmb();

	/* Now set kthread_should_stop() to true, and wake it up. */
	kthread_stop_info.k = k;
	wake_up_process(k);
	put_task_struct(k);

	/* Once it dies, reset stop ptr, gather result and we're done. */
	wait_for_completion(&kthread_stop_info.done);
	kthread_stop_info.k = NULL;
	ret = kthread_stop_info.err;
	mutex_unlock(&kthread_stop_lock);

	return ret;
}

四、附加

发布了1300 篇原创文章 · 获赞 827 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/103743253