标签归档:MOOC

Coursera第一次获证失败

最担心的事发生了。由于一次编程作业迟交,没人评改最终导致课程不通过。好伤心,还好还有一次重修机会。否则我的29美刀真是心疼死我了。


还庆幸加入新版次原来完成的作业包括评分都一并保存。
这样我就可以不用重复上课了。只要坐等8月1号就好了。

USTC001 LINUX操作系统分析 部分期末考试题

想了我好久,却不知道我哪里写错了。后来发现我的是对的。要以16进制的格式写,而我却用10进制的格式。

1# 在Linux系统的一般执行过程中,即用户态进程X切换到用户态进程Y的过程中,分析如下进程切换的关键汇编代码,请问系统执行到标号1(即第50行代码)之前的时间点CPU的寄存器eip的值是?直接填eip存储的数值(立即数以$开头)
答案就是$1f 2# 从运行时的角度(执行视图)来看Linux系统,下列哪些属于Linux系统中的执行实体?

一直以来, linux内核并没有线程的概念. 每一个执行实体都是一个task_struct结构, 通常称之为进程. 进程是一个执行单元, 维护着执行相关的动态资源. 同时, 它又引用着程序所需的静态资源(注意这里说的是linux中的进程).通过系统调用clone创建子进程时, 可以有选择性地让子进程共享父进程所引用的资源. 这样的子进程通常称为轻量级进程.

——

《linux线程浅析》

3# Linux系统中,用户态切换到内核态时,int指令或中断信号会触发CPU自动保存下面哪些信息到内核堆栈中?

#2.Linux系统的一般执行过程

Linux系统的一般执行过程大概可以抽象成:

最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程

  1. 正在运行的用户态进程X
    
  2. 发生中断——save cs:eip/esp/eflags(current) ,
    
    
    load cs:eip(entry of a specific ISR)
    
    
    ss:esp(point to kernel stack). 这一过程由CPU自动完成
    
  3. SAVE_ALL //保存现场
    
  4. 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换
    
  5. 标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)
    
  6. <a href="http://codelab.shiyanlou.com/s?refs=restore_all&amp;project=linux-3.18.6" rel="nofollow" target="_blank">
     restore_all
    </a>
    //恢复现场
    
  7. iret - pop cs:eip/ss:esp/eflags from kernel stack
    
  8. 继续运行用户态进程Y
    



理解进程调度时机跟踪分析进程调度与进程切换的过程

4# Linux内核中,系统调用处理过程中保护现场使用的宏是 SAVE_ALL 5# 动态连接有两种形式:可执行程序装载时动态连接和运行时动态链接。

——《

linux可执行文件的装载

退掉MOOC通信原理

实在是没时间了,临近毕设deadline,实在是没时间跟进通信了。虽然我很想学,但也只好暂时放弃,以后再看吧。


虽然有点不舍,认认真真的做完前三周的作业。

Linux内核学习总结

文章目录:

  1. 实验:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
  2. 实验:完成一个简单的时间片轮转多道程序内核代码
  3. 实验:跟踪分析Linux内核的启动过程
  4. 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
  5. 从system_call开始到iret
  6. 分析Linux内核创建一个新进程的过程
  7. linux可执行文件的装载
  8. 理解进程调度时机跟踪分析进程调度与进程切换的过程

1#我对linux系统的理解

linux很难,这8周的学习我对绝对还是不理解Linux的。只能说比学之前更理解它了。很多细节深入到底层硬件方面的问题,所以还需要有计算机组成原理的知识,尤其是在堆栈方面的讨论。

linux的哲学

Linux is built with a certain set of unifying principles in mind. Understanding these principles is very helpful in understanding how the system works as a whole. They are known as the “Linux Way”, which is derived from the philosophy behind the UNIX system.

The Linux Way can be summarized as:

  • Use programs that do only one task, but do it well.
    <br/>
    
  • To accomplish complex tasks, use several programs linked together.
    
  • Store information in human-readable plain text files whenever it is possible.
    
  • There is no "one true way" to do anything.
    
  • Prefer commandline tools over graphical tools.
    

Most traits of Linux are a consequence of these principles. In accordance with them, a Linux system is built out of small, replaceable components. We will examine the most important of them in more detail. Those are: the boot loader, the kernel, the shell, the X window server, the window manager and the desktop environment. After that, we will have a look at the file system in Linux. Finally, we will discuss the security of a computer running Linux.

——

Linux Guide/How Linux Works

简单总结就是KISS(keep it simple and stupid)原则

  • 用程序来完成一项任务,然后把任务做好。

  • 复杂的任务用多个程序来完成。

  • 把信息存在更人性化的文本中。

  • 没有唯一的方法。

  • 命令行能做的事就不用图形工具来完成。

