您的位置:首页 > 运维架构 > Linux

Linux 进程概述

2013-01-10 11:13 281 查看
浅谈Linux进程

作者:嵌入式学院武汉华嵌中心技术支持涂佩君



内容清单:

1.
在Linux内核内进程表示

2.
在Linux内进程管理

3.
在Linux内核创建一个进程

引言

Linux的用户空间进程的创建和管理所涉及的原理与UNIX有很多共同点,但也有一些特定于Linux的独特之处。在本文中,了解Linux进程的生命周期,探索用户进程创建、内存管理内幕。
Linux是一种动态系统,能够适应不断变化的计算需求。Linux计算需求的表现是以进程的通用抽象为中心的。进程可以是短期的(从命令行执行的一个命令),也可以是长期的(一种网络服务)。因此,对进程及其调度进行一般管理就显得极为重要。
在用户空间,进程是由进程标识符(PID)表示的。从用户的角度来看,一个PID是一个数字值,可惟一标识一个进程。一个PID在进程的整个生命期间不会更改,但PID可以在进程销毁后被重新使用,所以对它们进行缓存并不见得总是理想的。
在用户空间,创建进程可以采用几种方式。可以执行一个程序(这会导致新进程的创建),也可以在程序内,调用一个fork或exec系统调用。fork调用会导致创建一个子进程,而exec调用则会用新程序代替当前进程上下文。接下来,我将对这几种方法进行讨论以便您能很好地理解它们的工作原理。
具体介绍:

进程表示

在Linux
内核内,进程是由相当大的一个称为task_struct的结构表示的。此结构包含所有表示此进程所必需的数据,此外,还包含了大量的其他数据用来统计(accounting)和维护与其他进程的关系(父和子)。清单1给出了task_struct的一小部分。task_struct
于./linux/include/linux/sched.h。

清单1.task_struct的一小部分
structtask_struct{
volatilelongstate;//state变量是一些表明任务状态的比特位

void*stack;
unsignedintflags;//flags定义了很多指示符,表明进程是否正在被(PF_STARTING)//或退出(PF_EXITING),或是进程当前是否在分配内存(PF_MEMALLOC)
intprio,static_prio;//static_prio优先级

structlist_headtasks;

structmm_struct*mm,*active_mm;//mm代表的是进程的内存描述符,//active_mm则是前一个进程的内存描述符(为改进上下文切换时间的一种优化)。

pid_tpid;
pid_ttgid;

structtask_struct*real_parent;

charcomm[TASK_COMM_LEN];//可执行程序的名称

structthread_structthread;//标识进程的存储状态

structfiles_struct*files;

...
};

进程管理

现在,让我们来看看如何在Linux
内管理进程。在很多情况下,进程都是动态创建并由一个动态分配的task_struct表示。一个例外是init进程本身,它总是存在并由一个静态分配的task_struct表示。在./linux/arch/i386/kernel/init_task.c
内可以找到这样的一个例子。Linux内所有进程的分配有两种方式。第一种方式是通过一个哈希表,由PID值进行哈希计算得到;第二种方式是通过双链循环表。循环表非常适合于对任务列表进行迭代。由于列表是循环的,没有头或尾;但是由于init_task总是存在,所以可以将其用作继续向前迭代的一个锚点。让我们来看一个遍历当前任务集的例子。

清单2.发出任务信息的简单内核模块(procsview.c)

#include<linux/kernel.h>

#include<linux/module.h>

#include<linux/sched.h>


staticint__initinit_module(void)

{

structtask_struct*task=&init_task;


do{

printk(KERN_INFO"***%s[%d]parent%s\n",

task->comm,task->pid,task->parent->comm);

}while((task=next_task(task))!=&init_task);


return0;


}


Staticvoid__exitcleanup_module(void)

{

return;

}


module_init(init_module);

module_exit(cleanup_module);

MODULE_LICENSE("GPL");


注意,还可以标识当前正在运行的任务。Linux维护一个称为current的符号,代表的是当前运行的进程(类型是task_struct)。

staticinlinestructtask_struct*get_current(void)
{
structtask_struct*current;
__asm__("andl%%esp,%0;":"=r"(current):"0"(~8191UL));
returncurrent;
}
#definecurrentget_current()

附:最大进程数
在Linux内虽然进程都是动态分配的,但还是需要考虑最大进程数。在内核内最大进程数是由一个称为max_threads的符号表示的,它可以在./linux/kernel/fork.c内找到。可以通过/proc/sys/kernel/threads-max的proc文件系统从用户空间更改此值。

进程创建

如何从用户空间创建一个进程。用户空间任务和内核任务的底层机制是一致的,因为二者最终都会依赖于一个名为do_fork的函数来创建新进程。在创建内核线程时,内核会调用一个名为kernel_thread的函数(参见./linux/arch/i386/kernel/process.c),此函数执行某些初始化后会调用
do_fork。

创建用户空间进程的情况与此类似。在用户空间,一个程序会调用fork,这会导致对名为sys_fork的内核函数的系统调用(参见./linux/arch/i386/kernel/process.c)。

您可能已经看到过系统调用的模式了。在很多情况下,系统调用都被命名为sys_*并提供某些初始功能以实现调用(例如错误检查或用户空间的行为)。实际的工作常常会委派给另外一个名为do_*的函数。

kernel_thread函数的作用是产生一个新的线程,内核线程实际上就是一个共享父进程地址空间的进程,它有自己的系统堆栈.

假设我们在structdev中定义了structtask_struct*thread;

在初始化函数创建并启动该线程;

thread=kthread_run(kthread_fun,dev,"%s",dev->name);

在注销函数中:

kthread_stop(dev->thread);//停止内核线程

附:

创建并启动线程的函数:

structtask_struct*kthread_run(int(*threadfn)(void*data),void*data,

constchar*namefmt,...);


线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。

intkthread_stop(structtask_struct*thread);//kthread_stop()通过发送信号给线程。

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

structtask_struct*kthread_create(int(*threadfn)(void*data),void*data,constchar*namefmt,...);


#definekthread_run(threadfn,data,namefmt,...)\

({\

structtask_struct*__k\

=kthread_create(threadfn,data,namefmt,##__VA_ARGS__);\

if(!IS_ERR(__k))\

wake_up_process(__k);\

__k;\

})
可见这里的函数kthread_create()只是创建了内核线程,而最后启动是怎么启动的呢,我们看到了后面的wake_up_process()函数,没错就是这个函数启动了这个线程,让它在一开始就一直运行下去。知道遇见kthread_should_stop函数或者kthread_stop()函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: