从整体上理解进程创建、可执行文件的加载和进程执行进程切换,重点理解分析fork、execve和进程切换

本文深入探讨Linux系统中的进程创建、可执行文件加载和进程执行过程,包括fork函数如何创建新进程、execve如何加载可执行文件以及进程切换的细节。通过分析task_struct数据结构、gdb跟踪系统调用,揭示了Linux进程执行过程的关键步骤,如进程上下文切换、动态链接和ELF文件格式。实验总结强调了进程的创建、执行和调度的理解,强调了内核如何管理进程的生命周期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

235+原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ 

 

实验要求:

  • 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235;
  • 分析fork函数对应的内核处理过程do_fork,理解创建一个新进程如何创建和修改task_struct数据结构;
  • 使用gdb跟踪分析一个fork系统调用内核处理函数do_fork ,验证您对Linux系统创建一个新进程的理解,特别关注新进程是从哪里开始执行的?为什么从那里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
  • 理解编译链接的过程和ELF可执行文件格式;
  • 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接;
  • 使用gdb跟踪分析一个execve系统调用内核处理函数do_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解;
  • 特别关注新的可执行程序是从哪里开始执行的?为什么execve系统调用返回后新的可执行程序能顺利执行?对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时会有什么不同?
  • 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否准确;
  • 使用gdb跟踪分析一个schedule()函数 ,验证您对Linux系统进程调度与进程切换过程的理解;
  • 特别关注并仔细分析switch_to中的汇编代码,理解进程上下文的切换机制,以及与中断上下文切换的关系;
  • 撰写一篇博客内容的具体要求如下:
    • 题目自拟,内容围绕Linux系统的执行过程进行;
    • 博客中需要使用实验截图
    • 博客内容中需要仔细分进程创建、可执行文件的加载和进程执行进程切换
    • 总结部分需要阐明自己对Linux系统的执行过程的理解

实验内容:

一、阅读理解task_struct数据结构

剖析task_struct,在这之前我们需要先了解一下进程的概念和Linux下进程控制块PCB。

1.进程是计算机中已运行程序的实体。在面向线程设计的系统(Linux 2.6及更新的版本)中,进程本身不是基本运行单位,而是线程的容器。

2.每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。该结构定义位于:

/include/linux/sched.h

3.task_struct是Linux内核的一种数据结构,它会被装载到RAM中并且包含着进程的信息。每个进程都把它的信息放在 task_struct 这个数据结构体,task_struct 包含了这些内容: 

(1)标识符 : unsigned int flags;

每一个进程都拥有自己的进程标识符、用户标识符、组标识符 
进程标识符PID是用来表示不同进程的,每一个进程都有唯一的标识符,内核就是通过这个标识符来识别不同的进程的。

pid_t pid;//进程的唯一标识
pid_t tgid;// 线程组的领头线程的pid成员的值

(2)状态 : volatile long state;   

Linux中的进程由多种状态,在运行的过程中,进程会随着调度在多种情况下转换,进程的信息是进程进行调度的对换的依据。表示进程的运行状态,-1为不可运行,0可以运行,大于0表示停止。 
内核中状态的表示有以下几种: 

#define TASK_RUNNING        0//进程要么正在执行,要么准备执行
#define TASK_INTERRUPTIBLE  1 //可中断的睡眠,可以通过一个信号唤醒
#define TASK_UNINTERRUPTIBLE    2 //不可中断睡眠,不可以通过信号进行唤醒
#define __TASK_STOPPED      4 //进程停止执行
#define __TASK_TRACED       8 //进程被追踪
/* in tsk->exit_state */ 
#define EXIT_ZOMBIE     16 //僵尸状态的进程,表示进程被终止,但是父进程还没有获取它的终止信息,比如进程有没有执行完等信息。                     
#define EXIT_DEAD       32 //进程的最终状态,进程死亡
/* in tsk->state again */ 
#define TASK_DEAD       64 //死亡
#define TASK_WAKEKILL       128 //唤醒并杀死的进程
#define TASK_WAKING     256 //唤醒进程


(3)进程的标志:  unsigned int flags; 

