Linux内核分析--理解进程调度时机、跟踪分析进程调度和进程切换的过程
学号后三位:426 原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/
1.进程的创建
除了0号进程(系统创建的)之外,linux系统中都是由其他进程创建的。创建新进程的进程,即调用fork函数的进程为父进程,新建的进程为子进程。
fork创建进程分为三种情况:
1)对于父进程,fork函数返回新建子进程的pid;
2)对于子进程,fork函数返回 0;
3)如果出错, fork 函数返回 -1。
PID进程的结构体部分:
struct task_struct{ pid_t pid; //进程id uid_t uid,euid; gid_t gid,egid; volatile long state; //进程状态,0 running(运行/就绪);1/2 均等待态,分别响应/不响应异步信号;4 僵尸态,Linux特有,为生命周期已终止,但PCB未释放;8 暂停态,可被恢复 int exit_state; //退出的状态 unsigned int rt_priority; //调度优先级 unsigned int policy; //调度策略 struct list_head tasks; struct task_struct *real_parent; struct task_struct *parent; struct list_head children,sibling; struct fs_struct *fs; //进程与文件系统管理,进程工作的目录与根目录 struct files_struct *files; //进程对所有打开文件的组织,存储指向文件的句柄们 struct mm_struct *mm; //内存管理组织,存储了进程在用户空间不同的地址空间,可能存的数据,可能代码段 struct signal_struct *signal; //进程间通信机制--信号 struct sighand_struct *sighand; //指向进程 cputime_t utime, stime; //进程在用户态、内核态下所经历的节拍数 struct timespec start_time; //进程创建时间 struct timespec real_start_time; //包括睡眠时间的创建时间 }
2.运行一个案例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { pid_t pid ; pid = fork(); if(pid < 0) { printf("fail to fork\n"); exit(1); } if(pid == 0) { printf("this is the child,pid is : %u\n",getpid()); exit(0); } if(pid > 0) { printf("this is the parent\n"); exit(0); } return 0; }
3.gdb分析:
依次执行:
cd menu
gcc linktable.c menu.c test.c -lpthread -o init -m32 -static
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
qemu-system-i386 -kernel '/home/xu/mykernel/linux-5.0.2/arch/x86/boot/bzImage' -initrd /home/xu/mykernel/rootfs.img -S -s -append nokaslr
开启另一终端,启用gdb调试
gdb vmlinux target remote:1234 b sys_clone b dup_task_struct b do_fork b copy_process b copy_thread b ret_from_for
进程的建立经历了:sys_clone->do_fork->copy_process->copy_thread->ret_form_fork
创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。 每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。 fork执行完毕后,出现两个进程,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。可以说,我们就是通过fpid来识别和操作父子进程的。 还有人可能疑惑为什么不是从#include处开始复制代码的,这是因为fork是把进程当前的情况拷贝一份,执行fork时,进程已经执行完了int count=0;fork只拷贝下一个要执行的代码到新的进程。