韦东山开发板S3c2440学习笔记

                                  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]

  CPU0x00000000位置开始运行程序。

  1nand启动:

  如果将S3C2440配置成从NANDFLASH启动(将开发板的启动开关拔到nand端,此时OM0管脚拉低)S3C2440Nand控制器会自动把Nandflash中的前4K代码数据搬到内部SRAM(地址为0x40000000),同时还把这块SRAM地址映射到了0x00000000地址。CPU0x00000000位置开始运行程序。

  2、如果将S3C2440配置成从Norflash启动(将开发的启动开关拔到nor端,此时OM0管脚拉高),0x00000000就是norflash实际的起始地址,norflash中的程序就从这里开始运行,不涉及到数据拷贝和地址映射

  3、总结:

  nand启动时,地址0x00000000为内部SRAM映射的地址;

  nor启动时,地址0x00000000norflash的实际起始地址。

  向Norflash中写数据需要特定的命令时序,而向内存中写数据可以直接向内存地址赋值。

 

 

MMU来访问速度快好多,比如LED点亮实验

SDRAM来访问速度比SRAM慢一些;

 

开机启动时,NADN<4K将由硬件自动拷贝到片内的SRAM内存,若超过4K则前4K拷贝到SRAM内存的同时还负责将其他NANDFLASH上的程序拷贝到SDRAM上;

 

NANDflash没有地址总线,原理图上看

NADNflash K9F2G08X0M 每页2K+64字节(ooB,一块128K64

  • 从硬件止,访问NAND
    1. 命令 CLE放到DATABUS →寄存器NFCMMD
    2. 地址,ALE放到DATABUS NFADDR
    3. 传输数据 放到DATABUS, NFDATA
    4. 状态 NFSATA
    5. 写时必须要先擦除

NAND 4K放到SRAMhead.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 framebufferLCD控制器→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)

asvc模式

b 关看门狗

c屏蔽中断

d 初始化SDRAM

e 设置栈

f时钟

g 重定位代码flashSDRAM

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实际上定义了命令结构体,其中包含了namefuntion

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.内核

内核功能、结构,结合MakefileKconfig进行分析

解压缩;打补丁

配置: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

  • configautoconf.h
  • configauto.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.ca.o

 b.cb.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
    1. flash上启动根文件系统 ,再用命令挂接NFS

mkdir /mnt

mount -t nfs -o nolock 192.168.1.102:/work/nfs_root/first_fs /mnt

    1. 直接让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 interfacesys.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来分辨不同的硬件驱动文件属性,类型和设备号

驱动方面:

    • 写出驱动程序
    • 怎么告诉内核
      1. 定义一个file_operations
      2. 把这个结构告诉内核register_chardev
      3. 谁来调用→驱动 的入口函数
      4. 修饰 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 .writeled_write
  • 入口函数调用register_chrdev(major主设备号,name,入口函数)

 

加载官字符设备

cat /proc/devices 列出内核现在支持的设备

insmod first_drv.ko

创建设备文件节点mknod /dev/xxx c 111 0

若主设备号写0就会让系统自动给我们分配设备号

  • 驱动:①自动分配主设备号

2手工指定

  • 应用 open(“/dev/xxx”)怎么来的
    1. 手工建立 mknod /dev/xxx c
    2. 自动创建: 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看原理图

b2440手册

c 写代码,不同于单片机程序,驱动写入的是虚拟地址,用ioremap 映射虚拟地址

d 启用mdev机制,自动分配设备号,创建classclass创建设备节点

e用户空间与内核空间的数据传输copy_from_user

}

  • 怎么编译驱动:Makefile
  • 测试

9.22个驱动程序 :查询方式获取按键值

学习:

key_vals[0]=regval & (1<<0))?10;

key_vals[1]=regval & (1<<2)?10;

  • 写出框架
    1. file_operations结构体 .open .write .write
    2. 入口函数 注册,出口函数卸载
    3. sysfs提供更多信息,udev机制可以自动创建设备节点
      1. 创建类class
      2. class创建device次设备号
  • 硬件操作
    1. 看原理图 引脚
    2. 2440手册:Reg
    3. 编程: 与单片机使用物理地址不同的是这里要使用虚拟地址
      1. ioremap

 

9.33个驱动程序 :中断方式

①按键按下

cpu发生中断

跳到异常向量入口执行

  • b 函数
    1. 保存的现场
    2. 执行中断处理函数
    3. 恢复现场

Linux :

  • 异常向量

trap_init 构造

  • vector irq 用宏实现 +offset
  • __irq_usr
  • b do_asm_IRQ
  • irq_desc[irq] desc->handle_irq
  • handle_edge_irq
    1. desc->chip->ack(irq) :清中断
    2. handle_IRQ_event :处理中断
      1. 取出action 链表中的成员
      2. 执行 action ->handler

注册中断

  • request.irq(irq,handler,flags,name,devid)
    1. 分配一个irqaction
    2. setup_irq把这个结构action放入irq_desc[irq] action链表里面
      1. 设置引脚,descchip settype
      2. 使能中断  descchip startup/enable
  • free_irq(irq,devid)
    1. 出链
    2. 禁止中断(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.hstdlib.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异步通信

       目标:按键时,驱动通知应用程序

  • 应用程序 :注册信号处理函数
  • 谁发:驱动
  • 发给谁 appapp要告诉驱动pid
    1. 应用程序:
    2. fcntl(fd, F_SETOWN, getpid());  // 告诉内核,发给谁
    3. Oflags = fcntl(fd, F_GETFL);  
    4. fcntl(fd, F_SETFL, Oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct
  • 怎么发 kill_fasync

 

 

按键获取:

  • 应用程序主动去read方式:
    1. 查询:耗尽资源
    2. 中断:read();一直在等待
    3. poll机制:等待一段时间返回
  • 驱动→去提醒应用
    1. 异步通信机制 signal
      1. 应用程序 :注册信号处理函数
      2. 谁发  驱动
      3. 发给谁 app APP要告诉驱动PID
      4. 怎么发 kill_fasync
      5. 详见fasync.txt

 

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() 关闭,
  1. SOCK_STREAM 表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
  2. 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个快递公司,就可以通过它向任意地址邮寄包裹。同样,只需1UDP套接字就可以向任意主机传送数据。

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
 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值