Linux学习心得

一、首先我分享一下我对Linux学习过程的几篇博客:
1. 通过反编译一个简单的C程序,解释堆栈的变化
2. 一个精简的linux内核分析
3. 跟踪分析一个简单的linux内核的运行
4. 分析一个API的调用机制
5. 分析Linux内核创建一个新进程的过程
6. Linux内核如何装载和启动一个可执行程序
7. 理解进程调度时机跟踪分析进程调度与进程切换的过程

二、我对Linux的理解
1、Linux下使用的汇编和windows下的汇编是不太一样的。首先,windows的运算结果都是存在左边的寄存器中,而Linux下汇编的运算结果是存储在右边的寄存器中的。其次,关于寻址的表示方法上也有一些不同:
这里写图片描述
这里末尾的b,w,l,q分别代表8位,16位,32位和64位;%代表寄存器;$代表数值;
call指令相当于:

 pushl eip
 movl f,eip       (f为跳转地址)

ret指令相当于:

popl eip(*) //*表示用户是无法进行这样的操作的

2、在调用函数时,函数的参数传递是从右至左一次传递的。所以要从右至左依次进行push,等参数传递完毕之后进行call操作。系统调用号的参数传递是通过eax来进行传递的。

3、在进入一个函数调用之后需要为函数开辟一个新的堆栈空间。具体操作如下:

push %ebp
mov  %esp,%ebp

在离开调用函数之前,需要将这块堆栈空间释放。具体操作如下:

mov  %ebp,%esp
popl %ebp

4、学会在C程序中嵌入汇编代码,将会使我们的功能更加灵活。具体例子如下:
这里写图片描述
需要注意的就是output和input部分的编号是从0依次增大的。

5、用户态和内核态:
Linux中,使用0级表示内核态;3级表示用户态。
cs寄存器的最低两位表明了当前代码的特权级;
0xc0000000以上的地址空间只能在内核态访问,0x00000000—-0xbfffffff的地址空间在两种状态下都可以访问。

6、使用gdb对程序进行跟踪调试是一个十分重要的程序分析手段,我们可以通过跟踪程序的运行过程来搞清楚程序的运行机制。下面我们演示一下dgb的使用方式:

# 启动MenuOS系统
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

# 使用gdb跟踪调试内核
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:(这个是在LinuxKernel目录下执行的)
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

另开一个shell窗口

gdb
(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

7、进程的创建:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
    int pid;
    /* fork another process */
    pid = fork();
    if (pid < 0) 
    { 
        /* error occurred */
        fprintf(stderr,"Fork Failed!");
        exit(-1);
    } 
    else if (pid == 0) 
    {
        /* child process */
        printf("This is Child Process!\n");
    } 
    else 
    {  
        /* parent process  */
        printf("This is Parent Process!\n");
        /* parent will wait for the child to complete*/
        wait(NULL);
        printf("Child Complete!\n");
    }
}

fork()的调用是比较特殊的,一次调用两次返回。一次返回是父进程的返回,还有一次是子进程的返回。子进程返回后的执行位置是ret_from_fork。

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

9、进程的切换
1)为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
2)挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
3)进程上下文包含了进程执行需要的所有信息
(1)用户地址空间:包括程序代码,数据,用户堆栈等
(2)控制信息:进程描述符,内核堆栈等
(3)硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
4)schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换

二、总结
这里总结的一些心得只是我Linux学习过程中的一部分,只起到一个总领的作用,具体内容的学习还是要要进入相关的博客正文进行学习。相关博客已经在最前面给出了链接,需要使用时请到对应的博客进行查看。
这次的学习收获很多,尤其是系统调用和进程的切换这一块。以前对用户态和内核态完全没有任何概念,现在通过对中断的学习,对内核态有了一个基本的认识。
但是这次的学习还是不足,对Linux内核的整体工作机制还是不是很理解,对知识的掌握比较片面,不能将这些知识点串联起来。接下来要做的就是对这些知识点进行一下梳理,把零散的知识点连成一片。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值