/*********************1************************/
第1阶段视频目录(Linux系统管理)
*RedHat企业版安装光盘
*第一天(安装与命令)
1-1-1(嵌入式系统概述)
1-1-2(Linux介绍)
1-1-3(Linux定制安装)
1-1-4(Linux命令1)
1-1-4(Linux命令2)
1-1-5(VI使用)
1-1-5(VI使用)
*第二天(系统管理)
1-2-1(系统管理)
tftp服务器:TFTP服务器作为工作于宿主机上的软件,主要提供对目标机的主要映像文件的下载
工作。
nfs:网络文件系统(NFS,Network FileSystem)是一种将远程主机上的分区(目录)经网络挂载到本地的一种机制,通过对网络文件系统的支持,用户可以在本地系统上像操作本地分区一样来对远程主机的共享分区(目录)进行操作。(类似于windows共享目录)。
samba:Linux与Linux之间通过NFS实现共享
Windows与windows之间通过共享目录实现共享
Wireshark :是一款非常棒的Linux和Windows上的开源网络协议分析器。它可以实时检测网络通讯数据,也可以抓取网络通讯数据。可以通过图形界面浏览这些数据,可以查看网络通讯数据包中每一层的详细内容。
1-2-2 (Shell编程)
shell简单地讲,就是命令解析器,将用户输入的指令转换为相应的机器能够运行的程序。
shell脚本是一个包含一系列命令序列的文件文本。当运行这个脚本文件时,文件中包含的命令序列将得到执行。
*第三天(编程基础)
1-3-1(GCC程序编译)
gcc可以在多种硬体平台上编译出可执行程序
1-3-2(GDB程序调试)
GDB主要完成下面三个方面的功能:
1、启动被调试程序。
2、让被调试的程序在指定的位置停住。
3、当程序被停住时,可以检查程序状态(如变量值)。
1-3-3(Makefile工程管理)
Makefile文件描述了整个工程的编译,连接等规则
******** 2 ***********
*第2阶段(应用程序设计)
*第一天(文件编程)
2-1-1(系统调用方式访问文件)
Linux中文件编程可以使用两种方法:Linux系统调用和C语言库函数
文件描述符:
在Linux系统中,所有打开的文件都对应一个文件描述符。文件描述符的本质是一个非负整
数。当打开一个文件时,该整数由系统来分配。文件描述符的范围是0 - OPEN_MAX 。
系统调用-创建:int creat(const char *filename,mode_t mode)
系统调用-打开:int open(const char *pathname, int flags)
int open(const char *pathname, int flags,mode_t mode)
系统调用-关闭:int close(int fd)
系统调用-读:int read(int fd, const void *buf, size_t length)
系统调用-写:int write(int fd, const void *buf, size_t length)
系统调用-定位:int lseek(int fd, offset_t offset, int whence)
系统调用-访问判断:int access(const char*pathname,int mode)
2-1-2(库函数方式访问文件)
库函数-创建和打开:FILE *fopen(const char *filename, const char *mode)
库函数-读:size_t fread(void *ptr, size_t size, size_t n, FILE*stream)
库函数-写:size_t fwrite (const void *ptr, size_t size, size_t n,FILE *stream)
库函数-读字符:int fgetc(FILE *stream)
库函数-写字符:int fputc(int c, FILE *stream)
库函数-格式化读:fscanf(FILE *stream, char *format[,argument...])
库函数-格式化写:int fprintf(FILE *stream, char* format[,argument,...])
库函数-定位:int fseek(FILE *stream, long offset, int whence)
路径获取:char *getcwd(char *buffer,size_t size)
创建目录:int mkdir(char * dir, int mode)
2-1-3(时间编程)
时间获取:time_t time(time_t *tloc)
时间显示:char *asctime(const struct tm *tm)
获取时间:int gettimeofday(struct timeval *tv,struct timezone *tz)
延时执行:unsigned int sleep(unsigned int seconds)
*第二天(进程控制)
2-2-1(进程控制理论)
进程是一个具有一定独立功能的程序的一次运行活动
三种状态:执行、就绪、阻塞
进程互斥:进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。
临界资源:操作系统中将一次只允许一个进程访问的资源称为临界资源。
临界区:进程中访问临界资源的那段程序代码称为临界区。
进程同步:一组并发进程按一定的顺序执行的过程称为进程间的同步。
进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行。
死锁:多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进。
2-2-2(进程创建)
pid_t pid=fork()
exec函数族:exec用被执行的程序替换调用它的程序。
2-2-3(进程等待)
pid_t wait(int *status)
阻塞该进程,直到其某个子进程退出
*第三天(进程通讯)
2-3-1(进程通讯概述)
进程通信方式:管道和有名管道、信号、消息队列、共享内存、信号量、套接字
2-3-2(管道通讯)
管道:单向的,先进先出,尾部写,头部读
无名管道用于父进程和子进程间的通信
有名管道用于运行同一系统的任意两个进行间的通信
int pipe(int filefis[2])
2-3-3(信号通讯)
int kill(pid_t pid, int signo)
信号处理的主要有种方法:一种是用简单的signal函数
另一种是使用信号集函数租
Alarm:每个进程只能有一个闹钟时间。
Pause:pause函数使调用进程挂起直至捕捉到一个信号。
2-3-4(共享内存通讯)
共享内存是被多个进程共享的一部分物理内存。是进程间共享数据的一种最快方法。
共享内存实现分为两个步骤:
一、创建共享内存,使用shmget函数。
二、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。
创建:int shmget ( key_t key, int size, int shmflg )
映射:int shmat ( int shmid, char *shmaddr, int flag)
删除:int shmdt ( char *shmaddr )
*第四天(进程通讯)
2-4-1(消息队列)
信号能够传送信息量有限,管道只能传送无格式的字节流,消息队列可以克服这些缺点。
消息队列就是一个消息的链表,进程可以向其中按照一定的规则添加新消息,另一些进程则可以从消息队列中读走消息。
主要有两种消息队列:POSIX消息队列和系统V消息队列。
系统V消息队列是随内核持续的,只有内核重启或者人工删除时,该消息队列才会被删除。
IPC_CREAT创建新的消息队列。
键值:key_t ftok (char*pathname, char proj)
打开/创建:int msgget(key_t key, int msgflg)
发送消息:int msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg)
接收消息:int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)
2-4-2(信号量)
信号量有名信号灯,主要用于保护临界资源,还可用进程同步。
控制进程互斥、同步。
二值信号灯:值只能取1或0,类似互斥锁。
创建/打开:int semget(key_t key, int nsems, int semflg)
操作:int semop(int semid, struct sembuf *sops, unsigned nsops)
*第五天(多线程)
2-5-1(线程创建)
创建线程:int pthread_create(pthread_t * tidp,const pthread_attr_t *attr,void *(*start_rtn)(void),void *arg)
线程退出:void pthread_exit(void * rval_ptr)
2-5-2(线程等待与清除)
线程等待:pthread_join(pthread_t tid, void **rval_ptr)
线程标识:pthread_t pthread_self(void)
线程清除:void pthread_cleanup_push(void (*rtn)(void *),void *arg)
void pthread_cleanup_pop(int execute)
2-5-3 (线程同步)
通过下面这些技术的使用,可以解决线程之间对资源的竞争:
1 互斥量Mutex
2 信号灯Semaphore
3 条件变量Conditions
互斥量从本质上说就是一把锁, 提供对共享资源的保护访问
创建:int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)
int pthread_mutex_destroy(pthread_mutex_t *mutex)
加锁:int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex)
互斥量PK信号量:
Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。
Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。
*第六天(网络)
TCP/IP、socket编程、TCP、UDP
目前常用的无服务模型有:
循环服务器:服务器在同一时刻只能响应一个客户端的请求
并发服务器:服务器在同一个时刻可以响应多个客户端的请求
第3阶段(ARM程序设计)
*第一部分(体系结构指令)
3-1-1(ARM概述)
3-1-2(mini2440)
3-1-3(ARM寄存器)
3-1-4(ARM寻址方式)
3-1-5(ARM指令1)
3-1-6(ARM指令2)
*第二部分(ARM综合实验)
*裸机程序
Key_example Key_poll_example Led_example
Timer_example Uart_example Uart_Int_example
******** 3***********
第4阶段(内核开发)
*第1天
4-1-1(内核简介)
linux体系结构:linux由用户空间和内核空间两部分组成。
通过系统调用和硬件中断能够完成从用户空间到内核空间的转移。
SCI层(系统调用接口)为用户空间提供了一套标准的系统调用函数来访问linux内核,搭建起了用户空间到内核空间的桥梁。
进程管理的重点是创建线程、停止线程、并控制他们之间的通信。
进程管理还包括控制活动进程如何共享CPU,即进程调度。
内存管理的主要作用是控制多个进程安全地共享内存区域。
虚拟文件系统(VFS)为文件操作提供了统一的接口。
4-1-2(Linux内核源代码)
4-1-3(Linux内核配置与编译)
4-1-4(内核模块-1)
4-1-4(内核模块-2)
*第2天
4-2-1(Linux内存管理)
地址类型:物理地址、线性地址(虚拟地址)、逻辑地址。
物理地址:是指出现在CPU总线上的寻址物理内存的地址信号,是地址转换的最终结果。
逻辑地址:程序代码经过编译后在汇编程序中使用的地址。
线性地址:在32位CPU架构下,可以表示4G的地址空间。
地址转换:CPU要将一个逻辑地址转换成物理地址,需要两步:
首先是CPU利用段式内存管理单元,将逻辑地址转换成线程地址,再利用页式内存管理单元,把线性地址最终转换成物理地址。
4-2-2(Linux进程与内核地址空间)
linux将4G虚拟地址空间换分为两个部分:用户空间和地址空间
用户空间是从0-0xbfffffff,内核空间是从3G-4G,用户空间通常不能访问内核空间,除非系统调用和中断。
实际物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际的页框的程序。最后虚拟地址实实在在地映射到了物理地址上。
内存分配标志:GFP_KERNEL
内核空间是由内核负责映射的,它并不会跟着进程改变,是固定的。
线性地址=3G+物理地址
4-2-3(Linux内核链表)
4-2-4(内核定时器)
度量时间差:每当时钟中断发生时,全局变量jiffies就加1,因此jiffies记录了自linux启动后时钟中断发生的次数。驱动程序常利用jiffies来计算不同事件的时间间隔。
Unsigned long j=jiffies +jit_delay*HZ; HZ= 1s
操作定时器的有如下函数:
void init_timer(struct timer_list *timer);
初始化定时器队列结构。
void add_timer(struct timer_list * timer);
启动定时器。
int del_timer(struct timer_list *timer);
在定时器超时前将它删除。当定时器超时后,系统会自动地将它删除。
*第3天
4-3-1(内核进程)
进程四要素:
1. 有一段程序供其执行。这段程序不一定是某个进程所专有,可以与其他进程共用。
2. 有进程专用的内核空间堆栈。
3. 在内核中有一个task_struct数据结构,即通常所说的“进程控制块”。有了这个数据结
构,进程才能成为内核调度的一个基本单位接受内核的调度。
4. 有独立的用户空间。
进程线程区别:
有独立的用户空间堆栈,是进程,如果没有,但是有共享的用户空间是用户线程,如果两者都没有则是内核线程。
4-3-2(进程调度)
调度:从就绪的进程中选出最合适的一个来执行。
学习调度的知识:1、调度策略
2、调度时机
3、调度步骤
调度策略:
vSCHED_NORMAL(SCHED_OTHER):普通的分时进程
vSCHED_FIFO :先入先出的实时进程
vSCHED_RR:时间片轮转的实时进程
vSCHED_BATCH:批处理进程
vSCHED_IDLE: 只在系统空闲时才能够被调度执行的进程
调度时机:1、主动式:在内核直接调用调度函数
2、被动式(抢占):
用户抢占可能发生在:1、发生在系统调用返回用户空间
2、从中断处理程序返回用户空间
内核抢占可能发生在:1、中断处理程序完成返回内核空间之前
2、当内核代码具有再一次抢占的时候,如软中断和解锁
4-3-3(系统调用)
Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。
系统调用和普通的函数调用非常相似,区别仅仅在于,系统调用由操作系统内核实现,运行于内核态;而普通的函数调用由函数库或用户自己提供,运行于用户态。
工作原理:
一般情况下,用户进程是不能访问内核的。它既不能访问内核所在的内存空间,也不能调用内核中的函数。系统调用是一个例外。其原理是进程先用适当的值填充寄存器,然后调用一个特殊的指令,这个指令会让用户程序跳转到一个事先定义好的内核中的一个位置:
在Intel CPU中,这个指令由中断0x80实现。
在ARM中,这个指令是SWI。
4-3-4(Proc文件系统)
proc文件系统是一种在用户态检查内核状态的机制
4-3-5(异常分析)
Oops 可以看成是内核级的Segmentation Fault。应用程序如果进行了非法内存访问或执
行了非法指令,会得到Segfault信号,一般的行为是coredump,应用程序也可以自己截获
Segfault信号,自行处理。如果内核自己犯了这样的错误,则会打出Oops信息。
*第4天
4-4-1(交叉工具链)
编译器:arm-linux-gcc
arm-linux-gcc hello.c –o hello
反汇编工具:arm-linux-objdump
arm-linux-objdump –D –S hello
ELF文件查看工具:arm-linux-readelf
arm-linux-readelf –a hello
arm-linux-readelf –d hello 查看hello使用的动态库
4-4-2(嵌入式系统构建)
嵌入式Linux系统由Linux内核与根文件系统两部分构成,两者缺一不可。
根文件系统是Linux启动时使用的第一个文件系统。没有根文件系统,Linux将无法正常启动。根文件系统由一系列目录组成,目录中包含了应用程序、C库、以及相关的配置文件。
Linux文件系统:
Linux支持多种文件系统类型,包括ext2、ext3、vfat、jffs、romfs和nfs等,为了对各类文件系统进行统一管理,Linux引入了虚拟文件系统VFS(Virtual File System),为各类文件系统提供一个统一的应用编程接口。
在嵌入式Linux应用中,主要的存储设备为RAM 和FLASH,常用的基于存储设备的文
件系统类型包括:jffs2, yaffs, cramfs,ramdisk, ramfs等。
Flash(闪存)作为嵌入式系统的主要存储媒介,主要有NOR和NAND两种技术。Flash存储器的擦写次数是有限的,NAND闪存还有特殊的硬件接口和读写时序。因此,必须针对Flash的硬件特性设计符合应用要求的文件系统。
yaffs/yaffs2 (Yet Another Flash FileSystem)是专为NAND型flash而设计的一种日志型文件系统。
Cramfs是Linux的创始人Linus 参与开发的一种只读的压缩文件系统,它也基于MTD驱动程序
Ramdisk是将一部分固定大小的内存当作分区来使用。它并非一个实际的文件系统,而是
一种将实际的文件系统(如ext2)装入内存的机制。
NFS (Network File System)是由Sun开发的一种在不同机器之间通过网络共享文件的技术。
*第5天
4-5-1(bootloader介绍)
一个嵌入式系统从软件角度来看分为三个层次:
1. 引导加载程序
包括固化在固件(firmware)中的boot 程序(可选),和BootLoader 两大部分。
2. Linux 内核
特定于嵌入式平台的定制内核。
3. 文件系统
包括了系统命令和应用程序。
BootLoader就是在操作系统运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统做好准备。
BootLoader 大多采用两阶段,即启动过程可以分为stage 1和stage2:stage1完成初始化硬
件,为stage2准备内存空间,并将stage2复制到内存中,设置堆栈,然后跳转到stage2。
BootLoader 的stage1 通常包括以下步骤:
·硬件设备初始化
·为加载BootLoader 的stage2 准备RAM 空间
·拷贝BootLoader 的stage2 到RAM 空间中
·设置好堆栈(why??)
·跳转到stage2 的C 入口点
BootLoader 的stage2 通常包括以下步骤:
·初始化本阶段要使用到的硬件设备
·将内核映像和根文件系统映像从flash 上读到RAM 中
·调用内核
为什么需要进行bootloader移植?
每种不同的CPU体系结构都有不同的BootLoader。除了依赖于CPU的体系结构外,BootLoader 还依赖于具体的嵌入式板级设备的配置,比如板卡的硬件地址分配,外设芯片的类型等。这也就是说,对于两块不同的开发板而言,即使它们是基于同一种CPU而构建的,但如果他们的硬件资源或配置不一致的话,要想在一块开发板上运行的BootLoader程序也能在另一块板子上运行,还是需要作修改。
4-5-2(Uboot简介)
4-5-3(UBoot命令)
4-5-5(Uboot移植)
为什么需要对Uboot进行移植?
BootLoader 依赖于:具体的CPU体系、具体的板级设备配置(芯片级移植、板级移植)
开始移植之前,首先要分析U-Boot已经支持的开发板,选择出硬件配置最接近的开发
板。选择的原则是,首先选择MCU相同的开发板,如果没有,则选择MPU相同的开发板。
******** 4***********
*第5阶段(驱动程序设计)
*第1天
5-1-1(Linux驱动程序介绍)
字符设备:字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现open, close,read和write 系统调用。
5-1-2(字符设备驱动).avi
5-1-3(简单字符设备驱动实例分析).avi
5-1-4(并发控制).avi
*第2天
5-2-1(Ioctl设备控制).avi
5-2-2(内核等待队列).avi
5-2-3(阻塞型字符设备驱动).avi
5-2-4(poll设备方法).avi
5-2-5(自动创建设备文件).avi
*第3天
5-3-1(mmap设备方法).avi
5-3-2(硬件访问).avi
5-3-3(LED驱动程序).avi
*第4天
5-4-1(总线).avi
5-4-2(设备).avi
5-4-3(驱动).avi
5-4-4(platform驱动).avi
5-4-5(中断处理程序).avi
5-4-6(按键驱动).avi
*第5天
5-5-1(网卡驱动程序设计).avi
5-5-2(CS8900A网卡驱动程序分析).avi
5-5-3(输入子系统).avi
5-5-4(触摸屏驱动程序).avi
*第6天
5-6-1(PCI总线).avi
5-6-2(PCI驱动程序设计).avi
5-6-3(串口驱动程序).avi
*第6阶段(深入专题与项目实战)
H.264远程视频监控系统
MP3嵌入式播放器
USB驱动程序专题