Linux内核(Day21)

一 回忆昨天的内容
    
    移植Linux内核,阅读Linux内核源码
    make V=1 
    head.S 
    linux内核的特点
    linux内核的7大子系统(功能)
    linux内核的配置
    make x6818_defconfig 
    cp arch/arm/configs/x6818_defconfig .config
    linux内核的编译
    make menuconfig 
    make config ---> 不推荐
    
    流程: 启动uboot --> 硬件的初始化 --> 
    执行bootcmd中的命令 --> 加载linux内核 --->
    检查cpuid是否支持 ---> 创建页表 --> 开启mmu ---> 
    创建内核线程 ---> 执行bootargs 中的命令 ---> 
    执行1号进程 --> 执行后续的进程 ---> 启动shell ---> 
    解析用户输入的命令 
    
二 掌握大名鼎鼎的NFS网络服务
    
    2.0 rootfs_ext4.img  ---> emmc 
    明确:各种应用程序属于根文件系统的一部分
    如果将来要更新一个应用程序,需要将它放在
    rootfs_ext4.img中
    开发效率低,关键emmc的寿命就变短了。
    
    采用NFS网络服务, 就是在上位机上部署一个根文件系统
    rootfs,将来各种应用程序放在上位机上进行交叉编译
    让下位机的linux内核到上位机找根文件系统
    一旦找到,下位机就可以访问上位机中的各种应用程序了,
    开发效率就会大大提高
    
    2.1 具体实施步骤:
        2.1.1获取最干净的内核:
        rm kernel -rf 
        tar xf kernel.tar.bz2
        cd kernel
        2.1.2 如果是新板子, 老板子直接忽略这一步 
        需要网卡打一个补丁
        cp /mnt/hgfs/easthome_porting/yt8511_support.patch ./
        patch -p1 < yt8511_support.patch
        
        2.1.3 配置ip:
        下位机:192.168.1.6
         ifconfig eth0 192.168.1.6
        上位机:192.168.1.8
        拿网线连接开发板和PC机,
        需要桥接到网线所连接的网卡上
        
        2.1.3 编译内核源码 
        make distclean
        make x6818_defconfig
        make uImage
        cp uImage /mnt/hgfs/easthome_porting/fastboot
        通过OTG_USB fabootboot 烧录uImage到emmc中
        cp arch/arm/boot/uImage /mnt/hgfs/easthome_porting/fastboot
        烧录
        在uboot的命令行执行:
        fastboot 
        win:fastboot flash boot uImage
        
        2.1.4 上位机中需要安装NFS的服务
        注意:提供虚拟机中已经安装过
        sudo apt install nfs-kernel-server
        上位机需要指定NFS服务一个共享目录
        sudo vi /etc/exports 
        /opt/rootfs  *(rw,sync,no_root_squash)
        
        2.1.5 上位机获取根文件系统
        sudo cp /mnt/hgfs/easthome_porting/rootfs_qt.tar.bz2 /opt
        tar xf rootfs_qt.tar.bz2
        
        2.1.6 重启上位机的nfs服务
        sudo service nfs-kernel-server restart 
        
        2.1.7 在上位机的rootfs中准备一个测试程序
        vi helloworld.c
        
        2.1.8 重启下位机,修改bootargs
         setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs ip=192.168.1.6:
         192.168.1.8:192.168.1.1:255.255.255.0 
         init=/linuxrc console=ttySAC0,115200
         tp=gslx680-linux lcd=wy070ml maxcpus=1
        
        2.1.9 重启下位机,静静等待下位机的linux系统启动
        cd /home
        ./hello 
        
        注意:切记下位机的"/" 就是上位机的/opt/rootfs
        
