三、系统的进程管理

1、系统的进程运转方式

系统时间 (jiffies 系统滴答)

cpu内部有一个RTC(系统的定时器),会在上电的时候调用mktime函数算出1970年一月一日0时开始到当前开机点所过的秒数。

mktime.c

/**  linux/kernel/mktime.c**  (C) 1991  Linus Torvalds*/#include <time.h>/** This isn't the library routine, it is only used in the kernel.* as such, we don't care about years<1970 etc, but assume everything* is ok. Similarly, TZ etc is happily ignored. We just do everything* as easily as possible. Let's find something public for the library* routines (although I think minix times is public).*/
/** PS. I hate whoever though up the year 1970 - couldn't they have gotten* a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy.*/
#define MINUTE 60
#define HOUR (60*MINUTE)
#define DAY (24*HOUR)
#define YEAR (365*DAY)/* interestingly, we assume leap-years */
static int month[12] = {0,DAY*(31),DAY*(31+29),DAY*(31+29+31),DAY*(31+29+31+30),DAY*(31+29+31+30+31),DAY*(31+29+31+30+31+30),DAY*(31+29+31+30+31+30+31),DAY*(31+29+31+30+31+30+31+31),DAY*(31+29+31+30+31+30+31+31+30),DAY*(31+29+31+30+31+30+31+31+30+31),DAY*(31+29+31+30+31+30+31+31+30+31+30)
};long kernel_mktime(struct tm * tm)
{long res;int year;year = tm->tm_year - 70;
/* magic offsets (y+1) needed to get leapyears right.*/res = YEAR*year + DAY*((year+1)/4);res += month[tm->tm_mon];
/* and (y+2) here. If it wasn't a leap-year, we have to adjust */if (tm->tm_mon>1 && ((year+2)%4))res -= DAY;res += DAY*(tm->tm_mday-1);res += HOUR*tm->tm_hour;res += MINUTE*tm->tm_min;res += tm->tm_sec;return res;
}

首先是四个宏定义,然后用调用kernal_mktime,会看到传了一个time,这个time参数就是一个时间结构体,赋值是由初始化时间从RTC(CMOS)读出来。转化为时间存入全局变量中,并且为jiffies所用。

jiffies一个系统滴答 每隔10ms会引发一个定时器中断
中断服务函数中首先进行了jiffies的自加。这个中断叫timer_interrupt 在sys_call.s。
首先是栈的保存,然后修改一些寄存器的值,然后自加jiffies,然后就call_do_timer。

_timer_interrupt:push %ds        # save ds,es and put kernel data spacepush %es      # into them. %fs is used by _system_callpush %fspushl %edx      # we save %eax,%ecx,%edx as gcc doesn'tpushl %ecx      # save those across function calls. %ebxpushl %ebx      # is saved as we use that in ret_sys_callpushl %eaxmovl $0x10,%eaxmov %ax,%dsmov %ax,%esmovl $0x17,%eaxmov %ax,%fsincl _jiffiesmovb $0x20,%al       # EOI to interrupt controller #1outb %al,$0x20movl CS(%esp),%eaxandl $3,%eax        # %eax is CPL (0 or 3, 0=supervisor)pushl %eaxcall _do_timer       # 'do_timer(long CPL)' does everything fromaddl $4,%esp       # task switching to accounting ...jmp ret_from_sys_call

上面的最后调用了 _do_timer
这个函数在sched.c

void do_timer(long cpl)
{extern int beepcount;extern void sysbeepstop(void);if (beepcount)if (!--beepcount)sysbeepstop();if (cpl)current->utime++;elsecurrent->stime++;if (next_timer) {next_timer->jiffies--;while (next_timer && next_timer->jiffies <= 0) {void (*fn)(void);fn = next_timer->fn;next_timer->fn = NULL;next_timer = next_timer->next;(fn)();}}if (current_DOR & 0xf0)do_floppy_timer();if ((--current->counter)>0) return;current->counter=0;if (!cpl) return;schedule();
}

if (cpl)
current->utime++;
else
current->stime++;

cpl表示当前被中断的程序的特权。0表示内核进程。1表示用户进程。
进程都是在跟task_struck对象,比如一个进程的创建,就是建立一个这个对象。
current就是task_struck的一个实例。
utime用户程序的运行时间。
stime内核程序的运行时间。