这些原则使linux保持简单且高效。

用户通过交互程序与内核打交道,而内核则与硬件直接打交道。这样就把用户、内核、硬件分离开。因此内核就是理解linux的关键。

在内核与用户之间并不是直接关联的,而是通过系统调用来把用户和内核隔离开。这样既安全又方便程序员进行编程,而不是深入到内核中去操作。

内核中又分为多个模块,各各模块管理者不同的任务

通过这些模块与硬件进行打交道
整体框架大概就是这样。我并不是程序员,从我学习的目的来说我并不需要深入到每个模块是如何实现的一些具体细节。就像程序员编程不需要知道每个轮子是怎么制造的一样。 Kernel (operating system)
2#反思实验都做了些什么?

八周下来,周周有实验。再把每周标题串起来,构建一条思路。


###先整体:1~3周

1.计算机只能读懂机器代码,因此我们编程后还要将代码转换成机器代码

2.计算机要高效,就必须多个进程进行轮转。因此就产生了切换时机问题,上下文保存切换问题。

###再系统:4~5周

所谓三层皮目的就是将用户与内核分隔开。用户程序调用API,API调用系统调用,系统调用调用内核

###最后细节:6~8周

三层皮之间的相互调用必然涉及到不用的程序执行,也就是进程切换。进程又可分为用户空间和内核空间进程。进程进行切换,又设计到上下文的保存于切换,于是又回到底层的堆栈操作上。实际上我认为,在这里程序员就不再需要讨论堆栈操作了,将堆栈操作留给系统去做就可以了。否则就会陷入到无限的细节中。这也就是为什么对内核进行层层封装了。

#3.总结课程

这门课让我对linux有了一个感性的认识,这些认识对我日后学习嵌入式必然有帮助。我学习这门课的目的也大体达到。剩下的一些问题,或者说缺乏的理性认识,则可以在实践中慢慢摸索。

《Linux内核分析》课程后最大的遗憾?好吧,那就是缺乏对细节的把握。这单单看视频是不够的。

==================================================

sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

理解进程调度时机跟踪分析进程调度与进程切换的过程

理解Linux系统中进程调度的时机

Linux的调度程序是一个叫Schedule()的函数,这个函数被调用的频率很高,由它来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等等。

Linux调度时机主要有:

  1. 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule():
  2. 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
  3. 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

不同类型的进程有不同的调度需求

第一种分类:

  1. I/O-bound:频繁的进行I/O通常会花费很多时间等待I/O操作的完成
  2. CPU-bound:计算密集型需要大量的CPU时间进行运算

第二种分类:

  1. 批处理进程(batch process):不必与用户交互,通常在后台运行不必很快响应典型的批处理程序:编译程序、科学计算

  2. 实时进程(real-time process):有实时需求,不应被低优先级的进程阻塞响应时间要短、要稳定典型的实时进程:视频/音频、机械控制等

  3. 交互式进程(interactive process):需要经常与用户交互,因此要花很多时间等待用户输入操作响应时间要快,平均延迟要低于50~150ms典型的交互式程序:shell、文本编辑程序、图形应用程序等Linux 进程调度+Linux系统一般执行过程 笔记

进程上下文包含了进程执行需要的所有信息:

用户地址空间:包括程序代码,数据,用户堆栈等

控制信息:进程描述符,内核堆栈等

硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)

2.schedule()函数

schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
关键函数
__schedule();

next = pick_next_task(rq, prev);包装了使用某种进程调度策略,但不管使用什么调度策略,它总是选择下一个进程

选完下一个进程后,就要完成进程上下文的切换。
进程上下文的切换主要是通过context_switch(rq, prev, next)实现。

## context_switch(rq, prev, next) 是如何实现上下文切换的?

1-prepare_task_switch(rq,prev,next);//提前做准备

2-context_tracking_task_switch(prev,next);//切换寄存器的状态和堆栈

3-switch_to(prev,next,prev);

switch_to(prev, next, prev);的汇编

switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程