三 Linux内核态编程
    
    3.1 内核态编程的特点
        1) 内核中所有的函数都是linux内核自己实现的
        2) 内核源码使用的是GNU C
                GNU C 是标准C的扩展版
        3) 更多关注的是执行的效率 以及可移植性问题
    
    3.2 内核态编程时,需要注意的问题
        1) 内核编程时使用的虚地址是0~4G
        2) 内核中不允许做浮点运算
        3) 每个线程对应独立的栈空间
                每一个线程都有两个栈,
                一个是内核态的栈,一个用户态的栈
                
    3.3 编写独立的ko文件
    
        做内核态的开发相当于英文中完形填空
        做裸板开发相当于英文中的作文
        vi hello.c 
        vi Makefile 
        //注意:all: 后面加tab,千万不要加空格
        make 
        生成hello.ko 
        cp hello.ko /opt/rootfs
        
        下位机中执行:insmod hello.ko //安装模块
        [root@easthome-ly:/]# insmod hello.ko 
        [  338.369000] hello world!
        rmmod hello //卸载模块
        [root@easthome-ly:/]# rmmod hello
        [  423.355000] bye bye!
        
        lsmod 查看系统中已安装的模块
        insmod xxxx.ko 安装模块
        rmmod xxxx 卸载模块
        
    3.4 导出符号
        它是解决模块与模块之间的函数调用的问题
        应用编程:
        a.c 
            int add (int x, int y)
            {
                return x+y;
            }            
        a.h
            extern int add (int x,int y);        
        b.c 
         #include "a.h"
         ret = add (a,b);
            
        内核编程:
        a.c 
        int add (int x,int y)
        {
            return x+y;
        }
        EXPORT_SYMBOL (add);
        
        a.h 
        extern int add (int x, int y);
        
        b.c
        #include "a.h"
        res = add (a, b);
        
        vi export.c import.c Makefile
        注意安装模块的顺序:
        insmod export.ko 
        insmod import.ko
        卸载的顺序:
        rmmod import.ko
        rmmod export.ko 
    
    3.5 关于 printk     
        printk 的标准用法:
        printk ("<0/1/2/3/4/5/6/7>" "res = %d\n", res);
        
        特殊情况:
        printk ("res = %d\n", res);
        默认的打印优先级 是4 
        
        linux内核将打印优先级设置为8级
        值越小打印优先级越高
        
        printk 输出的信息先输出到内核维护的输出缓冲区
        输出缓冲区中的内容能不能打印到屏幕上是有限制的
        printk 调用时使用的优先级 > 优先级的阈值 
        能够输出到屏幕
        
        实际项目中有debug 和 release 
        一般是使用宏来控制
        
        proc,是基于内存的虚拟文件系统
            板子上的proc和pc上的proc目录不会做同步
            用于向用户空间输出内核空间执行的状态
            
            cat /proc/进程id/maps
            cat /proc/cpuinfo
            cat /proc/meminfo
            cat /proc/sys/kernel/printk
            上位机:
            cat /proc/sys/kernel/printk
            4                4                1                7
            下位机:
            cat /proc/sys/kernel/printk
            3       4       1       7
            
            第一个值:优先级的阈值开关
                优先级高于该值的printk的信息可以输出到终端
            第二个值:系统默认的打印优先级
                printk ("res = %d\n",res) 
            
    3.6 模块参数
        用户态:
            ./a.out xxx yyy zzz 
            int main (int argc, char *argv[]);
            
        内核态:
            安装模块时给其传递参数
        
        内核中模块参数的使用步骤:
            1) 定义一个全局变量
            2) 通过特定的宏将其声明为模块参数
                module_param (name, type, perm)
                    name, 要声明为模块参数的变量
                    type, 变量的类型
                                int long short charp (char *)
                    perm, 权限 
                module_param_array (name, type, nump, perm)
                    nump, 数组元素个数指针
            
            3) 使用模块参数:
             insmod xxx.ko                
             ls /sys/module/moduleparam/parameters 
             //是个临时文件
             发现其中没有str,因为str权限是0
             神奇的是,我们在用户空间更改了内核空间的值!
             echo 10 >/sys/module/moduleparam/parameters/irq 
             
            4) 模块参数的使用场景
                驱动代码的调试
                    VAR声明为参数
                    REG=VAR
                    
    3.7 谈谈对系统调用的理解:
        3.7.1 系统调用的意义:
            是用户空间调用内核空间函数的一种方式
            由用户态进入内核态的一种方式
            注:由用户态进入内核态的另一种方式是中断
            
            保证了操作系统的安全性
                允许应用程序调用内核中的函数
                以安全的方式调用
                
        3.7.2 系统调用是如何实现的?
            每一个系统调用都有一个系统调用号
            open ---> 5
            close ---> 6
            应用程序首先使用适当的值去填充寄存器
            然后调用特殊的指令
            执行该指令 导致跳转到内核的某一个位置去执行
            在该位置根据填充到寄存器中的值
            来找到内核中对应的函数 并调用该函数
            函数执行完之后 原路返回到用户空间
            
            //实现用户空间和内核空间的交互
            
            open ("a.txt", O_RDWR);
            vi arch/arm/include/asm/unistd.h 
            arm 中,这个所填充的寄存器是 r7
            arm 中,这个特殊的指令是 swi 
            x86                                             int
            固定的位置指的是:
            软中断异常 跳转到异常向量表中
            在跳转异常处理函数
            
            arch/arm/kernel/entry-common.S
            addne   scno, r7, #__NR_SYSCALL_BASE
            ENTRY(vector_swi)
                //Get the system call number. 获取系统调用号
                adr     tbl, sys_call_table 
                sys_call_table [r7]
                sys_call_table 位于calls.S中 
        
            尝试实现一个自己的系统调用,
            完成内核和用户空间的交互。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值