当前进程的标志,用于内核识别当前进程的状态,以备下一步操作。 
flags的取值有以下几种情况: 

//flags成员的可能取值如下

//进程的标志信息
#define PF_ALIGNWARN    0x00000001    /* Print alignment warning msgs */
                    /* Not implemented yet, only for 486*/
#define PF_STARTING    0x00000002    /* being created */
#define PF_EXITING    0x00000004    /* getting shut down */
#define PF_EXITPIDONE    0x00000008    /* pi exit done on shut down */
#define PF_VCPU        0x00000010    /* I'm a virtual CPU */
#define PF_FORKNOEXEC    0x00000040    /* forked but didn't exec */
#define PF_MCE_PROCESS  0x00000080      /* process policy on mce errors */
#define PF_SUPERPRIV    0x00000100    /* used super-user privileges */
#define PF_DUMPCORE    0x00000200    /* dumped core */
#define PF_SIGNALED    0x00000400    /* killed by a signal */
#define PF_MEMALLOC    0x00000800    /* Allocating memory */
#define PF_FLUSHER    0x00001000    /* responsible for disk writeback */
#define PF_USED_MATH    0x00002000  /* if unset the fpu must be initialized before use */
#define PF_FREEZING    0x00004000    /* freeze in progress. do not account to load */
#define PF_NOFREEZE    0x00008000    /* this thread should not be frozen */
#define PF_FROZEN    0x00010000    /* frozen for system suspend */
#define PF_FSTRANS    0x00020000    /* inside a filesystem transaction */
#define PF_KSWAPD    0x00040000    /* I am kswapd */
#define PF_OOM_ORIGIN    0x00080000    /* Allocating much memory to others */
#define PF_LESS_THROTTLE 0x00100000    /* Throttle me less: I clean memory */
#define PF_KTHREAD    0x00200000    /* I am a kernel thread */
#define PF_RANDOMIZE    0x00400000    /* randomize virtual address space */
#define PF_SWAPWRITE    0x00800000    /* Allowed to write to swap */
#define PF_SPREAD_PAGE    0x01000000    /* Spread page cache over cpuset */
#define PF_SPREAD_SLAB    0x02000000    /* Spread some slab caches over cpuset */
#define PF_THREAD_BOUND    0x04000000    /* Thread bound to specific cpu */
#define PF_MCE_EARLY    0x08000000      /* Early kill for mce process policy */
#define PF_MEMPOLICY    0x10000000    /* Non-default NUMA mempolicy */
#define PF_MUTEX_TESTER    0x20000000    /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP    0x40000000    /* Freezer should not count it as freezeable */
#define PF_FREEZER_NOSIG 0x80000000    /* Freezer won't send signals to it */

(4)优先级 : int prio, static_prio, normal_prio; unsigned int rt_priority;

相对于其他进程的优先级。

è¿éåå¾çæè¿°

 

实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。值越大静态优先级越低。

static_prio用于保存静态优先级,可以通过nice系统调用来进行修改。 
rt_priority用于保存实时优先级。 
normal_prio的值取决于静态优先级和调度策略(进程的调度策略有:先来先服务,短作业优先、时间片轮转、高响应比优先等等的调度算法) 
prio用于保存动态优先级。 
policy表示进程的调度策略,目前主要有以下五种:

#define SCHED_NORMAL        0//按照优先级进行调度(有些地方也说是CFS调度器)
#define SCHED_FIFO        1//先进先出的调度算法
#define SCHED_RR        2//时间片轮转的调度算法
#define SCHED_BATCH        3//用于非交互的处理机消耗型的进程
#define SCHED_IDLE        5//系统负载很低时的调度算法
#define SCHED_RESET_ON_FORK     0x40000000

SCHED_NORMAL用于普通进程,通过CFS调度器实现;

SCHED_BATCH用于非交互的处理器消耗型进程;

SCHED_IDLE是在系统负载很低时使用;

SCHED_FIFO(先入先出调度算法)和SCHED_RR(轮流调度算法)都是实时调度策略.

(4)进程之间的亲属关系:

struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
struct list_head children;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值