S3C2440开发板学习笔记
1.Nor flash启动or Nand flash启动
第一,SDRAM 只能用来做内存,它就是为了做内存而生的。
第二,不管是 nor flash还是nand flash, 都是为了存储数据而生的,怎能用来做内存。
norflash 读取快,写入慢,总线结构,能运行代码,价格贵。 nandflash 读取慢,写入快,非总线结构,不能运行代码,价格便宜。 sdram 读取和写入都很快,掉电不能保存数据,价格贵。 1,SDRAM+nand flash 是因为SDRAM快,nand flash 便宜,现在最好的搭配。 2,nor flash + nand flash nor fash慢,nand flash便宜,估计没哪个公司会用。 3,SDRAM + nor flash SDRAM快,norflash使用比nand flash简单。
sdram是掉电就没了的,相当于PC的内存条
nor flash是可以存储少量代码的,很多SOC启动代码就存在nor flash里面(以前多用EEPROM)
nand flash一般相当于硬盘,密度高,存储量大
arm:启动代码判断是从nand启动还是从norflash启动,拷贝程序到内存的过程
一、nand启动和nor启动:[1]
CPU从0x00000000位置开始运行程序。
1、nand启动:
如果将S3C2440配置成从NANDFLASH启动(将开发板的启动开关拔到nand端,此时OM0管脚拉低)S3C2440的Nand控制器会自动把Nandflash中的前4K代码数据搬到内部SRAM中(地址为0x40000000),同时还把这块SRAM地址映射到了0x00000000地址。CPU从0x00000000位置开始运行程序。
2、如果将S3C2440配置成从Norflash启动(将开发的启动开关拔到nor端,此时OM0管脚拉高),0x00000000就是norflash实际的起始地址,norflash中的程序就从这里开始运行,不涉及到数据拷贝和地址映射
3、总结:
nand启动时,地址0x00000000为内部SRAM映射的地址;
nor启动时,地址0x00000000为norflash的实际起始地址。
向Norflash中写数据需要特定的命令时序,而向内存中写数据可以直接向内存地址赋值。
用MMU来访问速度快好多,比如LED点亮实验
用SDRAM来访问速度比SRAM慢一些;
开机启动时,NADN<4K将由硬件自动拷贝到片内的SRAM内存,若超过4K则前4K拷贝到SRAM内存的同时还负责将其他NANDFLASH上的程序拷贝到SDRAM上;
NANDflash没有地址总线,原理图上看
NADNflash K9F2G08X0M 每页2K+64字节(ooB),一块128K共64页
- 从硬件止,访问NAND
- 命令 CLE放到DATABUS →寄存器NFCMMD
- 地址,ALE放到DATABUS → NFADDR
- 传输数据 放到DATABUS,→ NFDATA
- 状态 NFSATA
- 写时必须要先擦除
NAND 4K放到SRAM(head.s,init.c);4096后将main放到SDRAM内存0X30000000
2.结构体指针学习
结构体指针 :eg:
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFEBLK;
….
} S3C2440_NAND;
/* 发出命令 */
static void s3c2410_write_cmd(int cmd)
{
volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD;
*p = cmd;
}
3.中断体系
中断体系结构,有7种中断 模式,中断是一种异常,发生异常时,PC=异常入口(固定地址)
中断控制器
- 不同的寄存器,不同的控制模式有自己的部分寄存器,更快速处理事件
- 不同的权限 Usr用户不能访问某些寄存器
- 触发条件
中断模式自己有自己的栈
发生中断:
硬件:
- 进入IRQ模式,切换Reg ,栈
- PC=IRQ入口地址
- b handle IQR:计算返回地址
b保存现场
c调用处理程序
d 恢复“被中断”程序
中断程序:
` a分辨中断源INTOFFSET
b处理
c 清除:清中断
4.系统时钟
开发板 CPU 400M FCLK
内存 SDRAM 100M~133M HCLK
外设:UART TIMER 50M PCLK
晶振 12M 利用PLL进行倍频等
soc (system on chips)包括CPU UART IIIC 存储管理器 片上外设等;
PLL 倍频:锁定时间,设置寄存器
bl指令是位置无关码,相当于PCnew=pc+偏移
PCnew=(当前地址+8)+偏移0x28(由系统自己计算)
PCnew=当前PC+8+0x28
uartf时用的memsetup不同?????
LCD实验:
16bpp:2440开发板LCD framebuffer→LCD控制器→LCD
8bpp:2440开发板LCDframebuffer→调色板→LCD控制器→LCD
5.u-boot分析
uboot最终目的:
从FLASH读出内核放到SDRAM;
启动内核
u-boot要实现的功能:(复杂点的单片机程序 )
①读写FLASH
- 硬件相关初始化:初始化SDRAM、初始化时钟,初始化串口,关看门狗
- 启动内核
- 额外增加的功能:串口、网卡、USB、烧写FLASH
分析编译:
- 第1个文件 cpu/arm920t/start.s
- 链接地址: board/100ask2 0x33f80000
uboot第一阶段:start.s(cpu/arm920t)
a设svc模式
b 关看门狗
c屏蔽中断
d 初始化SDRAM
e 设置栈
f时钟
g 重定位代码flash→SDRAM
H 清bss段
i调用c函数 start_armboot
uboot 第二阶段:
uboot目标:从flash上读出内核;从0地址启动内核
为开发方便,又使得要读写FLASH,那就得分两种情况:
nor flash :flash inti函数 ;
nand flash: nand init
u-boot:启动内核
①从flash上读出内核 → nand read.jffs 0x30007fc0 kernel
- 启动内核 bootm 0x30007FC0;
该板的开发板的控制界面菜单函数 : run_command(“manu”,0);
命令:
- 启动内核 :
s=getenv(“bootcmd”); run_command(s,)
- u-boot界面:
readline(读入串口的数据);
run_command;
u-boot的核心是命令;
u-boot命令是通过以下运作:
①输入命令字符串→name
②动作→函数
cmd_tbl_t实际上定义了命令结构体,其中包含了name和funtion
command.h里
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
nand read.jffs2 0x30007fc0 kernel;
从NAND读出内核:从哪里读,从kernel分区
放到时哪里去?——0x30007FC0
nand read.jffs2 0x30007FC0 0x00060000 0x00200000 起始地址60000,大小为2M jffs2可以不用页对齐 在Cmd_nand.c里面nand_read_opts 命令启动do_bootm
uimage: 头部(64字节)+真正的内核
头部 image_header eg: in_load 加载地址 内核运行要放在哪里
in_ep:入口地址,要运行内核时跳转到该入口地址
这里内核的加载地址为0x30008000,与0x30007FC0刚好相差64字节,无需移动内核,加快启动速度
bootm:
- 根据头部将内核到合适的地方;
- 启动函数 do_bootm_linux:
- u-boot设置启动参数;在某个地址按某种格式保存数据30000100 setup_start_tag setup_memory_tags
- 跳到入口地址启动内核 theKenel
theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 第二个参数单板机器码,第三个是启动设置的参数 后面一去不返回
6.内核
内核功能、结构,结合Makefile、Kconfig进行分析
解压缩;打补丁
配置:a,make menuconfig
b 使用默认配置,在上面修改 在arch/arm/configs
找到并make s3c2410_defconfig →.config
make menuconfig
c使用厂家提供的配置文件 cp 厂家的配置文件 .config
make menuconfig
配置结果 生成.config
eg:CONFIG_DM9000
- C源码: CONFIG_DM9000
- 子目录Makefile drviers/net/makefile
obj-$(CONFIG_DM9000) += dm9dev9000c.o
obj_y+=xxx,o 编译成内核 ,若obj_m+=xxx.o则编译进模块
- incule/config/auto.conf(来源于.config)配置CONFIG_DM9000=y 为②所调用
- inlcue/liux/autoconf.h(来源于.config) 在这里定义#define CONFIG_DM9000
make uImage时
- config→autoconf.h
- config→auto.conf
内核配置:
分析arch/arm下的Makefile和顶层的Makefile找出目标相关性
分析Makefile: 找到第一个文件,arch/arm/kernel/head.s
链接脚本 arch/arm/kernel/vmlinux.lds
编译成模块可读document和各子目录下的Makefile:
obj –m+=ab.o
ab – objs=a.o b.o→ a.c→a.o
b.c→b.o
①子目录下的Makefile
obj-y +=…. obj –m += ….
②make uImage →包含arch/arm/makefile 发现目标uImage 那么肯定被包含在顶层Makefile
内核启动流程
- 处理u-boot传入的参数 arch/arm/kernel/head.S
0判断是否支持这个 CPU
1判断 是否支持该单板 由u-boot启动内核时传入的机器ID – MACH_TYPE
2建立页表 bl _create_page_tables
3使能MMU
4跳到start_kernel →内核第一个C函数 开始处理uboot传入的函数
start_kernel
setup_arch //解析u-boot传入的启动参数
setup_command_line //解析u_boot传入的启动参数
parse_early_param
do_early_param
从__setup_start到__setup_end 调用early函数
unknown_bootoption
obsolete_checksetup
从__setup_start到__setup_end 调用非early函数
rest_init
kernel_init
prepare_namespace
mount_root
init_post();
//执行应用程序
挂接根文件系统
最终目的:运行应用程序(应用程序放在文件系统)
分区
vmlinux.lds
__proc_info_begin = .;
*(.proc.info.init)
__proc_info_end = .;
内核怎样启动第1个应用
- open(/dev/console) 标准输入输出或err输出终端
sys_dup(0),sys_dup(0)
②run_init_process 命令行init=xxx或/sbin/init or /etc/init or /bin/init 而且第一个程序应会死循环
7构建根文件系统 :
- 内容:可执行程序 、库、配置文件,设备节点
- ②制作映象文件:YAFFS2,JFFS2
- 烧写
- NFS:
- 从flash上启动根文件系统 ,再用命令挂接NFS
mkdir /mnt
mount -t nfs -o nolock 192.168.1.102:/work/nfs_root/first_fs /mnt
-
- 直接让NFS启动,改变root地址,设置服务器地址和开发板地址
设置可以参加documentation下的nfsroot.txt有说明
原来 :
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
改为:
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.1.102:/work/nfs_root/first_fs ip=192.168.1.17:192.168.1.102:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
参考:
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
制作最小根文件系统需要的:
①/devg/console /dev/null
②init 本身,即busybox 读取配置文件,执行
③/etc/inittab/ 配置文件
④配置文件里指定的应用程序
- c库
最后用mkyaffs2image 来制作根文件系统
过程:解压busybox-1.7.0,
make menuconfig 设置CROSS_COMPILE ?=arm-linux-
后make
请注意不可直接make install
要make CONFIG_PREFIX=/path/from/root install
到创建的frist_fs后,创建设备文件console /dev/null
mkdir dev sudo mknod console c 5 1
创建vi /etc/inittab 建立标准输入输出错误console::askfirst:-/bin/sh
创建 mkdir /work/nfs_root/first_fs/lib
在/work/tools/gcc-3.4.5-glibc-2.3.6/lib
复制cp *.so* /work/nfs_root/first_fs/lib –d
制作yaffs2文件工具
/work/system/Development_util_ok/yaffs2/util
执行make 复制sudo cp mkyaffs2image /usr/local/bin
sudo chmod +x /usr/local/bin/mkyaffs2image
在/work/nfs_root下输入mkyaffs2image
mkyaffs2image first_fs first_fs_zxd.yaffs2
拷贝first_fs_zxd.yaffs2进行最小文件系统烧写
要制作一些脚本,两种方法可以制作:
手工:mount –t proc none /proc
也可以 创建脚本在/etc/inittab 上添加脚本::sysinit:/etc/init.d/rcS
vi /etc/init.d/rcS上添加mount –t proc none /proc (创建时记得添加执行权限 sudo chmod +x /etc/init.d/rcS)
也可以在脚本上添加 mount –a (该文件是在调用/etc/fstab,同时也要在该文件进行配置)
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
查看当前挂载的文件系统
cat /proc/mounts
如果某个程序是交叉编译的话,千万不要sudo make install 它会破坏你的系统
可以用mdev创建设备文件
jffs2文件一般用在制作nor flash上启动的根文件系统,但也可以用在nand flash上,不过烧写完,要修改菜单bootargs的文件系统类型
jffs 工具
已制作好,到/work/nfs_root 输入 mkfs.jffs
修改文件系统文件类型:
开发时也可以用NFS挂载文件系统,而不用烧写
若看不到时ifconfig 则 ifconfig eth0 up
配置ip ifconfig eth0 IP地址
注意:①服务器“允许”那个目录可被挂接
vi /etc/exports
添加 /work/nfs_root/first_fs *(rw,sync,no_root_squash)
sudo /etc/init.d/nfs-kernel-server restart
- 单板去挂接
mkdir /mnt
mount -t nfs -o nolock 192.168.1.102:/work/nfs_root/first_fs /mnt
也可以一启动就启用nfs上的文件系统:
8.字符设备驱动
应用:读写文件,点灯,获取按键。。。都是通过open read write 函数来写的,它们是通过中间C库再通过系统调用层system call interface(sys.open sys.write sys read)来调用设备驱动程序dev**.open dev** .write dev**.read
应用程序:
app 打开“/dev/xxx”设备文件怎么去对应注册的驱动,VFS打开设备文件后分析是类型和主设备号
应用open,read,write 通过swi val产生异常,根据产生的异常来调用相应的函数
VFS中间用sys.open sys.write sys.read来分辨不同的硬件驱动文件属性,类型和设备号
驱动方面:
-
- 写出驱动程序
- 怎么告诉内核
- 定义一个file_operations
- 把这个结构告诉内核register_chardev
- 谁来调用→驱动 的入口函数
- 修饰 moudule_nit
moudule_exit
APP open (“/dev/xxx”).read write
文件属性 c设备类型 111 主设备号
C库
VFS 根据内核数组 中装载的驱动operationgs eg : 0 1 2 3 4 5 主设备号111
- 驱动程序 led_open led_write led_read
- 定义一个file_oeerationgs 包含.open→ led_open .write→led_write
- 入口函数调用register_chrdev(major主设备号,name,入口函数)
加载官字符设备
cat /proc/devices 列出内核现在支持的设备
insmod first_drv.ko
创建设备文件节点mknod /dev/xxx c 111 0
若主设备号写0就会让系统自动给我们分配设备号
- 驱动:①自动分配主设备号
2手工指定
- 应用 open(“/dev/xxx”)怎么来的
- 手工建立 mknod /dev/xxx c 主 次
- 自动创建: udev,mdev 在装载驱动后会在/sys/class 生成一引些信息,mdev根据 这些信息来创建设备节点
前提是要在驱动新建类,类再创建设备
编译成模块
制作Makefile
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
9驱动
9.1写一个点LED驱动:
- 框架
- 完善硬件的操作{a看原理图
b看2440手册
c 写代码,不同于单片机程序,驱动写入的是虚拟地址,用ioremap 映射虚拟地址
d 启用mdev机制,自动分配设备号,创建class,class创建设备节点
e用户空间与内核空间的数据传输copy_from_user
}
- 怎么编译驱动:Makefile
- 测试
9.2第2个驱动程序 :查询方式获取按键值
学习:
key_vals[0]=(regval & (1<<0))?1:0;
key_vals[1]=(regval & (1<<2)?1:0;
- 写出框架
- file_operations结构体 .open .write .write
- 入口函数 注册,出口函数卸载
- 给sysfs提供更多信息,udev机制可以自动创建设备节点
- 创建类class
- class创建device次设备号
- 硬件操作
- 看原理图 引脚
- 看2440手册:Reg
- 编程: 与单片机使用物理地址不同的是这里要使用虚拟地址
- ioremap
9.3第3个驱动程序 :中断方式
①按键按下
②cpu发生中断
跳到异常向量入口执行
- b 函数
- 保存的现场
- 执行中断处理函数
- 恢复现场
Linux :
- 异常向量
trap_init 构造
- vector irq 用宏实现 +offset
- __irq_usr
- b do_asm_IRQ
- irq_desc[irq] desc->handle_irq
- handle_edge_irq
- desc->chip->ack(irq) :清中断
- handle_IRQ_event :处理中断
- 取出action 链表中的成员
- 执行 action ->handler
注册中断
- request.irq(irq,handler,flags,name,devid)
- 分配一个irqaction
- setup_irq把这个结构action放入irq_desc[irq] action链表里面
- 设置引脚,desc→chip →settype
- 使能中断 desc→chip →startup/enable
- free_irq(irq,devid)
- 出链
- 禁止中断(1个链表的情况下)
打开设备断
cat /proc/interrupts
exec 5</dev/buttons打开设备定位到5去
ls –l /proc/771/fd
exec 5<&- 关闭设备中断
lsmod入看设备有没有用
结构体学习
struct pin_desc{
unsigned int pin;
unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
};
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(pindesc->pin);
}
Linux下的分步编译:网上找到的
1) 预处理
gcc -E test.c -o test.i
在当前目录下会多出一个预处理结果文件 test.i,打开 test.i 可以看到,在 test.c 的基础上把stdio.h和stdlib.h的内容插进去了。
2) 编译为汇编代码
gcc -S test.i -o test.s
其中-S参数是在编译完成后退出,-o为指定文件名。
3) 汇编为目标文件
gcc -c test.s -o test.o
.o就是目标文件。目标文件与可执行文件类似,都是机器能够识别的可执行代码,但是由于还没有链接,结构会稍有不同。
3) 链接并生成可执行文件
gcc test.o -o test
如果有多个源文件,可以这样来编译:
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test
9.4 poll机制
见文档
9.5异步通信
目标:按键时,驱动通知应用程序
- 应用程序 :注册信号处理函数
- 谁发:驱动
- 发给谁 →app→app要告诉驱动pid
- 应用程序:
- fcntl(fd, F_SETOWN, getpid()); // 告诉内核,发给谁
- Oflags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, Oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct
- 怎么发 kill_fasync
按键获取:
- 应用程序主动去read方式:
- 查询:耗尽资源
- 中断:read();一直在等待
- poll机制:等待一段时间返回
- 驱动→去提醒应用
- 异步通信机制 signal
- 应用程序 :注册信号处理函数
- 谁发 驱动
- 发给谁 →app →APP要告诉驱动PID
- 怎么发 kill_fasync
- 详见fasync.txt
- 异步通信机制 signal
9.6驱动程序 之同步互斥阻塞
1原子操作 作用:不可两进程同时打开一个文件
2.信号量 :获取信号量
3.阻塞
kill -9 918
9.7 Socket编程
服务器server.cpp
- 通过 socket() 函数创建了一个套接字,参数 AF_INET 表示使用 IPv4 地址,SOCK_STREAM 表示使用面向连接的数据传输方式,IPPROTO_TCP 表示使用 TCP 协议。在 Linux 中,socket 也是一种文件,有文件描述符,可以使用 write() / read() 函数进行 I/O 操作。
- bind() 函数将套接字 serv_sock 与特定的IP地址和端口绑定,IP地址和端口都保存在 sockaddr_in 结构体中
- socket() 函数确定了套接字的各种属性,bind() 函数让套接字与特定的IP地址和端口对应起来,这样客户端才能连接到该套接字。
- 让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”。 accept() 函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。
- write() 函数用来向套接字文件中写入数据,也就是向客户端发送数据。
- socket 在使用完毕后也要用 close() 关闭。
客户端client.cpp
- //创建套接字。connect() 向服务器发起请求,服务器的IP地址和端口号保存在 sockaddr_in 结构体中。直到服务器传回数据后,connect() 才运行结束。
- 通过 read() 从套接字文件中读取数据
- //关闭套接字在使用完毕后也要用 close() 关闭,
- SOCK_STREAM 表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
- 2) SOCK_DGRAM 表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
在Linux下创建 socket
在 Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:
int socket(int af, int type, int protocol);
使用 connect() 建立连接时,客户端和服务器端会相互发送三个数据包,请看
最后的说明
三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号 Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack = Seq + 1是否成立,如果成立说明对方正确收到了自己的数据包。
TCP四次握手断开连接(图解)
TCP 是面向连接的传输协议,建立连接时要经过三次握手,断开连接时要经过四次握手,中间传输数据时也要回复ACK包确认,多种机制保证了数据能够正确到达,不会丢失或出错。
UDP 是非连接的传输协议,没有建立连接和断开连接的过程,它只是简单地把数据丢到网络中,也不需要ACK包确认。
UDP 的可靠性虽然比不上TCP,但也不会像想象中那么频繁地发生数据损毁,在更加重视传输效率而非可靠性的情况下,UDP是一种很好的选择。比如视频通信或音频通信,就非常适合采用UDP协议;通信时数据必须高效传输才不会产生“卡顿”现象,用户体验才更加流畅,如果丢失几个数据包,视频画面可能会出现“雪花”,音频可能会夹带一些杂音,这些都是无妨的。
UDP不像TCP,无需在连接状态下交换数据,因此基于UDP的服务器端和客户端也无需经过连接过程。也就是说,不必调用 listen() 和 accept() 函数。UDP中只有创建套接字的过程和数据交换的过程
UDP服务器端和客户端均只需1个套接字
TCP中,套接字是一对一的关系。如要向10个客户端提供服务,那么除了负责监听的套接字外,还需要创建10套接字。但在UDP中,不管是服务器端还是客户端都只需要1个套接字。之前解释UDP原理的时候举了邮寄包裹的例子,负责邮寄包裹的快递公司可以比喻为UDP套接字,只要有1个快递公司,就可以通过它向任意地址邮寄包裹。同样,只需1个UDP套接字就可以向任意主机传送数据。
server.cpp 中没有使用 listen() 函数,client.cpp 中也没有使用 connect() 函数,因为 UDP 不需要连接。
大端序和小端序
CPU向内存保存数据的方式有两种:
- 大端序(Big Endian):高位字节存放到低位地址(高位字节在前)。
- 小端序(Little Endian):高位字节存放到高位地址(低位字节在前)。
仅凭描述很难解释清楚,不妨来看一个实例。假设在 0x20 号开始的地址中保存4字节 int 型数据 0x12345678,大端序CPU保存方式如下图所示:
图1:整数 0x12345678 的大端序字节表示
对于大端序,最高位字节 0x12 存放到低位地址,最低位字节 0x78 存放到高位地址。小端序的保存方式如下图所示:
图2:整数 0x12345678 的小端序字节表示
不同CPU保存和解析数据的方式不同(主流的Intel系列CPU为小端序),小端序系统和大端序系统通信时会发生数据解析错误。因此在发送数据前,要将数据转换为统一的格式——网络字节序(Network Byte Order)。网络字节序统一为大端序。
mount -t nfs -o nolock,vers=2 192.168.10.102:/work/nfs_root /mnt
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.10.102:/work/nfs_root/tmp/fs_mini_mdev ip=192.168.10.101:192.168.10.102:192.168.10.23:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.0.102:/work/nfs_root/tmp/fs_mini_zxd ip=192.168.0.101:192.168.0.102:192.168.0.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
bootloader 有些在这之前还会有一段BIOS程序运行,MIPS,而ARM则直接运行bootloader
bootloader | bootloader paramers | kernel | filesystem
bootloader 分为两部分:一部分为汇编初始化硬件结构,第二部分开始C语言完成更复杂的功能
第一阶段:设为SVC模式,关闭看门狗,关中断,关闭MMU CACHE,SDRRAM初始化,设置栈,设置CPU时钟频率FCLK/HCLK/PCLK,
拷贝NAND代码至SDRAM,清BSS段,调用start_armboot
cpu/arm920t/start.S平台相关
第二阶段:开发板相关 board/smdk/2410/lowlever_init.S
初始化本阶段要初始化的硬件设备
检测系统内存映射
将内核映像和文件系统拷贝到RAM空间去
为内核设置启动参数
调用内核
uboot :读FLASH,初始化SDRAM,启动内核
编译uboot: tar jxvf u-boot1.1.6.tar.bz2
patch -p1 < ../u-boot-1.1.6_jz2440.patch
//要用3.4.5 gcc的编译器
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/tools/gcc-3.4.5-glibc-2.3.6/bin/
make 100ask.....config
make
在这里编译失败要讲gcc 编译器版本降低为3.4.5
分析u-boot cpu/arm920t/start.S
makefile 连接脚本 uboot lds Text 0x33f80000
uboot第一阶段 start.S 步骤
设置为SV32管理模式
关看门狗
屏蔽中断
初始化SDRAM
设置栈
始终初始化
拷贝FLASH代码至DRAM
重定位uboot至SDRAM
清BSS段,调用start_armboot
uboot第二阶段: start_armboot
s = getenv("bootcmd")
run_command(s,0);
u-boot界面:
s = readline(串口读入的数据);
run_command(s,0)
增加boot cmd命令
按照U_BOOT_CMD 结构体并填充内容,会绑定cmd 的name和执行函数,便可增加新的命令
u_boot 目的:从FLASH中读出内核 nand.read.jffs addr kernal offset
启动内核 bootm 校验uimage头部,将内核data移动到合适加载地址
do_bootm_linux->设置启动参数->设置tag,setup—_start_tag,memory_tag,cmdline_tag,end_tag,启动内核,调用The_Kernal,传入机器码arch_number,bootm_parames
memory_tag是内存大小,命令cmdline 是bootargs启动命令告诉内核位置,初始化脚本,打印串口信息,
kernal
下载源码
打补丁
配置menuconfig 自己配
默认配置 arch/arm/s3cdefconfig make s3c2410_defconfig make menuconfig make uImage
厂家推荐的配置 cp 厂家config 为.config
.config 生成->include/linux/autoconf.h 定义了很多很多宏 ex: grep "DM_9000" * -nwR
.config ->/include/config/auto.conf 定义了makefile中的y or m
makefile 包含 obj-y += xxx.o
obj-m += xxx.o
-y -m 来源于
编译makefile 分析 /kbuild/makefile详细看看
kernel = vmlinux = 头部+正式内核数据
第一个文件:arch/arm/kernel/head.S
第二个文件:arch/arm/kernel/vmlinux.lds
内核源码启动分析
arch/arm/kernel/head.S 处理U-boot传下来的参数
定义了一个sattic const machine_desc __mach_desc_s3c2440 \
与宏展开后设定了一些段属性与链接脚本相对应lds
1.判断是否支持这个CPU
2.判断是否支持这个单板 处理传入的机器ID
3.建立页表
4.使能MMU
5.调到start_kernel 第一个C函数,处理传入的参数
start_kernel
setup_arch //解析u-boot传入参数
setup_command_line //解析u-boot传入参数
reset_init
kernel_init
prepare_namespace
mount_root //挂接文件系统
init_post
//执行应用程序
挂载根文件文件系统
启动应用程序
根文件系统
①打开串口/dev/console=ttySAC0 sys_dup(0),sys_dup(1) 标准输入,标准输出,标准错误都输向串口
kernel的编译
tar jxf linux-2.6.22.6.tar.bz2
patch -p1 < ../linux-2.6.22.6_jz2440.patch
cd linux-2.6.22.6
cp config.ok ./config
make
2.启动传入的init应用程序
最小根文件系统
int本身即busybox
/dev/console /dev/null
配置文件initab
启动配置文件指定的应用程序
C库
busybox
打开对应的终端/dev/console
读取配置文件,/etc/inittab
如果没有配置文件,采用默认配置 /bin/bash之类的
创建new_init_action链表将具体的配置开机的应用程序写入链表,然后从链表中读取执行并删除
new_init_actions(分不同的类级别的应用程序) SYSINIT,ONCE,ASKFIRST之类的,配置具体的COMMAND
编译根文件系统
编译busybox :make menuconfig 配置相应项,设置交叉编译工具 CROSS_COMPLIE ,make;make instll -p dir
创建/dev/console /dev/null 设备: cd dev/ sudo mknod console c 5 1;sudo mknod null c 1 3
配置文件 inittab : cd /etc/ touch inittab ,输入 console::ASKFIRST:-/bin/bash
配置应用程序启动:暂时不需要,这是最小根文件系统
C库: 编译glibc库,拷贝动态库至 lib/目录下 cp *.so lib/ -d
用makeyaffs2image 工具生成镜像
ps命令以来于:mount -t proc none /proc
可以直接在inittab ::sysinit:/etc/init/rcS 添加该命令 mout -t proc none /proc
也可以直接mount -a 会自动加载 /etc/fstab中的命令
根据linux应用手册编辑 rcS和fstab文件,就会在/dev/目录下生成很多设备节点 udev 自动创建节点 mdev
编译根文件系统
编译busybox
tar xjf busybox.tar.bz2
修改根文件目录下的Makefile中的arch = arm CROSS_COMPLIE = arm-linux-
make CONFIG_PREFIX = 安装目录 install
mkdir etc proc etc/init.d/ sys tmp mnt
vi etc/inittab
#console::askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
s3c2410_serial0::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
vi etc/init.d/rcS
输入mount -a
#!/bin/sh
ifconfig eth0 192.168.0.101
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
vi etc/fstab
# device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
chmod +x /etc/initab
chmod +x /etc/init.d/rcS
chmod +x /etc/fstab
mkdir dev
cd dev/
sudo mknod console c 5 1
sudo mknod null c 1 3
mkyaffs2Image fs_mini_mdev fs_mini_mdev.yaffs2
NFS挂载文件系统
手工挂载
①开启NFS服务
/etc/export 输入
/work/nfs_root/fs_mini_mdev_new *(rw,sync,no_root_squash)
sudo /etc/init.d/nfs-kernel-server restart
尝试去挂载到自己的/mnt
sudo mount -t nfs 192.168.0.102:/work/nfs_root/tmp/fs_mini_zxd /mnt
2.开发板挂载
mount -t nfs -o nolock 192.168.0.102:/work/nfs_root/tmp/fs_mini_zxd /mnt
3.开机即挂载
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.0.102:/work/nfs_root/tmp/fs_mini_zxd ip=192.168.0.101:192.168.0.102:192.168.0.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0