2024年【操作系统】进程数据结构_进程地址空间内核数据结构是在哪里(1),细节爆炸

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

任务 ID

每一个任务都应该有一个 ID,作为这个任务的唯一标识。task_struct 里面涉及任务 ID 的如下:

pid\_t pid; //process id线程
pid\_t tgid; //thread group ID 进程的主线程
struct task\_struct \*group_leader;  //进程的主线程。

  • 进程和线程到了内核这里,统一变成了任务,用ps 就可以展出所有点进程。
  • 给任务下发指令kill 可以杀死进程 ,但是杀了线程却杀不死进程。

信号处理

task_struct 里面关于信号处理的字段.

/\* Signal handlers: \*/
struct signal\_struct		\*signal; // 指向shared\_pending 线程组共享的。
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;

任务状态

在这里插入图片描述

在 task_struct 里面,涉及任务状态的是下面这几个变量:
volatile long state;    /\* -1 unrunnable, 0 runnable, >0 stopped \*/
 int exit_state;
 unsigned int flags;




//state(状态)可以取的值定义在 include/linux/sched.h 头文件中。
/\* Used in tsk->state: \*/
#define TASK\_RUNNING 0 //表示进程在时刻准备运行的状态 如果有进程就运行 没有就等
//在运行中的进程,一旦要进行一些 I/O 操作,需要等待 I/O 完毕,这个时候会释放 CPU, 进入睡眠状态。
#define TASK\_INTERRUPTIBLE 1 //可中断的睡眠状态 (根据信号决定)
#define TASK\_UNINTERRUPTIBLE 2 // 不可中断的睡眠状态 (一直等IO完成)
#define TASK\_KILLABLE (TASK\_WAKEKILL | TASK\_UNINTERRUPTIBLE) // TASK\_KILLABLE,一种新的进程睡眠状态 可以终止的新睡眠状态 TASK\_WAKEKILL 用于在接收到致命信号时唤醒进程
#define \_\_TASK\_STOPPED 4 //收到 SIGSTOP、SIGTTIN、SIGTSTP 或者 SIGTTOU 信号之后进入该状态。
#define \_\_TASK\_TRACED 8//表示进程被 debugger 等进程监视,当一个 进程被另外的进程所监视,每一个信号都会让进程进入该状态。
/\* Used in tsk->exit\_state: \*/
#define EXIT\_DEAD 16//EXIT\_DEAD 是进程的最终状态
#define EXIT\_ZOMBIE 32 // exit\_state。
#define EXIT\_TRACE (EXIT\_ZOMBIE | EXIT\_DEAD)
/\* Used in tsk->state again: \*/
#define TASK\_DEAD 64 // exit\_state。
#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


//标志 : 放在 flags 字段中,这些字段都被定义称为宏,以 PF 开头。
#define PF\_EXITING 0x00000004 //PF\_EXITING表示正在退出
#define PF\_VCPU 0x00000010 // PF\_VCPU表示进程运行在虚拟 CPU 上。在函数 account\_system\_time 中, 统计进程的系 统运行时间
#define PF\_FORKNOEXEC 0x00000040 //PF\_FORKNOEXEC表示 fork 完了,还没有 exec(以新的进程去代替原来的进程,但进程的PID保持不变)

在这里插入图片描述

进程调度

进程的状态切换往往涉及调度,下面这些字段都是用于调度的, 看注释即可

// 是否在运行队列上
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;
// 可以使用哪些 CPU
int				nr_cpus_allowed;
cpumask\_t			cpus_allowed;
struct sched\_info		sched_info;


运行统计信息

//进程的运行过程中 运行情况的变量
u64 u64
utime;// 用户态消耗的 CPU 时间 stime;// 内核态消耗的 CPU 时间
unsigned long unsigned long
u64 u64
nvcsw;// 自愿 (voluntary) 上下文切换计数 nivcsw;// 非自愿 (involuntary) 上下文切换计数 start\_time;// 进程启动时间,不包含睡眠时间 real\_start\_time;// 进程启动时间,包含睡眠时间

进程亲缘关系

  • 任何一个进程都有父进程。所以,整个进程其 实就是一棵进程树。而拥有同一父进程的所有进程都具有兄弟关系。
struct task\_struct __rcu \*real_parent; /\* real parent process \*/
struct task\_struct __rcu \*parent; /\* recipient of SIGCHLD, wait4() reports \*/
struct list\_head children;      /\* list of my children \*/
struct list\_head sibling;       /\* linkage in my parent's children list \*/

在这里插入图片描述

如果在 bash 上使用 GDB 来 debug 一个进程,这个时候 GDB 是 real_parent,bash 是这个进程的 parent。

  • parent 指向其父进程。当它终止时,必须向它的父进程发送信号。
  • children 表示链表的头部。链表中的所有元素都是它的子进程。
  • sibling 用于把当前进程插入到兄弟链表中。

