1,进程的基本概念
简单来说进程就是执行期的程序,但是同时进程也不至于此,它还包括了其它资源(如打开的文件,挂起的信号等等)
Linux是一个多任务多用户操作系统,一个任务(task)就是一个进程(process),即进程=任务,在linux系统中,进程和线程共享一系列的资源(地址空间,文件,信号,名字空间等)
简单来说进程就是执行期的程序,但是同时进程也不至于此,它还包括了其它资源(如打开的文件,挂起的信号等等)
Linux是一个多任务多用户操作系统,一个任务(task)就是一个进程(process),即进程=任务,在linux系统中,进程和线程共享一系列的资源(地址空间,文件,信号,名字空间等)
进程的task_struct结构体中tgid代表的是进程id, pid代表的是线程id或者进程id
2,进程的标识;
每个进程都有一个独一无二的结构体即struct task_struct,一个进程相关的所有信息都可以在该结构体里标识,每一个进程的task_struct又组成了一个任务队列,用双向链表表示:另外每一个进程还对应一个独一无二的数字,即PID,PID最大值为32767(32768=8*4096,32位系统一个页是4096字节,每个字节为8bit),后一个进程创建的PID是前一个进程PID加1,当PID到达最大值后又从最小值开始,linux用pidmap_array位图来知道哪些pid被使用,一个进程中所有线程使用的PID是相同的,但是有各自不同的线程ID,线程ID只在该进程环境中有效,进程的PID在整个系统是唯一的,
struct task_struct
{
//......
pid_t pid;
}
task_struct结果体是如何分配的?slab分配器?
3,内核如何访问当前的进程:
#define current get_current()
static inline struct task_struct *get_current(vold)
{
return current_thread_info()->task;
}
关于struct thread_info和内核堆栈可以参考另外一篇文章:
对每个进程来说,linux把两个不同的数据结构放在内核的一个动态存储区,大小为8k,一个是struct thread_info,一个是内核堆栈,在arm处理器上,内核通过sp寄存器指针获得当前cpu正在运行进程的thread_info结构地址,在Thread_info.h文件.
如何查看thread_info结构体的信息
#define task_thread_info(task) ((struct thread_info *)(task)->stack)
V.V (struct thread_info*)(0xE9A1C000 & ~(8192-1))
#define task_thread_info(task) ((struct thread_info *)(task)->stack)
V.V (struct thread_info*)(0xE9A1C000 & ~(8192-1))
4,进程的状态:
就绪态,阻塞态,运行态
内核将进程的状态保存在struct task_struct的state字段中:
内核将进程的状态保存在struct task_struct的state字段中:
struct task_struct
{
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
//......
}
进程的僵尸状态:
进程的僵尸状态:表示进程结束但尚未消亡的一种状态。此时进程已经结束运行并释放掉大部分资源(放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度),但尚未释放task_struct(只剩下它了,所以称为僵尸),记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。
子进程在退出的过程中,内核会给其父进程发送一个信号(默认是SIGCHLD,但是在通过clone系统调用创建子进程时,可以设置这个信号),通知父进程来"收尸"。父进程可以通过wait系列的系统调用(如wait4、waitid)来等待某个或某些子进程的退出,并获取它的退出信息。然后wait系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉 如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,子进程的尸体(task_struct)也就无法释放掉
如何处理僵尸进程:找出父进程号,然后杀死父进程,在Android下,子进程一起被杀死
内核中设置某个进程状态的函数: set_task_sate(task,state);
如何处理僵尸进程:找出父进程号,然后杀死父进程,在Android下,子进程一起被杀死
内核中设置某个进程状态的函数: set_task_sate(task,state);
5,进程的优先级:
在task_struct中有四个变量和进程的优先级相关:
sched.h
struct task_struct
{
//......
int prio, static_prio, normal_prio;
unsigned int rt_priority;
//......
}
① prio指的是任务当前的动态优先级,其值影响任务的调度顺序。
② normal_prio指的是任务的常规优先级,该值基于static_prio和调度策略计算。
③ static_prio指的是任务的静态优先级,在进程创建时分配,该值会影响分配给任务的时间片的长短和非实时任务动态优先级的计算。范围是120+nice值,nice值范围是-20到19
④ rt_priority指的是任务的实时优先级。若为0表示是非实时任务,[1, 99]表示实时任务,值越大,优先级越高。
对于非实时任务,prio = normal_prio = static_prio
② normal_prio指的是任务的常规优先级,该值基于static_prio和调度策略计算。
③ static_prio指的是任务的静态优先级,在进程创建时分配,该值会影响分配给任务的时间片的长短和非实时任务动态优先级的计算。范围是120+nice值,nice值范围是-20到19
④ rt_priority指的是任务的实时优先级。若为0表示是非实时任务,[1, 99]表示实时任务,值越大,优先级越高。
对于非实时任务,prio = normal_prio = static_prio
实时进程:prio = normal_prio = MAX_RT_PRIO – 1 – rt_priority
子进程的静态优先级继承父进程的静态优先级,子进程的动态优先级继承自父进程的普通优先级~
6,各个进程关系
linux系统进程存在一个继承关系,系统中每个进程都有父进程,init(PID为1,由idle进程(PID为0创建))进程是所有用户进程的祖先,而kthreadd是所有内核进程的祖先进程关系除了父子兄度,可能还存在其它关系,如进程组,登录会话等等。进程如何从PID导出task_struct结构体:内核主要根据pid tgid pgrp session四个不同含义用到了四个hash表。
{
PIDTYPE_PID, // 进程的PID
PIDTYPE_TGID, // 线程组领头进程的PID
PIDTYPE_PGID, // 进程组领头进程的PID
PIDTYPE_SID, // 会话领头进程的PID
PIDTYPE_MAX // 类型个数
};