if (next_timer) {
next_timer->jiffies–;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}

next_time是一个时间链表的指针。是嫁接与jiffies这个变量的所有定时器的时间链表。
查看定时器链表,如果时间等于0就调用中断函数。

current->counter --进程的时间片。
标志着当前进程还能运行多长时间。
tack_struck[]时间向量表 counter 时间片
counter—在哪里用 进程的调度就是task_struck[]进程链表的检索,找时间片最大的那个进程对象,然后进行调用,知道时间片为0,就退出,之后再进行新一轮的调用。

counter—在哪里设置 当全部的task_struck[]如果所有的进程的counter都为0,就是进程都运行完了,就进行新一轮的counter分配。(优先级分配)

(*p)->counter = ((*p)->counter >> 1) +(*p)->priority

我们0.1.1 优先级时间片轮转调度算法。

2、如何进行创建一个新的进程

进程封装成了一个结构体。

struct task_struct {/* these are hardcoded - don't touch */long state; /* -1 unrunnable, 0 runnable, >0 stopped  进程的装态*/long counter; /*时间片的计数值*/long priority; /*优先级*/long signal;struct sigaction sigaction[32];long blocked; /* bitmap of masked signals */
/* various fields */int exit_code;unsigned long start_code,end_code,end_data,brk,start_stack;long pid,father,pgrp,session,leader;unsigned short uid,euid,suid;unsigned short gid,egid,sgid;long alarm;long utime,stime,cutime,cstime,start_time;unsigned short used_math;
/* file system info */int tty;      /* -1 if no tty, so it must be signed */unsigned short umask;struct m_inode * pwd;struct m_inode * root;struct m_inode * executable;unsigned long close_on_exec;struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */struct desc_struct ldt[3];
/* tss for this task */struct tss_struct tss;
};

state; -1 unrunnable, 0 runnable, >0 stopped 进程的装态
counter; 时间片的计数值
priority 优先级
counter = counter/2+priority

有一个进程链表,task。检索链表,查看counter。

每个进程都有LDT局部描述符,还有一个TSS进程的状态描述符。
LDT里面会有进程的数据段,跟进程的代码段。
TSS 在进程运行的过程,cpu需要知道的进程的状态标识。

分时技术进行多进程调度。

重点:进程的创建是如何的?

linux在初始化的过程中会进行0号进程的创建。
main函数。

void main(void)      /* This really IS void, no error here. */
{           /* The startup routine assumes (well, ...) this */
/** Interrupts are still disabled. Do necessary setups, then* enable them*/ROOT_DEV = ORIG_ROOT_DEV;drive_info = DRIVE_INFO;memory_end = (1<<20) + (EXT_MEM_K<<10);memory_end &= 0xfffff000;if (memory_end > 16*1024*1024)memory_end = 16*1024*1024;if (memory_end > 12*1024*1024) buffer_memory_end = 4*1024*1024;else if (memory_end > 6*1024*1024)buffer_memory_end = 2*1024*1024;elsebuffer_memory_end = 1*1024*1024;main_memory_start = buffer_memory_end;
#ifdef RAMDISKmain_memory_start += rd_init(main_memory_start, RAMDISK*1024);
#endif//进行内存控制器的初始化加载内存驱动mem_init(main_memory_start,memory_end);//异常函数的初始化trap_init();//进行块设备驱动的初始化,加载块设备驱动blk_dev_init();//进行字符型设备驱动的初始化,加载字符型设备驱动chr_dev_init();//进行控制台设备的初始化,加载显示和传输设备的驱动tty_init();//加载定时器驱动time_init();//进行进程调度的初始化sched_init();//进行缓冲区初始化buffer_init(buffer_memory_end);//进行硬盘设备的初始化,加载硬盘驱动hd_init();//进行软盘设备的初始化,加载软盘驱动floppy_init();sti();//从内核的初始化状态切换到用户模式。move_to_user_mode();//创建0号进程运行最初的应用软件if (!fork()) {     /* we count on this going ok */init();}
/**   NOTE!!   For any other task 'pause()' would mean we have to get a* signal to awaken, but task0 is the sole exception (see 'schedule()')* as task 0 gets activated at every idle moment (when no other tasks* can run). For task0 'pause()' just means we go check if some other* task can run, and if not we return here.*/for(;;) pause();
}

