进程数据结构 进程在linux内核中管理,使用了一个统一的结构体 task_struct
内核由一个链表用来存储task_struct
task_struct内容 任务ID 1 2 3 pid_t pid;pid_t tgid;struct task_struct *group_leader ;
任何一个进程,如果只有主线程,那 pid 是自己,tgid 是自己,group_leader 指向的还是自己。但是,如果一个进程创建了其他线程,那就会有所变化了。线程有自己的 pid,tgid 就是进程的主线程的 pid,group_leader 指向的就是进程的主线程。
信号处理 结构体中包含了许多信号处理相关的字段
1 2 3 4 5 6 7 8 9 10 11 struct signal_struct *signal ;struct sighand_struct *sighand ;sigset_t blocked;sigset_t real_blocked;sigset_t saved_sigmask;struct sigpending pending ;unsigned long sas_ss_sp;size_t sas_ss_size;unsigned int sas_ss_flags;
定义了哪些信号被阻塞暂不处理(blocked),哪些信号尚等待处理(pending),哪些信号正在通过信号处理函数进行处理(sighand)。处理的结果可以是忽略,可以是结束进程等等。
信号处理函数默认使用用户态的函数栈,当然也可以开辟新的栈专门用于信号处理,这就是 sas_ss_xxx 这三个变量的作用。
task_struct 里面有一个 struct sigpending pending。如果我们进入 struct signal_struct *signal 去看的话,还有一个 struct sigpending shared_pending。它们一个是本任务的,一个是线程组共享的。
任务状态 状态相关的变量有3个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 volatile long state; int exit_state; unsigned int flags; #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_NEW 2048 #define TASK_STATE_MAX 4096
任务的切换流程
进程调度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int on_rq;int prio;int static_prio;int normal_prio;unsigned int rt_priority;const struct sched_class *sched_class ;struct sched_entity se ;struct sched_rt_entity rt ;struct sched_dl_entity dl ;unsigned int policy;int nr_cpus_allowed;cpumask_t cpus_allowed;struct sched_info sched_info ;
结构体内容概览
运行统计信息 在进程的运行过程中,会有一些统计量,具体你可以看下面的列表。这里面有进程在用户态和内核态消耗的时间、上下文切换的次数等等。
1 2 3 4 5 6 7 u64 utime; u64 stime; unsigned long nvcsw;unsigned long nivcsw;u64 start_time; u64 real_start_time;
进程亲缘关系 1 2 3 4 5 6 struct task_struct __rcu *real_parent ; struct task_struct __rcu *parent ; struct list_head children ; struct list_head sibling ;
保存了父进程、子进程、兄弟进程的信息。
real_parent 和 parent 是一样的,但是也会有另外的情况存在。例如,bash 创建一个进程,那进程的 parent 和 real_parent 就都是 bash。如果在 bash 上使用 GDB 来 debug 一个进程,这个时候 GDB 是 parent,bash 是这个进程的 real_parent。 具体如何实现的, 还需要深入了解代码后才能清楚。
进程权限 1 2 3 4 5 const struct cred __rcu *real_cred ;const struct cred __rcu *cred ;
real_cred 就是说明谁能操作我这个进程,而 cred 就是说明我这个进程能够操作谁。
cred结构保存的基本上就是一些用户或者组的id, 权限控制时,通过比较这些id来确定是否拥有权限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct cred {...... kuid_t uid; kgid_t gid; kuid_t suid; kgid_t sgid; kuid_t euid; kgid_t egid; kuid_t fsuid; kgid_t fsgid; ...... kernel_cap_t cap_inheritable; kernel_cap_t cap_permitted; kernel_cap_t cap_effective; kernel_cap_t cap_bset; kernel_cap_t cap_ambient; ...... } __randomize_layout;
第一个是 uid 和 gid,注释是 real user/group id。一般情况下,谁启动的进程,就是谁的 ID。但是权限审核的时候,往往不比较这两个,也就是说不大起作用。
第二个是 euid 和 egid,注释是 effective user/group id。一看这个名字,就知道这个是起“作用”的。当这个进程要操作消息队列、共享内存、信号量等对象的时候,其实就是在比较这个用户和组是否有权限。
第三个是 fsuid 和 fsgid,也就是 filesystem user/group id。这个是对文件操作会审核的权限。一般说来,fsuid、euid,和 uid 是一样的,fsgid、egid,和 gid 也是一样的。因为谁启动的进程,就应该审核启动的用户到底有没有这个权限。
内存管理 每个进程都有自己独立的虚拟内存空间,这需要有一个数据结构来表示,就是 mm_struct。
1 2 3 struct mm_struct *mm ;struct mm_struct *active_mm ;
文件与文件系统 每个进程有一个文件系统的数据结构,还有一个打开文件的数据结构。
1 2 3 4 5 6 struct fs_struct *fs ;struct files_struct *files ;
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。