嵌入式基础常识

一、

基本概念

linux       @           ubuntu     :~$ 
用户名 类型:普通用户   系统名称   家目录

root
管理员用户

pwd  显示当前工作路径(绝对路径)

/home/linux  家目录    /$根目录下  /代表根目录

ls 列出文件状态   + 选项 (-l  -i   -h  -1)

rwx                             r-x                                 r--
文件所属用户对该文件执行权限    同组其他用户对该文件的执行权限      非同组其他用户对文件的执行权限
读 写  执行均可                 可读  不可写   可执行               只可读  不可写 不可执行

文件类型 七种:
b    block     块设备文件       例如:U盘  硬盘  按内存块读取
c    char      字符设备文件     例如: 键盘  鼠标    按字符读写   先进先出原则
d              目录
-              普通文件
l              链接文件
s    socket    套接字文件
p    pipe      管道文件

cat +文件名  显示文件信息    rm +文件名   删除文件   cp +被复制的文件名+新文件名  复制文件
mv  +被剪切的文件+新文件名   剪切文件     mkdir  创建文件夹 /目录    rmdir  删除空目录     touch  创建一个新文件   
clear 或者  CTRL+l   可以清屏
rmdir 不能删除非空目录     rm  +  文件名  +  -rf    可以删除所有目录

cd    切换目录     cd  +  -   切换到上一次切换的目录

grep 查询关键字   +    关键字名称   +   路径名   +    -rn  逐个目录查找/递归搜索

二、

vim编辑器

模式:1.底行模式  :wq  保存并退出   :q!  强制退出不保存     shift+zz  保存并退出     
                    esc后 + yy 复制光标所在行 + p  复制    dd  剪切一行    按d剪切所有  按y复制所有     u 回复上一次状态
     
      2.命令行模式/输入模式
                  vsp  +  文件名  分屏        :wqa退出所有分屏       
                   vi + -O + 文件名+ 文件名  直接打开分屏  大写O左右分屏 小写  上下分屏


CTRL+alt+T  重新打开一个家目录下的编辑界面

sudo su   进入root用户

sudo shutdown -h now 关机    -r  now  重启  ==   sudo reboot  now


三、

shell命令

head  +  行数 +    显示文件开头
tail  +  行数 +    显示文件结尾
diff     比较两个文件
file     查看文件类型 
echo     显示文本信息   》代表输出    》》代表继续输出   在终端输入文件到设备中
         + 字符串  +  》(重定向) +  文件名   清空方式
         + 字符串 +   》》(重定向)+ +文件名  追加的方式  
df       + -TH    检查文件系统空间占用情况
du       + -mh   查看文件大小
chmod    改变文件或目录的访问权限
         sudo chmod   755  +  文件名

man      linux官方文档

passwd  用户密码   sudo  passwd  +用户名(linux)

四、文件压缩以及解压

gzip  +  - 级别(1-9)+  文件名   1-9为压缩级别  越大压缩越小
gunzip  + -f    +  压缩文件名         解压
gzip    + -l    +  压缩文件名         查看压缩比


tar 归档 压缩 解压
tar -c  创建一个归档文件
    -f  指定归档文件名称
    -x  释放归档文件
    -v  显示归档以及释放过程

    -j  由tar生成归档 由bzip2压缩
    -z  由tar生成归档 由gzip压缩

归档并压缩
    -cjf  +  压缩文件名 + 需要压缩的文件夹
    -czf  +  压缩文件名 + 需要压缩的文件夹
 
解压 

tar -xvf + 压缩文件名    便可直接解压
 
gcc 编译器
 
gcc + 文件名  编译文件
gcc + 文件名  +  -o  + 生成可执行程序名
 
执行: ./执行程序

五、编译器编译源码:

预处理(pre-processing)  xxx.c--->  (预处理文件)
编译(compiling)         xxx.i--->  (汇编文件)
汇编(assembling)        xxx.s--->  (可重定向文件)
链接(linking)           xxx.o--->   (可执行文件)

预处理:gcc -E test.c -o test.i
编译:  gcc -s test.i -o test.s
汇编:  gcc -c test.c -o test.o
链接:  gcc test.o -o test
 
makefile 
makefile格式

变量名称($)
$<  第一个依赖文件的名称
$^  所有不重复的依赖文件 以空格分开
$@  目标文件的完整名称

  
六、静态库、动态库
静态库:
1.将功能函数所在文件编译成可重定向文件
   gcc -c func.c -o func.o
2.将可重定向文件制作成静态库
   ar crs libfunc.a func.o