42  asm volatile(“pushfl\n\t”      /* save    flags */

43           “pushl %%ebp\n\t”        /* save    EBP   */ \

44           “movl %%esp,%[prev_sp]\n\t”  /* save    ESP   */ \这两步实际上完成了内核 45           “movl %[next_sp],%%esp\n\t”  /* restore ESP   */ \堆栈的切换

46           “movl $1f,%[prev_ip]\n\t”    /* save    EIP   */ \

47           “pushl %[next_ip]\n\t”   /* restore EIP   */    \将NEXT进程的起点压入堆栈

48           __switch_canary                   \

49           “jmp __switch_to\n”  /* regparm call  */ \

50           “1:\t”                        \

51           “popl %%ebp\n\t”     /* restore EBP   */    \

52           “popfl\n”         /* restore flags */  \

53                                  \

54           /* output parameters */                \

55           : [prev_sp] “=m” (prev->thread.sp),     \

56             [prev_ip] “=m” (prev->thread.ip),        \

57             “=a” (last),                 \

58                                  \

59             /* clobbered output registers: */     \

60             “=b” (ebx), “=c” (ecx), “=d” (edx),      \

61             “=S” (esi), “=D” (edi)             \

62                                       \

63             __switch_canary_oparam                \

64                                  \

65             /* input parameters: */                \

66           : [next_sp]  “m” (next->thread.sp),        \

67             [next_ip]  “m” (next->thread.ip),       \

68                                       \

69             /* regparm parameters for __switch_to(): */  \

70             [prev]     “a” (prev),              \

71             [next]     “d” (next)               \

72                                  \

73             __switch_canary_iparam                \

74                                  \

75           : /* reloaded segment registers */           \

76          “memory”);                  \

Linux系统的一般执行过程

Linux系统的一般执行过程大概可以抽象成:

最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程

  1. 正在运行的用户态进程X

  2. 发生中断——save cs:eip/esp/eflags(current) ,

    load cs:eip(entry of a specific ISR)

    ss:esp(point to kernel stack). 这一过程由CPU自动完成

  3. SAVE_ALL //保存现场

  4. 中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换

  5. 标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)

  6. restore_all //恢复现场
  7. iret – pop cs:eip/ss:esp/eflags from kernel stack

  8. 继续运行用户态进程Y

几种特殊情况

  • 通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;

  • 内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;

  • 创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;

  • 加载一个新的可执行程序后返回到用户态的情况,如execve;

Linux系统架构和执行过程概览

  • 内核(进程管理,进程调度,进程间通讯机制,内存管理,中断异常处理,文件系统,I/O系统,网络部分)
  • 其他程序(例如函数库、shell程序、系统程序等等)

从用户的角度来看:
从CPU的角度来看: #4.gdb跟踪分析一个schedule()函数
qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img -s -S

打开GDB

gdb

file …/linux-3.18.6/vmlinux

target remote:1234

设置断点:

b schedule

b context_switch

b switch_to

b pick_next_task


==================================================

似乎明白了一点点。

sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

从system_call开始到iret

自己搭建了实验环境,逃离实验楼,实在是太卡了。自己的环境就是爽!

实验开始:

先加如getuid和getuidAsm代码:


运行系统:


实验下,添加的功能是否可以
效果没问题。

用GDB调试,设置断点

一步一步执行


实验目的达到了。

===========================================================================================================================这里是分割线========================================

从system_call开始到iret

网上有几张图很好的说明了这个过程。

能力不足,不足以详细的说明从system_call到iret的详细过程。在查阅了些资料后,发现有几张图还是解释的不错的,于是就把他们贴出来了。其实也是一种偷懒啦~

下图引用

《linux系统调用原理分析(下)》

下图引用

《system_call中断处理过程》

经过《扒开系统调用的三层皮》2周的学习,对系统调用有些感性的认识,但是没有书看啊,总感觉还是挺乱的。

sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about ”

ubuntu环境搭建MenuOS的过程

实验楼做实验总是卡死,于是决定干脆自己搭建环境。


安装ubunt 14

安装qemu

下载linux-3.9.4源码包

原有桌面环境太消耗资源了,于是换成了xfce4

解压linux-3.18.6源码包

开始漫长的make。。。。。

终于好了

安装个git

git clone https://github.com/mengning/menu.git


搭建好啦

使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。


首先man getuid查看相关信息

编程


编译并运行:

嵌入式汇编:

系统调用是怎么工作的?

一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为什么它被称作"保护模式")。系统调用是这些规则的一个例外。其原理是进程先用适当的值填充寄存器,然后调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置(当然,这个位置是用户进程可读但是不可写的)。在Intel CPU中,这个由中断

0x80

实现。硬件知道一旦你跳到这个位置,你就不是在限制模式下运行的用户,而是作为操作系统的内核–所以你就可以为所欲为。

进程可以跳转到的内核位置叫做sysem_call。这个过程检查系统调用号,这个号码告诉内核进程请求哪种服务。然后,它查看系统调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等返回后,做一些系统检查,最后返回到进程(或到其他进程,如果这个进程时间用尽)。如果你希望读这段代码,它在<内核源码目录>/kernel/entry.S,Entry(system_call)的下一行。

对比结果相同

================================================

Linux 中 x86 的内联汇编 系统调用跟我学(1)
Linux系统调用列表
【linux系统编程】 Linux系统调用概述

> sunfy(与最后申请证书的姓名务必一致) + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

实验:跟踪分析Linux内核的启动过程

