第四步 Linux初步学习内核

来源:互联网 发布:网络直播紫菱真实姓名 编辑:程序博客网 时间:2024/05/16 15:09

局域网络共享:http://blog.sina.com.cn/s/blog_7a85b6e70100ri7a.html
虚拟机网络设置:http://blog.sina.com.cn/s/blog_601bc5cf0101d32z.html

help:帮助
printenv:查看环境变量
setenv: 添加、修改、删除环境变量
setenv filename test.txt  添加变量filename
setenv filename hello.txt 修改环境变量值为hello.txt
setenv filename   删除环境变量filenames

saveenv : 保存环境变量

tftp通过网络下载文件(注意:使用tftp,需先配置好网络)
setenv ethaddr xx:xx....
setenv ipaddr xx.xx.xx.xx
setenv serverip xx.xx.xx.xx(tftp服务器地址)(Linux的IP地址)
ping 服务器地址 (xx  is alive 说明成功)
vim /etc/xinetd.d/tftp 修改配置(disable=no)
service tftp restart
setenforce 0

文件下载范例:
tftp 0x50008000 uImage.bin
(把tftp服务器的uImage.bin下载到0x50008000处)

bootm {addr} {arg} 执行固定格式的二进制程序
bootm 0x50008000

md addr内存显示(md.b)
mm addr 内存修改命令
nand erase 0x40000000 0x50000000(擦除nand flash 从0x4... 到0x5..的内容)
nand write 内存起始地址 flash起始地址 长度 (写nand flash)
nand read 内存起始地址 flash起始地址 长度
例:nand write 0xc0008000 0x400000 500000

设置自启动:
1.设置从nand flash启动(设置后保存)
setenv bootcmd nand read c0008000 400000 500000 \; bootm c0008000
2.设置自动下载内核到内存后启动
setenv tftp c0008000 uImage.bin \; bootm c0008000

Linux体系结构:
1.user space:用户程序和C库(硬盘、flash中)
2.kernel space:系统调用接口、内核代码等(内存中)
CPU不同工作模式权限不一样,通过系统调用和硬件中断能够
完成从用户空间到内核空间的切换

Linux内核架构:7模块
system call interforce(SCI)
process management(PM)
memory management(MM)
arch
virtual file System (VFS)
Network Stack
Device Drivers(DD)

目录结构:
arch目录:结构体系相关代码
documentation:内核的文档
drivers:驱动
fs:文件系统
net:网络协议实现代码
下载内核源代码:www.kernel.org

下载系统到开发板所须文件: bootloader、内核映象、文件印象

一.Linux内核配置方法:
  make config:基于文本模式的交互式配置
  make menuconfig:基于文本模式的菜单型配置(redhat下出错
 可能没安装包ncurses devel)
  [*] : 内核映象 (必要功能)直接加入内存运行
  <M> : 内核模块 (偶尔调用的功能)需要时才加入内存里面运行
  < > : 不选择该功能

二.内核编译:
  1.编译内核([*]部分) [1]. make zImage [2]. make bzImage V=1
  2.编译内核模块(<M>部分)
 1>. make modules (编译、时间很长)
 2>. make modules_install

  3.制作init ramdisk(打包)
   方法:mkinitrd initrd-$version $version
 $servion可以通过查询/lib/modules下的目录得到(uname -r)
   例:mkinitrd initrd-2.6.39 2.6.39

三.安装内核:复制内核文件到/boot/
   1.cp linux-2.6.39/arch/x86/boot/bzImage /boot/vmlinuz-$version
   2. cp initrd-$version /boot/
   3. 修改/etc/grub.conf (系统启动时的选项)
      文件最后几行 (title Red Hat...开始)
      复制,改所有的2.6.32.xxx(原版本)为2.6.39(新版本)
   4.重启Linux

四.清理内核: make clean(只清理*.o文件)
      make distclean (清除*.o和.config文件)


下载内核到开发板:
1.make distclean
2.配置内核 make menuconfig ARCH=arm
3.编译内核 make uImage ARCH=arm CROSS_COMPILE=arm-linux-
(若报错拷贝u-boot/tools/mkimage到/bin/)
4.下载u-boot.bin