3.编译时,指定库的路径及库的名字
  gcc main.c -o main -L. -lfunc

动态库:
1.将功能函数所在的文件编译成可重定向文件
   gcc -fPIC -c func.c -o func.o
2.将可重定向文件制作成动态库
   gcc -shared func.o -o libfunc.so
3.生效,将库文件存放到/lib
  gcc main.c -o main -L. -lfunc  不可执行
  sudo cp libfunc.so  /lib/      转移路径后
  ./main                         便可执行
  


七、存储结构:
数组:
     一维数组:数组名   《==》数组的首地址,数组的起始地址, 首元素地址
     一维数组数组名:列性质地址  
     *(array+i)每次移动到下一位地址(列发生偏移)
     *  代表解引用 ,根据数据所在地址,获取该地址上的内容
 
     二维数组:数组名 《==》数组的起始地址 ,
     二维数组数组名   :行性质地址
     a[]:               列性质地址
     &a[0][0]:          列性质地址
 
    *(*(a+i)+j)  括号中*,表示降级处理,将a+i由行性质地址转换为列性质
    结论:
    a【i】  《=====》  *(a+i)
    一维数组中* 代表解引用
    二维数组中* 代表降级处理

指针:
    指针变量在内存上占有四个字节,与其指向无关
    普通指针是列指针, 保存的是指向的数据所在的内存的地址

数组指针:
    指向数组的指针,不同于普通指针,数组指针是行指针
    例如:int(*p)[4];

指针数组:
    数组,  数组中的元素是指针(普通指针或函数指针),保存的是地址

结构体:
       struct 结构体名
           {
              成员名:
              成员名:
              。。。。
           };
      结构体(初始化  赋值 获取结构体成员的值)
          1.变量名:
            变量名.成员名==值;
          2.指针:
            指针变量名指向成员名
            p=&test;先指向结构体名


八、IO:

input    output    输入 输出  

文件IO: 采用系统调用的方式对文件进行读写
         对文件进行读写的功能由内核完成--系统调用
         
 操作方式:先打开(open),后读(read)写(write)
 
 函数原型:int open(const char *pathname, int flags);
           int open(const char *pathname, int flags, mode_t mode);
 功能:打开一个文件或设备
 参数:pathname 路径名(文件名)
       flags    当前进程对该文件的操作权限
                O_RDONLY     以只读的方式打开文件
                O_WRONLY     以只写的方式打开文件
                O_RDWR       以读写的方式打开文件
                
                O_APPEND     以追加的方式打开文件
                O_CREAT      如果文件不存在,自动创建
                O_TRUNC      如果文件中有数据,清空数据
                
        O_RDONLY   以只读的方式打开文件,文件必须存在,不存在,出错
    O_RDWR     以读写的方式打开文件,文件必须存在,不存在,出错
    O_WRONLY|O_CREAT|O_TRUNC    以只写的方式打开文件,如果文件不存在,自动创建,
                                如果文件已存在,清空文件原有的数据
    O_RDWR|O_CREAT|O_TRUNC    以读写的方式打开文件,如果文件不存在,自动创建,
                                如果文件已存在,清空文件原有的数据
    O_WRONLY|O_CREAT|O_APPEND   以只写的方式打开文件,如果文件不存在,自动创建,
                                如果文件已存在,已追加方式进行访问                                 
    O_RDWR|O_CREAT|O_APPEND   以读写的方式打开文件,如果文件不存在,自动创建,
                                如果文件已存在,已追加方式进行访问                            
       mode   当flags指定O_CREAT,必须有第三个参数  
              mode:该文件所属用户对该文件的执行权限  
              例:0664    
              mode & ~umask(文件权限掩码)              
                                
返回值: 成功  文件描述符(非负数)
           失败  -1  
                                
当程序打开文件,文件描述符的值从3开始,0,1,2系统默认自动打开                                                                
  0:标准输入
  1:标准输出
  2:标准出错输出 

#include <unistd.h>头文件
函数原型:ssize_t write(int fd, const void *buf, size_t count);
          功能:向文件中写入count个字节的数据
          参数:fd  文件描述符
                buf 写入到文件的数据
                count 需要写入到文件的字节数

         返回值:  成功   实际写入的字节数
                   失败   -1
函数原型:ssize_t read(int fd, void *buf, size_t count);
          功能: 期望读取count个字节的数据
          参数:fd  文件描述符
                buf 保存读取的数据 
                count 期望读取count个字节的数据
 
          返回值: 成功 实际读取的字节数
                   失败  -1