——————————-实验开始gdb————————————————-

进入gdb进行一步一步调试:

break start_kernel:

start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。 这些动作有的是公共的,有的则是需要配置的才会执行的。

在start_kernel()函数中,

  • 输出Linux版本信息(printk(linux_banner))

  • 设置与体系结构相关的环境(setup_arch())

  • 页表结构初始化(paging_init())

  • 使用"arch/alpha/kernel/entry.S"中的入口点设置系统自陷入口(trap_init())

  • 使用alpha_mv结构和entry.S入口初始化系统IRQ(init_IRQ())

  • 核心进程调度器初始化(包括初始化几个缺省的Bottom-half,sched_init())

  • 时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等,time_init())

  • 提取并分析核心启动参数(从环境变量中读取参数,设置相应标志位等待处理,(parse_options())

  • 控制台初始化(为输出信息而先于PCI初始化,console_init())

  • 剖析器数据结构初始化(prof_buffer和prof_len变量)

  • 核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())

  • 延迟校准(获得时钟jiffies与CPU主频ticks的延迟,calibrate_delay())

  • 内存初始化(设置内存上下界和页表项初始值,mem_init())

  • 创建和设置内部及通用cache(“slab_cache”,kmem_cache_sizes_init())

  • 创建uid taskcount SLAB cache(“uid_cache”,uidcache_init())

  • 创建文件cache(“files_cache”,filescache_init())

  • 创建目录cache(“dentry_cache”,dcache_init())

  • 创建与虚存相关的cache(“vm_area_struct”,“mm_struct”,vma_init())

  • 块设备读写缓冲区初始化(同时创建"buffer_head"cache用户加速访问,buffer_init())

  • 创建页cache(内存页hash表初始化,page_cache_init())

  • 创建信号队列cache(“signal_queue”,signals_init())

  • 初始化内存inode表(inode_init())

  • 创建内存文件描述符表(“filp_cache”,file_table_init())

  • 检查体系结构漏洞(对于alpha,此函数为空,check_bugs())

  • SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核,此函数为空,smp_init())

  • 启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle() 等待调度,init())

至此start_kernel()结束,基本的核心环境已经建立起来了。

【转)kernel 启动过程之三, start_kernel()函数 概叙】

break rest_init:

break kernel_init:

break run_init_process

启动完成:

================================================

Linux系统启动过程

关于linux系统的启动流程我们可以按步进行划分为如下:

POST加电自检–>BIOS(Boot Sequence)–>加载对应引导上的MBR(bootloader)–>主引导设置加载其BootLoader–>Kernel初始化–>initrd—>/etc/init进程加载/etc/inittab



详解linux系统的启动过程及系统初始化



Linux启动过程详解-《别怕Linux编程》之八

===============================================

sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

实验:完成一个简单的时间片轮转多道程序内核代码

本次实现是实验模拟操作系统是如何工作的。

mykernel

上图作证:


实验使用linux-3.9.4进行模拟。模拟操作系统如何进行进程管理以及中断处理。

Process Control Block 即PCB(进程控制块)。也称之为 Task Controlling Block。

查阅

WIKI

The role of the PCBs is central in process management: they are accessed and/or modified by most OS utilities, including those involved with scheduling, memory and I/O resource access and performance monitoring. It can be said that the set of the PCBs define the current state of the operating system. Data structuring for processes is often done in terms of PCBs. For example, pointers to other PCBs inside a PCB allow the creation of those queues of processes in various scheduling states (“ready”, “blocked”, etc.) that we previously mentioned.

可见PCB由操作系统创建和管理。同时PCB包含了足够充分的信息,这样就可以中断一个进程,并且在恢复执行该进程时就好像未被中断过一样。(《Operating Systems Internals and Design Principles》)

在mykernel中mypcb.h既是声明PCB数据结构头的文件。


PCB中定义了pid,state,stack,thread,task_entry,*next结构成员。

在定义了PCB类型的数据结构后,定义了实例变量tPCB.

在声明了PCB数据结构类型之后,Linux内核开始运作。

相关代码在mymain.c中。


该段代码在创建和管理PCB,也就是为进程设置足够充分的信息并保留下来,以便操作系统进行进程控制管理。
这是创建的一个进程。该进程是每隔1000万个时间单位打印出特定格式的信息。

为了实现多进程时间片轮转,还需要一个中断进程,不停的发出中断信号。这就是myinterrupt.c代码所执行的内容。


这就是当满足调剂时执行的中断程序。

这就是本次实验的主干内容。

可以说进程是由程序代码和相关数据还有PCB组成的。对于一个但处理器计算机,在任何时候最多只有一个进程在执行。而在运行的这个进程状态为运行态。

  • sunfy + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000