根文件系统创建:
  1.创建目录 mkdir rootfs
 cd rootfs
 mkdir bin dev lib proc sbin ysy usr mnt tmp var
 mkdir usr/bin usr/sbin lib/modues

  1.2 创建设备文件
  cd /rootfs/dev
 mknod -m 666 console c 5 1
 mknod -m 666 null c 1 3
  1.3 加入配置文件
 tar etc.tar.gz
 mv etc/* .../rootfs/etc -rf
  1.4 添加内核模块
 cd .../linux(内核文件目录,之前复制好的)
 make modules ARCH=arm CROSS_COMPILE=arm-linux-
 make modules_install ARCH=arm INSTALL_MOD_PATH=.../rootfs
 (编译在rootfs/lib/module)
  1.5 安装busybox
    1.配置busybox  make menuconfig
   busybox settings -> build options ->
 选中“build busybox as a static binary”静态链接
 cross compiler prefix (arm-linux-)
 
 installation options->
 选中"don't use/usr"
 选中该项可以避免busybox被安装到宿主系统的/usr目录下,破坏宿主系统

 busybox installation prefix(/xxxx/rootfs)
 该选项表明编译后的busybox的安装位置
    2.编译busybox
 make

uImage中包含内核和文件系统压缩包两部分

挂载根文件系统:(根据硬件和系统需求决定)
  1.文件系统类型
   1.基于Nandflash的文件系统 (1)Yaffs2 (2)UbiFS(读)
 2.基于NorFlash的文件系统 Jffs2 (可读可写)
 3.基于内存的文件系统(1)Ramdisk (2)Initramfs(启动速度快)
 4基于网络的文件系统 NFS (用于开发阶段)
  2.使用Initramfs挂载文件系统
 1.cd ..../rootfs/
   ln -s ./bin/busybox init
 2.配置Linux内核,支持initramfs
   cd ../../linux(内核文件夹)
   make menuconfig ARCH=arm
   (找到initial RAM filesysten...改路径为../rootfs)
 3.重新编译内核
   make uImage ARCH=arm CROSS_COMPILE=arm-linux-
 4.设置环境变量
   setenv bootargs noinitrd console=ttySAC0,115200
   5.下载uImage到开发板
  2.使用NFS挂载文件系统(添加更改文件不需重新下载)
 1.重新配置内核 make menuconfig
   1.取消initial RAM filesysten...
   2.找到filesystem -> network file system ->
     选中root file system on NFS
   3.编译make menuconfig ARCH=arm
 2.参数配置
   setenv bootargs noinitrd
     console=ttySAC0,115200 init=/init
     root=/dev/nfs rw
     nfsroot=192.168.1.3(linux IP):/...../rootfs
     ip=192.168.1.6(开发板IP):192.168.1.3(linux IP):
  192.168.1.1(网关):255.255.255.0(掩码)::eth0:off
 3.下载内核 tftp c0008000 uImage
 4.重启 bootm c0008000

内核模块本身并不被编译进内核文件(zImage或bzImage)
根据需要动态的安装和卸载

安装: insmod /home/dnw_usb.ko
卸载: rmmod dnw_usb
查看: lsmod(查看系统中所有的内核模块)

内核代码:头文件:linux/init.h  linux/module.h
 加载函数:module_init(xx);(入口)
 卸载函数:module_exit(xx);

内核printk打印级别:
http://www.educity.cn/linux/1582745.html

Makefile编写思路:
1.指明所编译内核模块最终生成文件
2.内核代码路径
3.目标 :编译内核模块命令

复制编译后*.ko文件到rootfs目录(tftp下载)下载
imsmod安装

模块申明:
MODULE_LICENSE("遵守的协议")
申明改模块遵守的许可证协议,如:"GPL","GPL v2"等
MODULE_AUTHOR("作者")
MODULE_DESCRIPTION("模块功能描述")
MODULE_VERSION("V1.0") 
模块参数:用于在加载模块是传递参数给模块
宏 module_param(name,type,perm)
name: 变量名称
type:变量类型,bool:布尔型int:整形charp:字符串行
perm:访问权限  S_TRUGO读权限  S_IWUSR写权限
例:
int a=1;
char *st;
module_param(a,int,S_IRUGO);
module_param(st,charp,S_IRUGO);
内核符号导出:
EXPORT_SYMBOL(符号名)符号可为函数名、变量名等
EXPORT_SYMBOL_GPL(符号名)(只用于包含GPL许可证模块)

linux内存管理子系统:1.物理内存分配 2.地址映射
  虚拟地址映射:(转换过程:页目录+页表过度)
 固定映射的线性地址区、(寄存器等)
 永久内核映射区
 vmalloc区、(固定访问高级内存区)
 直接映射区(虚拟地址=3G+物理地址)
  物理内存分配:(只有访问虚拟地址时才分配实在的内存)

进程管理子系统:
  程序:存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体
  进程:是一个执行中的程序,他是动态的实体
  进程四要素:
 1.有一段程序供执行
 2.专用的内核空间栈
 3.有一个task_struct数据结构(进程控制块)用于接受内核调用
 4.有独立的用户空间
 (用户线程:只有共享空间,内核线程:共享用户空间都没有)
  进程状态:
    1.TASK_RUNNING 就绪态(进程刚被创建时)
    2.TASK_INTERRUPTIBLE 阻塞态(等待条件成熟)
    3.TASK_UNINTERRUPTIBLE 阻塞态(不可由其他信号或中断唤醒)
    4.TASK_KILLABLE Linux进入新的睡眠状态(可被SIGKILL唤醒)
    5.TASK_TRACED 正在被调试状态
    6.TASK_DEAD 进程退出状态(调用do_exit)
  进程描述:
    在Linux内核代码中,线程、进程都是用结构task_struct(sched.h)
    来表示,它包含大量描述进程、线程的信息,比较重要的有:
    pid_t pid;(进程号)long state (进程状态)int prio(优先级)

  进程调度:
    1.调度策略:SCHED_FIFO陷入先出实时进程
      SCHED_RR时间片轮转实时进程
  SCHED_NORMAL(SCHED_OTHER)普通分时进程
  SCHED_BATCH批处理进程
  SCHED_IDLE只有在系统空闲才被调用的进程
    2.调度时机:(schedule()函数什么时候被调用)
      主动式:内核中直接调用schedule,当进程需要等待资源而暂停运行时
 会把自己的状态置于挂起(睡眠),并主动请求调度,让出CPU
 范例: current->state=TASK_INTERRUPTIBLE;
              schedule();
 被动式:抢占式调度(分用户态抢占和内核态抢占)
   用户态:从系统调用返回用户空间或中断处理程序返回时
 (内核即将返回用户空间时,如果need_resched标志被设置
  会导致schedule被调用,发生用户抢占),当某个进程耗尽时间片时,
  或者当一个优先级更高的进程进入可执行态的时候,会设置need_resched
 内核态抢占:中断处理程序完成,返回内核空间
  获取信号量再释放信号量等如解锁
    在支持内核抢占的系统中,某些特例下是不允许抢占的:
 v 内核正在运行中断处理。
 v 内核正在进行中断上下文的Bottom Half(中断的底半部)处理。
 硬件中断返回前会执行软中断,此时仍然处于中断上下文中。
 v 进程正持有spinlock自旋锁、writelock/readlock读写锁等,
 当持有这些锁时,不应该被抢占,否则由于抢占将可能导致其
 他进程长期得不到锁,而让系统处于死锁状态。
 v 内核正在执行调度程序Scheduler。抢占的原因就是为了进行
 新的调度,没有理由将调度程序抢占掉再运行调度程序。

    调度时机-抢占计数:
 为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量
 preempt_count,称为内核抢占计数。这一变量被设置在进程的thread_info
 结构中。每当内核要进入以上几种状态时,变量中。每当内核要进入以上几种状
 态时,变量preempt_count就加1,指示内核不允许抢占。每当内核从以上几种状
 态退出时,变量preempt_count就减1,同时进行可抢占的
 判断与调度。
    调度步骤 :
Schedule函数工作流程如下:
 1). 清理当前运行中的进程;
 2). 选择下一个要运行的进程;
 3). 设置新进程的运行环境;
 4). 进程上下文切换 。
 
内核链表:(双向循环链表)
  传统链表:指针指向节点的数据域
  内核链表:指针指向节点指针域
  内核链表-结构:
  struct list_head
  {
  struct list_head *next, *prev;
  };
  内核链表-函数:(使用参考已有内核代码)
 1. INIT_LIST_HEAD:创建链表
 2. list_add:在链表头插入节点
 3. list_add_tail:在链表尾插入节点
 4. list_del
 4. list_del:删除节点
 5. list_entry:取出节点
 6. list_for_each:遍历链表

系统调用:用户程序通过触发软中断,通过swi变量调用内核中相应函数
添加新的系统调用:
 kernel/printk.c中写入自己的函数
 arch/arm/include/asm/unistd.h添加新编号
 arch/arm/kernel/call.S最后中添加新标号
 编译内核make uImage ARCH=arm CROSS_COMPILE=arm-Linux-
 编写应用代码调用新添加的函数
 如:
 void pk()
 {
     __asm__ (
     "ldr r7,=363 \n"
     "swi \n"
     :
     :
     :"memory");
 }
 
 void main()
 {
     pk(); 
 }

0 0
原创粉丝点击