刚开始一些东西是初始化linux,然后就是初始化一些什么异常初始化,驱动初始化,时间初始化等等。
我们现在要分析的是进程调度初始化。

void sched_init(void)
{int i;struct desc_struct * p;if (sizeof(struct sigaction) != 16)panic("Struct sigaction MUST be 16 bytes");set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));p = gdt+2+FIRST_TSS_ENTRY;for(i=1;i<NR_TASKS;i++) {task[i] = NULL;p->a=p->b=0;p++;p->a=p->b=0;p++;}
/* Clear NT, so that we won't have troubles with that later on */__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");ltr(0);lldt(0);outb_p(0x36,0x43);       /* binary, mode 3, LSB/MSB, ch 0 */outb_p(LATCH & 0xff , 0x40); /* LSB */outb(LATCH >> 8 , 0x40); /* MSB */set_intr_gate(0x20,&timer_interrupt);outb(inb_p(0x21)&~0x01,0x21);set_system_gate(0x80,&system_call);
}

系统级别GDT 描述符。
代码先是复制信息到GDT,然后把task链表清空。
task链表就是进程链表。
然后进入汇编代码阶段,都是在初始化一些寄存器。
最后会发现有个这玩意

set_system_gate(0x80,&system_call);

所有人都能用的一个中断,那么这个中断是一个系统调用。

linux在初始化的过程中会进行0号进程的创建。
在move_to_user_mode()这个函数,下面fork已经是在创建1号进程了。
对于0号进程的软件抽象实体已经通过静态的方式定义好了,那如何让这个实体运行呢?回想一下,进程的软件抽象是由描述进程相关的成员及执行时的栈空间组成,对于进程描述符描述的是进程的相关静态属性,而栈空间是进程执行时动态空间。因为在进程0初次执行时,对于进程的调度程序其实还没初始化,所以进程0的首次运行肯定不是通过内核中的调度程序加载的,而是简单的通过将栈寄存器(esp)指向进程0的栈底位置,从而表示了在接下来的程序执行是在进程0的栈空间中,也就是进程0在执行了。

fork下面做了个init工作。
init是干了点啥。

void init(void)
{int pid,i;setup((void *) &drive_info);(void) open("/dev/tty0",O_RDWR,0);(void) dup(0);(void) dup(0);//打开了三个控制台,标准输入输出错误printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,NR_BUFFERS*BLOCK_SIZE);printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);if (!(pid=fork())) { //下面就是一个forkclose(0);if (open("/etc/rc",O_RDONLY,0))_exit(1);execve("/bin/sh",argv_rc,envp_rc);_exit(2);}if (pid>0)while (pid != wait(&i))/* nothing */;while (1) {if ((pid=fork())<0) {printf("Fork failed in init\r\n");continue;}if (!pid) {close(0);close(1);close(2);setsid();(void) open("/dev/tty0",O_RDWR,0);(void) dup(0);(void) dup(0);_exit(execve("/bin/sh",argv,envp));}while (1)if (pid == wait(&i))break;printf("\n\rchild %d died with code %04x\n\r",pid,i);sync();}_exit(0);    /* NOTE! _exit, not exit() */
}