九、进程

进程是一个程序的一次执行的过程

进程和程序的区别:
程序是静态的,它是一些保存在磁盘上的指令的有序的集合,没有任何执行的概念
进程是一个动态的概念,他是程序执行的过程,包括创建,调度,消亡

进程的创建:当程序开始执行时,系统会为每一个进程分配4G的虚拟地址空间,保存进程的资源
            并且会为每一个进程创建task_struct,描述进程的状态信息,或者进程资源

调度: cpu的调度或者时间片轮转调度
       时间片:进程在某一时刻获取cpu控制的时间

消亡(退出):进程退出,将该进程获取的有限资源全部释放

静态查看进程信息:ps  +  axj/(aux)

PPID(parent procss ID)     PID (process ID)      PGID(process croup ID)   SID(session ID)      
父进程ID号                   进程ID号                进程组ID号 (组长ID号)     会话组ID(会话组组长ID号)
 
TTY    TPGID   STAT          UID                   TIME                        COMMAND
终端           进程的状态     当前进程所属用户ID     进程获取CPU的控制总时间      进程名

CTRL + c 将进程退出     CTRL + z 将进程停止   bg 将进程放到后台执行(脱离终端控制)  fg 将进程放到前台


十、系统

系统移植

Windows装机: BIOS界面----》引导界面----》烧写Windows镜像----》加载当前文件系统
     
bootloader(引导系统)----》linux镜像----》文件系统(运行应用程序)

    uboot:
           driver(驱动)


tftp服务器:(配置)
  
检测: sudo dpkg -s tftpd-hpa  

在线安装:sudo apt-get install tftp-hpa tftp-hpa
         失败的话更新数据源:sudo apt-get install update
                                                                     
配置服务器端: sudo vi /etc/default/tftpd-hpa   以管理员模式进入编辑

修改代码: TFTP_USERNAME="tftp"                                                        
          TFTP_DIRECTORY="/tftpboot"
          TFTP_ADDRESS="0.0.0.0:69"
           TFTP_OPTIONS="-c -s -l"

改权限: sudo chmod 777 tftpboot   将tftpboot改成777最高权限   sudo mkdir tftpboot  在根目录下创建

配置参数:-c   create   可上传新文件
         -s   server    uboot默认tftpboot目录为传输目录
         -l   listen    监听           

启动tftp服务器: sudo service tftpd-hpa restart

nfs服务器

配置nfs服务器端: sudo vi /etc/exports  
                  
                 在11行添加代码: /source/rootfs/ *(rw,sync,no_subtree_check,no_root_squash)
  
/source  共享目录  自行创建   sudo mkdir source--》创建目录   sudo chmod 777 source--》改权限

rw 读写   sync 文件系统中的文件可以同步到内存与硬盘

no_subtree_check  如果子目录可以被访问,就不检测父目录的权限了

no_root_squash    如果以root用户进入文件系统,将不会检测权限

启动nfs服务器: sudo /etc/init.d/nfs-kernel-server restart


联网:

重启网卡:sudo /etc/init.d/networking restart 


十一、uboot

bootloader  操作模式:1.自启动模式  2.交互模式

setenv 设置新的环境变量

bootcmd  自启动命令、自启动模式、自动执行bootcmd后的参数

printenv 显示配置的环境变量

tftp 远程传输  tftp + address + file(二进制)

bootargs  uboot设置好,给内核使用

bootm    解压并启动该地址上的内容     bootm + address


dmesg 查看内核打印缓冲区的内容   sudo insmod driver.ko  插入内核    
lsmod 查看   sudo rmmod driver 卸载   modinfo driver.ko 查看
sudo dmesg -c  先查看,后刷新缓冲区 
sudo dmesg -C  直接刷新缓冲区
sudo mknod /dev/led c 500 0    执行

十二、

字符设备驱动框架

 1)设备号,驱动通过不同设备号区分不同的设备
        申请注册设备号:1)用户自定义,向内核注册
                        2)系统分配,再注册
 2)驱动需要实现对设备的控制(实现应用层对设备操作的函数接口)
 3)设备节点(文件),给应用程序,应用程序视设备为文件
  