进程权限

在 Linux 里面,对于进程权限的定义如下

/\* Objective and real subjective task credentials (COW): \*/ 
const struct cred __rcu \*real_cred;
/\* Effective (overridable) subjective task credentials (COW): \*/ 
const struct cred __rcu \*cred;
//Objective (客): 谁能操纵我 real\_cred
//Subjective(主) : 我能操纵谁 cred
 //进程的凭证集可用结构cred表示
//cred 的定义
struct cred {
......
        kuid\_t          uid;            /\* real UID of the task \*/
        kgid\_t          gid;            /\* real GID of the task \*/
        kuid\_t          suid;           /\* saved UID of the task \*/
        kgid\_t          sgid;           /\* saved GID of the task \*/
        kuid\_t          euid;           /\* effective UID of the task \*/
        kgid\_t          egid;           /\* effective GID of the task \*/
        kuid\_t          fsuid;          /\* UID for VFS ops \*/
        kgid\_t          fsgid;          /\* GID for VFS ops \*/
......
        kernel\_cap\_t    cap_inheritable; /\* caps our children can inherit \*/
        kernel\_cap\_t    cap_permitted;  /\* caps we're permitted \*/
        kernel\_cap\_t    cap_effective;  /\* caps we can actually use \*/
        kernel\_cap\_t    cap_bset;       /\* capability bounding set \*/
        kernel\_cap\_t    cap_ambient;    /\* Ambient capability set \*/
......
} __randomize_layout;

  • uid 和 gid :real user/group id 谁启动的进程,就是谁的 ID。
  • euid 和 egid: 当这个进程要操作消息队列、共享内存、信号量等对象的时候,其实就是在 比较这个用户和组是否有权限
  • fsuid 和 fsgid: filesystem user/group id。这个是对文件操作会审核的 权限。
    在这里插入图片描述

游戏进程的 uid、 euid、fsuid 都是用户 A。第二个图,只有r x,只能运行保存不了,可以通过chmod u+s program 给这个游戏程序设置 set-user-ID 的标识位,权限变成 rwsr-xr-x,然后用户 A 再启动这个游戏 的时候,创建的进程 uid 当然还是用户 A,但是 euid 和 fsuid 就不是用户 A 了,因为看 到了 set-user-id 标识,就改为文件的所有者的 ID,也就是说,euid 和 fsuid 都改成用户 B 了,这样就能够将通关结果保存下来。
在 Linux 里面,一个进程可以随时通过 setuid 设置用户 ID,所以,游戏程序的用户 B 的 ID 还会保存在一个地方,这就是 **suid 和 sgid,也就是 saved uid 和 save gid。**这样就可 以很方便地使用 setuid,通过设置 uid 或者 suid 来改变权限。

除了以用户和用户组控制权限,Linux 还有另一个机制就是capabilities,capabilities,用位图表示权限,在 capability.h 可以找到定义的 权限。

#define CAP\_CHOWN 0
#define CAP\_KILL 5
#define CAP\_NET\_BIND\_SERVICE 10
#define CAP\_NET\_RAW 13
#define CAP\_SYS\_MODULE 16
#define CAP\_SYS\_RAWIO 17
#define CAP\_SYS\_BOOT 22
#define CAP\_SYS\_TIME 25
#define CAP\_AUDIT\_READ 37
#define CAP\_LAST\_CAP CAP\_AUDIT\_READ

  • cap_permitted 表示进程能够使用的权限。但是真正起作用的是 cap_effective
  • cap_bset,也就是 capability bounding set,是系统中所有进程允许保留的权限.
  • cap_ambient 是比较新加入内核的,就是为了解决 cap_inheritable 鸡肋的状况

内存管理

每个进程都有自己独立的虚拟内存空间,这需要有一个数据结构来表示,就是 mm_struct

struct mm\_struct                \*mm;
struct mm\_struct                \*active_mm;


文件与文件系统

每个进程有一个文件系统的数据结构,还有一个打开文件的数据结构。这个我们放到文件系统那一节详细讲述。

/\* Filesystem information: \*/
struct fs\_struct                \*fs;
/\* Open file information: \*/
struct files\_struct             \*files;

小结 看图复述

在这里插入图片描述

用户态的执行和内核态的执行的连接需要变量 struct thread_info thread_info; 和 void *stack;

用户态函数栈

  • 程序的执行往往是一个函数调用另一个函数。函数调用都是通过栈来进行的.

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

来进行的.

[外链图片转存中…(img-l9ONRpZ7-1715591610200)]
[外链图片转存中…(img-AFc4uy8I-1715591610200)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值