进程初始化:
0号进程
1、设置了一些驱动信息,打开了一个tty0文件,打开标准输入控制台。
又打开了标准输出控制台跟标准错误控制台。
2、创建1号进程,如果创建成功,则在一号进程中打开一个文件/etc/rc,然后执行shell程序。etc在linux里面是配置文件,rc文件呢就是它会读取这个文件,然后执行这个里面所有的命令。
3、下面的while嵌套就是说用另外一种方式实现上面的工作。
4、0号进程不可能结束,他会在没有其他进程调用的时候调用,只会执行for(;

linux内核笔记二 进程管理相关推荐

  1. 【Linux 内核笔记】进程管理

    文章目录 进程创建 进程终结 孤儿进程 小结 clone()-fork()-exec()-exit() 子进程结束ZOMBIE 父进程wait4() 进程描述符task_struct进程所有信息 由t ...

  2. linux 进程管理 ppt,Linux内核结构与进程管理.ppt

    Linux内核结构与进程管理.ppt Linux 内核结构与进程管理,Linux系统结构Linux kernel 开放源代码的linux操作系统内核,目前版本为2.6,Linux内核组成1. 进程调度 ...

  3. 操作系统实验一 Linux基本操作|实验二 进程管理

    由于当时没存代码,只有实验文档代码截图,文末也可直接获取实验文档. 操作系统实验 目录 实验一 Linux基本操作 实验二进程管理 实验一 Linux基本操作 1实验目的 1.熟悉在Linux操作系统 ...

  4. Linux内核学习008——进程管理(四)

    Linux内核学习007--进程管理(四) 进程家族树 Unix系统的进程之间存在一个明显的继承关系,所有的进程都是PID为1的init进程的后代.内核在系统启动的最后阶段启动init进程,然后ini ...

  5. Linux内核机制总结进程管理之SMP调度(六)

    文章目录 1 SMP调度 1.1 进程的cpu亲和性 1.2 对调度器的拓展 1.3 期限调度类的cpu负载均衡 1.4 实时调度类的cpu负载均衡 1.5 公平调度类的cpu负载均衡 1.6 迁移线 ...

  6. linux内核——3_(进程管理)系统的进程管理

    作者:GWD 时间:2019.7.28 一.系统的进程的运转方式 1.系统时间:(jiffies系统滴答):CPU内部有一个RTC,会在上电的时候调用mktime函数算出从1970年1月1日0时开始到 ...

  7. Linux学习笔记_12_进程管理(ps, top)服务管理(service)查看系统网络(netstat)

    1. 进程的基本介绍 在 LINUX 中, 每个执行的程序(代码) 都称为一个进程. 每一个进程都分配一个 ID 号. 每一个进程, 都会对应一个父进程, 而这个父进程可以复制多个子进程. 例如 ww ...

  8. linux内核打开文件数,放开Linux内核对用户进程可打开文件数和TCP连接的限制

    一. 检查linux内核 uname -a lsb_release -a 二.用户进程可打开文件数限制 1)vim /etc/security/limits.conf *       -      n ...

  9. linux内核自旋锁解释,LINUX内核笔记:自旋锁

    目录 1.自旋锁作用与基本使用方法? 与其他锁一样,自旋锁也用于保护临界区,但是自旋锁主要是用于在SMP上保护临界区.在SMP上,自旋锁最多只能被一个可执行线程持有,如果一个线程尝试获得一个被争用的自 ...

最新文章

  1. linux虚拟地址被大量占用,《Linux中为什么要使用虚拟地址》
  2. 开源监控解决方案OpenFalcon系列(一)
  3. 前端学习(2822):页面配置文件
  4. python shelve模块
  5. PowerMock介绍
  6. 1.简述计算机硬盘如何保养,电脑硬盘的保养知识
  7. Java网络编程从入门到精通(15):为什么要使用SocketAddress来管理网络地址
  8. Word文件标尺工具不见了,怎么恢复
  9. 咸鱼ZTMR实例—PS2手柄控制板载LED
  10. ctf gif动图分解帧 提取flag
  11. redis 存 JSONObject 报 nested exception is java.io.NotSerializableException:
  12. C语言加油站程序,计算机学院“0101”计划名师导航篇“C语言加油站”第一讲如期开讲...
  13. 我的世界Java种子算法_Minecraft:说说“种子”的使用和原理吧
  14. Android 画布使用之电子签名
  15. 大文件分块计算MD5值 C++实现
  16. html列表太多转为下滑菜单,利用CSS过渡属性Transition制作缓缓弹出的纯CSS下拉菜单...
  17. 英文文章:中国国家医疗联合体的演化路径和内部交互机制研究:基于复杂系统理论的分析
  18. 10个可视化图表数据网站推荐
  19. android系统手机流量控制方法,手机流量控制!安卓手机控制流量设置小技巧
  20. 使用flex布局实现div垂直居中

热门文章

  1. 欧科传动变频器说明书_欧科变频器说明书文档
  2. [转]关于Mysql设置等
  3. 企业做搜索引擎关键词排名优化要多少钱?
  4. Shader|OpenGL与DirectX_该用户还没想到昵称_新浪博客
  5. coreldraw带圈字符_CDR怎么做弧形文字或者环形文字?
  6. 【2023秋招面经】20220713兴业数金前端一面
  7. 1688API大全、商品详情调用数据返回展示
  8. 倒计时5天丨《现场活动指南》新鲜出炉!带你玩转慕尼黑华南电子展
  9. 深度学习(十) Unsupervised Learning 理论部分
  10. 多触点电器自动测试系统