设备号

 设备号(32bit)= 主设备号(高12bit)  +  次设备号(低20bit)

 #define MINORBITS    20
 #define MINORMASK    ((1U << MINORBITS) - 1)

 #define MAJOR(dev设备号)    ((unsigned int) ((dev) >> MINORBITS))  根据设备号,得出主设备号
 #define MINOR(dev设备号)    ((unsigned int) ((dev) & MINORMASK))   根据设备号,得出次设备号
 #define MKDEV(ma主,mi次)    (((ma) << MINORBITS) | (mi))   根据主次设备号,生成设备号
  
 申请注册:
     1)静态注册(用户自定义设备号,向内核注册)
        #define major 500
        #define minor 0
        
        dev_t devno = MKDEV(major, minor);
     
        函数原型:int register_chrdev_region(dev_t from, unsigned count, const char *name)
        功能:注册设备号
        参数:from  设备号(用户自定义)
              count 表示的是注册设备号的数量
              name  名字 (描述设备号)
        返回值:成功   0
                失败   负数
              
     2)动态注册(系统自动分配,向内核注册)
                函数原型:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,                                                      const char *name)
        功能:系统分配设备号,并且注册
        参数:dev  系统自动分配设备号
              baseminor  次设备号
              count  注册设备号的数量
              name   名字  描述设备号
        返回值:成功  0
                失败  负数        

     3)注销   void unregister_chrdev_region(dev_t from, unsigned count)
            功能:注销设备号
        参数:from 设备号
              count  设备号的数量


如何实现对设备的控制
 
对于字符设备来讲,内核中提供结构体来描述一个设备strcut cdev

如何实现设备控制

 对于字符设备,内核提供结构体来描述一个设备struct cdev
 struct cdev {
    struct module *owner;    THIS_MODULE;
    const struct file_operations *ops;   对设备进行操作的集合
    dev_t dev;             设备号
    unsigned int count;
};

 1)初始化结构体
 
 函数原型:void cdev_init(struct cdev *cdev, const struct file_operations *fops)
 功能:初始化cdev结构体
 参数:cdev  cdev结构体
       fops  对设备操作集合
 
 2)将cdev结构体添加到内核链表
 
 函数原型:int cdev_add(struct cdev *p, dev_t dev, unsigned count) 
 功能:将cdev结构体添加到内核链表
 参数:p   cdev结构体
       dev   设备号
       count  cdev结构体的数量
 返回值:成功 0
         失败 负数

 3)将cdev结构体从内核链表中移除
 函数原型:void cdev_del(struct cdev *p) 
 参数:p  cdev结构体

 
设备节点

 手动创建: mknod  /dev/xxx c 主设备号 次设备号
 主、次设备号和驱动中的注册的设备号一致,将设备节点和设备关联
 设备节点被创建在内存上,系统重启,节点删除

 
如何实现设备操作
 
 应用程序:
     int fd;
     fd = open();
     //操作代码
     close(fd);
     
 驱动:
     int test_open(struct inode *, struct file *){
            //实现硬件设备打开操作
            return 0;
     }

     int test_close(struct inode *, struct file *){
           //实现硬件设备关闭操作
           return 0;
     }
     struct file_operations fops = {
         .open = test_open,
         .release = test_close,
     };

     
    
十三、
驱动如何操作硬件设备
应用程序:int ioctl(int d, int request, ...);
           功能:控制设备
           参数:d  文件描述符
                 request  命令码
                 ...     实现数据的传递
                 
                 #define _IO(type幻数,nr编号)        
                 #define _IOR(type,nr,size传输数据的大小)  
                 #define _IOW(type,nr,size)  
                 #define _IOWR(type,nr,size) 
                 
            返回值:成功  0
                    失败  -1
                    
 驱动:long test_ioctl(struct file *filep, unsigned int cmd, unsigned long arg(地址)){
                // 实现对设备的控制
                
        }
 
       struct file_operations fops = {
            .unlocked_ioctl = test_ioctl,
       }

       
硬件控制:

物理地址----------》虚拟地址
           映射
           
        函数原型: void  *ioremap(unsigned long offset, unsigned long size)
        功能:物理地址转换为虚拟地址
        参数:offset 物理地址 
        size:范围
        返回值: 成功  虚拟地址
                 失败  NULL
            
        函数原型:void iounmap(volatile void_iomemd *adr)    
        参数: adr 虚拟地址

        函数原型:u32 readl(const  void  *addr)
        功能:读取寄存器保存的值
        参数: addr 地址 (寄存器所在的地址)    ---虚拟地址
        返回值: 成功 寄存器的值
        
        函数原型:void writel(u32 b, volatile void __iomem *addr)
        功能:将修改之后的寄存器的值,写到寄存器
        参数:b   修改之后的值
        addr   寄存器所在的地址
        


 

猜你喜欢

转载自blog.csdn.net/qq_40383417/article/details/81285947