嵌入式学习笔记

6 篇文章 1 订阅

FreeRTOS
=========
    以下内容全部基于ARM Cotex-M系统
    * MCU的NVIC分组必须是第4组,这意味着所有中断的preemption priority是[0, 15],sub priority恒为0
    * LIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY是调用FreeRTOS中断API的中断最大优先级。换句话说,所有调用FreeRTOS中断API的中断的优先级都必须大于等于这个值


GIT
====
    git log
    ========
        git log --oneline   # 每个提交只显示一行
        git log -n          # 查看最近n个提交
        git log --stat      # 每个提交显示修改的文件以及增删行数
        git log --graph [--all]     # 用ascii字符显示分支树。--all是显示所有分支

    子模块
    ======
        添加子模块:
            git submodule add <仓库地址> <本地路径>        # 这会生成.gitmodules文件。然后你就可以push到github了

        获取子模块:
            # 当你git clone时,子模块只有目录而已,你得执行下面两条命令才行
            git submodule init
            git submodule update

        删除子模块:
            rm -rf 子模块目录 删除子模块目录及源码
            vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
            vi .git/config 删除配置项中子模块相关条目
            rm .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可

        注意:
            * 获取到子模块后,子模块里是没有任何分支的,建议每个子模块都新建一个分支
            * 如果改了子模块而不推送子模块的话,子模块的远程仓库是不更新的


    补丁
    ====
        生成补丁:
            git diff [旧ID] [新ID] > <xxx.diff>       # 注意[旧ID]和[新ID]别写反了
            git format-patch <ID> -n -o XXX.patch  # 生成SHA1为ID的提交相较之前n个提交的所有补丁。n如果是1就是相较于相邻的上一个提交;-o指定补丁存放路径

        检查补丁能否打上:
            git apply --check <XXX.patch 或者 xxx.diff>

        应用补丁:
            git apply <XXX.patch 或者 xxx.diff>
            git am <XXX.patch 或者 xxx.diff>

        注意:
            * 关于组合问题:
                * git apply一般对应于git diff。这个比较简单,就跟unix的diff差不多,它会把补丁中的内容应用到工作区但不提交,你得手动提交
                * git format-patch一般对应于git am。这两个操作会把提交信息一并打入,比git diff + git apply更加智能
                * 其他组合我没试过

    git fetch
    ==========
        # 有时你需要检查当前仓库是否是最新的,但你又不想用git pull,因为你想先看一下远程仓库改了哪些,这时你可以用git fetch
        git fetch origin master:tmp     # 把远程仓库的master分支复制到本地的tmp分支
        git diff tmp                    # 查看远程仓库作了哪些改动
        git pull 或者 git merge tmp     # 合并分支
        git branch -d tmp               # 删除临时分支

    解决中文乱码
    ==========
        git config --global core.quotepath false

    栈(stash)
    ===========
        注意:"git stash"仅仅是把工作区和仓库不同的部分压入栈中,已经压栈的代码相当于仓库的代码。例如:
              $ echo "" > demo.txt
              $ echo "1" >> demo.txt
              $ git stash save "V1"
              $ echo "2" >> demo.txt
              $ git stash save "V2"
              此时如果你git stash pop,那么V2这个栈就会恢复,然而demo.txt仅仅是"2"而已,不是"1\n2"

        git stash                   # 把工作区的代码存入栈中
        git stash save "注释"       # 类似于git stash,但是可是注释

        git stash list              # 列出栈
        git stash show              # 显示栈中的代码和工作区的区别

        git stash pop               # 把栈中的代码恢复到工作区,并清除栈
        git stash apply             # 把栈中的代码恢复到工作区,但是不清除栈

        git stash clear             # 清除所有的栈
        git stash drop + 名称       # 删除一个栈

    提交
    ====
        git commit    # git会调用vim来让你输入信息,适合较多信息输入的场合
        git commit -a 指的是把当前工作目录中没有被git add添加的文件也提交到本地仓库,不推荐这个选项,应该先git add -A在git commit [-m]

        删除指定提交
        ===========
            假设你有c1~c5五个提交,你要删除c5这个提交

            git log --oneline
                eb6a3dd (HEAD -> master) c5
                234ad32 c4
                269f234 c3
                bad34cd c2
                245cf8c c1

            git rebase -i <245cf8c>   # git要指定一个起点的,这里以c1为起点,你也可以以其它为起点
                # 此时弹出vim编辑器,内容大致是:开头几行字描述着各个提交该保留还是删除,下面的字讲的是所有可用的操作是
                意思
                pick bad34cd c2
                pick 269f234 c3
                pick 234ad32 c4
                pick eb6a3dd c5

                # balabalabalabalabalabalabalabala
                # balabalabalabalabalabalabalabala
                # balabalabalabalabalabalabalabala

            # 你想删除c5提交,只需把c5前面的pick改成drop即可。更改之后如下:
                pick bad34cd c2
                pick 269f234 c3
                pick 234ad32 c4
                drop eb6a3dd c5

                # balabalabalabalabalabalabalabala
                # balabalabalabalabalabalabalabala
                # balabalabalabalabalabalabalabala

            # vim保存退出即可

            # 注意:
                * 这个命令还有另外一个功效:它可以删除那种没有任何分支指向的提交,方法是你git rebase -i <...> 以后弹出的vim编辑器什么都不改,直接退出
                * 这个命令还有其他功能,比如压缩提交,修改提交信息等,具体请看输入git rebase后的那个弹窗
                * 如果遇到冲突,有三种操作:
                    * 放弃rebase:git rebase --abort
                    * 跳过此次rebase:git rebase --skip
                    * 合并冲突:(编辑冲突文件),然后git add <冲突文件>,然后git rebase --continue
                        * 编辑冲突文件时你会看到你之前的文件变成了类似下面这样:
                          1111
                          aaaa
                        ++<<<<<<< HEAD
                        ++=======
                        + bbbb
                        + dddd
                        ++>>>>>>> 1a91894... V4
                        这上面,只有开头两行是你的。
                        "++<<<<<<< HEAD"意思是这个符号上面的就是你原本的;
                        "++>>>>>>> <提交SHA1> <提交信息>"意思是这行前面直到"++======="是你之前要做的改动;
                        那些以"+"开头的内容得删掉,不然待会git rebase --continue它们会进到追踪库里。改好这个文件后git add <刚刚你改的文件>,然后git git rebase --continue就行了


    分支
    ====
        git checkout -b <新的branch>  # 创建一个新的分支,然后立马切过去
        git checkout 4c24c3 -- drivers/dma/xxx_dma.c        # 把drivers/dma/xxx_dma.c这个文件恢复到4c24c3这个提交
        git branch -d <要删除的分支名字>        # 删除本地分支
        git push origin --delete <要删除的分支名字>     # 删除远程分支。注意:<要删除的分支名字> 是直接写名字的,比如remotes/origin/bugfix这个分支你只要写bugfix就行了
        git branch -f <分支名> <SHA1>      # 强制移动一个分支到一个提交

    合并
    ====
        git merge -e --no-ff   # -e 合并后可以先看一下改了哪些再提交;--no-ff 不使用fast-forward合并策略,虽然git默认使用这种策略,但是
                               # 不推荐,应为这种策略合并后不会创建新的节点

        合并一个分支上的某个文件
        ======================
            有时候你只想合并一个分支上的某个文件到主线上来。
            注意这个命令并不是真的合并,它只是把合并来的内容写到worktree里面!!!

            git checkeout master                # 先切到主线分支
            git checkout -p deve ccc.txt        # "deve ccc.txt"指的是你想合并deve分支上的ccc.txt文件

            # 然后就会出现类似下面这种提示,你可以选择要不要合并
            # 我在这里输入了个"?"让它打印出y,n,q,a,d,e的意思,一般就用y和n就够了
                --- b/ccc.txt
                +++ a/ccc.txt
                @@ -1 +1,2 @@
                 333
                +333-333-3333
                Apply this hunk to index and worktree [y,n,q,a,d,e,?]? ?
                y - apply this hunk to index and worktree
                n - do not apply this hunk to index and worktree
                q - quit; do not apply this hunk or any of the remaining ones
                a - apply this hunk and all later hunks in the file
                d - do not apply this hunk or any of the later hunks in the file
                e - manually edit the current hunk
                ? - print help


    删除未被跟踪的文件,delete untracked files
    ==========================================
        # 删除 untracked files
        git clean -f

        # 连 untracked 的目录也一起删掉
        git clean -fd

        # 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
        git clean -xfd

        # 在用上述 git clean 前,强烈建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
        git clean -nxfd
        git clean -nf
        git clean -nfd

    删除已经加入跟踪的文件
    ====================
        # 比如.cproject这个文件已经加入版本跟踪系统了,你想把它从版本跟踪系统中移除

        git rm -nr --cached .cproject    # -n:这只是看而已,实际并不移除;-r:递归移除;--cached:从跟踪系统中移除,但仍保留在文件系统中

        注意:
            * 只要有“-n”这个参数就不会删除,只是打印出哪些文件会被删除而已,你如果真的十分确定删除了就去掉这个选项
            * 关于“.gitignore”失效的问题,“.gitignore”只能忽略未被跟踪的文件,假如有个文件先被加入跟踪了你才用“.gitignore”忽略它,那么“.gitignore”将失效。解决办法是:用git rm把那个文件取消跟踪,再git add -A


    查看已跟踪的所有文件
    ==================
        git ls-files                      # 列出所有已跟踪文件
        git ls-tree <分支名> --name-only   # 这个好像是列出working space中的所有文件,不建议用这个

    推送
    ===
        git push origin master      # 推送本地的master分支
        git push origin bugfix:bugfix  # 推送本地的bugfix分支(冒号前面)到远程的bugfix分支(冒号后面),如果远程不存在bugfix分支会自动创建;注意冒号两边不能有空格
        git push origin --tags      # 推送本地的所有标签
        git push origin --all       # 推送本地的所有分支
        git push -u origin develop  # 在远程创建一个新分支develop
        git push -u origin master -f # 强制推送,可以解决"hint: Updates were rejected because the tip of your current branch is behind"问题

    其他
    ====
        git config --global core.editor "vim"      # 把默认的编辑器改为vim
        git remote -v                                # 查看仓库地址


正则表达式
==========
    正则表达式各种语言都有内嵌,标准各有不同,有时同一个正则表达式在不同的语言环境中会有不同的匹配结果。比如VIM,grep以及perl的正则表达式都各不太一样
    正则表达式 [^a]  指的是匹配非a的字符串,[^abc] 匹配的是非a且非b且非c的字符串,val\s*=[^=] 匹配的是val=后面不是=的字符串

    . 匹配任意1个字符(除了\n)
    [ ] 匹配[ ]中列举的字符
    \d 匹配数字,即0-9
    \D 匹配非数字,即不是数字
    \s 匹配空白,即 空格,tab键
    \S 匹配非空白
    \w 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字
    \W 匹配特殊字符,即非字母、非数字、非汉字、非_

    a{3}指的是a出现3次
    (x|y)指的是x或者y

    (?<=abc).    什么的前面等于abc
    (?<!abc).    什么的前面不等于abc
    .(?=abc)    什么的后面等于abc
    .(?!abc)    什么的后面不等于abc

    [^abc]和(?!abc)的区别:
        前者指的是一个不是a也不是b也不是c的字符,后者指的不是abc的单词。
        也就是说前者以字符为单位,后者以单词为单位。另外,后者匹配的并不是非abc的单词,而是非abc的单词的前面的字符串

    正则表达式的一些例子:
    =====================
        (typedef|#define).*uint8_t          ---  typedef或者#define的后面跟上uint8_t
        (?<=\bVERSION\b).*                  ---  匹配#define VERSION 123中的123。注意(?<=xxx)中的xxx必须是定长的,使用GNU的grep要加-P选项,其他的正则表达式不一定支持
        aaa\s*=[^=\r\n]                     ---  匹配aaa后面带"="而不是"=="的字符串,也就是aaa这个变量的赋值语句
        ^.*(?<!root)@ubuntu.*?\$            ---  匹配一个由两部分组成的字符串,第二部分是以非"root"开头的"@ubuntu.*?\$",如果第二部分找到了,那么第一部分则是行首到第二部分的
                                                    任意字符串。比如能匹配"sam@ubuntu14:~$",不能匹配"root@ubuntu14:~$"。
                                                    其中"?"跟"\$"结合表示尽可能少地匹配以"$"结尾的字符串,"?"就是懒惰搜索的意思。

SHELL语言
=========
    分区
    ====
        删除全部分区:wipefs -a -f /dev/sdb

    压缩解压(归档)
    ==============
        cpio
        ====
            压缩:find <PATH> | cpio -ov > <你的归档名>.cpio   # 也可以把find换成ls,那样就是只压缩首层目录
            压缩文件系统:find <PATH> | cpio -H newc -o > <你的归档名>.cpio        # 制作initramfs文件系统得用-H把格式指定为newc格式
            解压:cd <target>; cpio -idmv < /path/to/your-cpio.cpio
            查看:cpio -t -I xxx.cpio
            添加:cpio -o -O xxx.cpio  -A   # 往xxx.cpio添加文件。执行这个命令后你把文件一行一行地输入,一行一个文件(如果是目录,你单输入目录名它不会递归添加,你只能一个一个输),输完按ctrl+d


    查看文件系统类型
    ===============
        df -T
        parted -l
        blkid
        lsblk -f

    查看内核符号表
    ==============
        cat /proc/kallsyms


    浮点运算
    ========
        echo | awk '{printf("%.3f\n", 34/0.459834)}'
        echo 'scale=3;34/0.459834' | bc                    # "scale=3;"的意思是小数点后面有3位


    获取ASCII码
        $ xxd
        $ <直接输入你想要转换的字符,可以输入多个>
        $ ctrl+d


    打补丁
    ======
        注意这是unix的diff,不是git diff

        $ diff -nrNp <目录1> <目录2> > diff.patch        # 生成目录2基于目录1的补丁
        $ cd <目录1>                                    # 打补丁前要进入目录1
        $ patch -p1 < ../diff.patch                     # 打补丁,使目录1变得跟目录2一样


    日期转秒
    =======
        date -d "2012-11-12 13:00:00" +"%s"

    秒转日期
    =======
        date -d@1352692800 +"%Y-%m-%d %H:%M:%S"

    同时移动或拷贝多个文件
    ====================
        mv {a.c,b.c,c.c} folder     # 把mv换成cp就是拷贝


    写入二进制数据到文件
    ==================
        echo -e "\x01\x02" > 111.bin

    读取二进制文件
    ===============
            使用xxd命令
            ===========
                xxd -g {m} -c {n} {文件名}     # 其中m是一个单元字节;n是一行几个单元
                例如:
                        xxd -g 1 -c 8 README.txt
                延伸:
                        这个命令可以被VIM调用:
                                %!xxd -g 1 -c 8

            使用od命令
            ==========
                od -t xC -A [doxn] -N [num] -w<列数> <文件名>

                    -t      指定格式
                    x       16进制格式
                    C       字节格式
                    -w      一行显示几列

                    doxn    d:十进制;o:八进制;x:十六进制;n:不显示地址
                    -N[num] 显示num个单位

                    od --traditional -d

                    --traditional       以传统方式接受参数(od可以以好几种方式接受参数)
                    -d                  每两个字节组成一个十进制数

                举例:
                    od -t xC -A n -N 80 -w8 ECG_Datas.dat   # 以char为单位(-t xC),不显示地址(-A n),总共显示80个单位(-N 80),一行8个单位(-w8)

    文本替换命令
    ============
        sed -i 's/idata//g' Mifare.c   # 把Mifare.c中的idata全部去掉

        # find命令
        find . ! -name 'port.h'   # 查找不是port.h的文件
        find . -regex ".*dat"     # 查找当前目录下的所有.dat文件,注意使用-regex(正则表达式)以后,匹配的不只是文件名
                                    而是绝对路径
        find . \( ! -name "*.dat" \) -a \( ! -name "*.sh" \)    # 当前目录找不是.dat也不是.sh文件的文件
        find . \( \( -name foo.c \) -o \( -name main.c \) \) -printf "${PWD}/%P size=%sByte\n"  # 找main.c和foo.c并打印出它们的绝对路径和大小

    shell的延时
    ===========
        sleep : 默认为秒。
        sleep 1s 表示延迟一秒
        sleep 1m 表示延迟一分钟
        sleep 1h 表示延迟一小时
        sleep 1d 表示延迟一天

        usleep表示微秒

    date命令
    ========
        tm=`date --date="1 hour ago" +%Y%m%d%H%M`       # 获取当前时间的一小时以前的时间
        echo $tm

        tm=`date --date="20181009 1045 1 hour ago" +%Y%m%d%H%M`     # 获取指定时间一小时以前的时间
        echo $tm

    把字符串写入文件
    ================
        如果字符串只有一行
        ==================
            这不难

                echo "111 222" > a.txt
            或者这样:
                for line in "111 222"
                do
                    echo $line >> a.txt
                done

        如果字符串有多行
        ================
            这个知识点很隐蔽,因为刚学这个的人肯定会以为可以像处理单行字符串那样,不行的。

            用cat:
                text="line 1
                line 2
                line 3"

                cat>$file<<EOF
                $text
                EOF

    字符串去重
    =========
        sort -u     # 去除重复只保留一个然后排序
        uniq        # 只去除连续的重复

        引申
        ====
            ls -R | grep -oE '\.[0-9a-zA-Z_\.]+$' | sort -u     # 列出一个目录下的所有文件类型

    列出一个目录下的全部文件
    ======================
        ls -lR | grep -E "^-" | grep -oE ":\w\w .*$" | grep -oE " .*$"

    grep
    ====
        grep -nrE --include=*.{c,h} "mf_"    # -n:打印行号;-r:递归查找;-E:正则表达式;--include=*.{c,h}:去.c和.h文件里面找;"mf_":以mf_开头的东西

        常用的选项:
            -v        反向查找,比如grep -nEv 'src/(uboot|package).*'   意思是src后面不是uboot也不是package


    文本编辑命令
    ============
        awk -F" " '{print NF}' read.txt

            -F" "                指定分隔符是空格
            '{print NF}'         打印每一行的列数
            cat yy.txt | awk '{printf "%-30s%-15s\n",$1,$2}' > yy2.txt
                                 把yy.txt中的第一第二列按固定的缩进打印并输出到yy2.txt
                                 # %-30s表示输出字符串,宽度30位,左对齐.%-15s用来指定第二列的,左
                                 # 对齐,宽度15.两个百分号之间可以没有空格.使用\n对每一行的输出加上换行符
                                 # 如果要表示第10列及以上,用$(10),$(11)...
            awk '{print $(NF-1)}' xxx.txt
                                 # 打印倒数第2列


    清空一个文件
    ===========
        cat /dev/null > dat.bin

C语言
===================
    宏定义封装printf
    ================
    >    #include <stdio.h>
    >    #define DBG(fmt, ...) printf("[%s,%s,%d]:" fmt"\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
    >    int main()
    >    {
    >        DBG("start:%d", 12);
    >        DBG();
    >        return 0;
    >    }


    宏定义又定义数字又定义字符串
    =============================
        struct AAA {
            int x;
            char *p;
        };
        #define PIN(a)   { .x=a, .p="==>"#a  }


    结构体还可以这样赋值
    ====================
        struct BBB {
                int x;
                int y;
        };

        struct AAA {
                int a;
                struct BBB *b;
        };

        int main(void)
        {
                struct AAA A = {
                        .a = 3,
                        .b = &(struct BBB) {        // 结构体的指针成员也可以直接初始化
                                .x = 11,
                                .y = 22,
                        }
                };


                printf("%d\n", A.b->x);

                return 0;
        }


    结构体也可以这样声明
    ====================
        有时你的头文件里需要声明一些函数,但是这些函数的形参和返回值包含了一些来自别的文件的结构体,按
        常规思路,你肯定要把那些定义了那个结构体的文件包含进来才行,但是你也可以这样:

            struct uart_port;
            struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx);

        上面的代码中,struct uart_port是一个空的结构体定义,它的具体定义来自别的文件。
        你这里并没有包含它所在的文件,而是直接来了个空声明:struct uart_port;  这样下面用到它的函数编
        译也能通过。


    结构体也可以这样定义
    ====================
        struct AAA {
            int a;
            int b;
        } k = {
            a: 123,
            b: 456,
        };


    关于typedef
    ===========
        typedef double  fftw_complex[2];
        fftw_complex a;                             // a是一个由两个double型组成的数组
        fftw_complex *b;                            // b是一个指向数组的指针,改数组由两个double型组成

    用宏定义把数字转成字符串
    ======================
        /*
        * 如果#define SAMPLE_RATE (16000)就不灵了
        */
        #define SAMPLE_RATE 16000
        #define STR1(R)  #R
        #define STR2(R)  STR1(R)

        int main()
        {
            printf(STR1(SAMPLE_RATE) "\n");  /* SAMPLE_RATE */
            printf(STR2(SAMPLE_RATE) "\n");  /* 16000 */
            return 0;
        }

    指向数组的指针
    =============
        int main() {
            const char *p= "12345678";
            char (*q)[2] = (char (*)[2])p;

            printf("%c%c\n", q[0][0], q[0][1]);
            printf("%c%c\n", q[1][0], q[1][1]);

            return 0;
        }


    库函数获取毫秒
    ==============
        #include <stdio.h>
        #include <sys/timeb.h>

        long long getSystemTime() {
            struct timeb t;
            ftime(&t);
            return 1000 * t.time + t.millitm;
        }

        int main() {
            long long start=getSystemTime();
            sleep(5);
            long long end=getSystemTime();

            printf("time: %lld ms\n", end-start);
            return 0;
        }

    库函数延时函数
    ===================
        sleep()         秒延时
        usleep()        微秒延时

    scanf用法
    ========
        #include <stdio.h>
        void main() {
            int num, hour, min, sec;
            printf("num=");
            scanf("%d", &num);
            hour = num / 3600;
            min = num % 3600 / 60;
            sec = num % 60;
            printf("%d 小时 %d 分钟 %d 秒\n", hour, min, sec);
        }

    判断闰年
    =======
        公历闰年判定遵循的规律为: 四年一闰,百年不闰,四百年再闰。
        公历闰年的简单计算方法(符合以下条件之一的年份即为闰年)
            1.能被4整除而不能被100整除。
            2.能被100整除也能被400整除。
        闰年是公历中的名词。闰年分为普通闰年和世纪闰年。
        普通闰年:能被4整除但不能被100整除的年份为普通闰年。(如2004年就是闰年,1999年不是闰年);
        世纪闰年:能被400整除的为世纪闰年。(如2000年是世纪闰年,1900年不是世纪闰年)。

    指向数组的指针
    =============
        char a[30] = "123";
        char (*p)[30] = &a;
        printf("p=%p, a=%p\n", p, a);    // p == a

        知识点
        =====
            * 上面的代码中,如果p++,p的地址一次加30
            * char (*p)[30] = &a; 指向数组的指针就是这样赋值的,&a其实是等于a的

    编程坑点
    =========
        * 取局部变量的地址要注意内存释放的问题。当你使用局部变量的地址引用局部变量时,局部变量可能早就被释放了,所以
          如果非要,记得局部变量加volatile
        * 内存、中断、电压(低功耗)是全局的,任何一个函数都要考虑这三个方面
        * 要有错误处理(日志,打印等)程序,switch和if要做好default和else的检查,空的else最好用/*nothing here*/注明
        * 小心函数的重复运行问题,比如task_create反复执行会创建出很多个一样的任务。另外,中断也是可以打断中断的,也是函数的重复运行问题
        * 全局变量和硬件如果被多任务,中断共享要做好保护措施
        * 系统的初始化也有学问,注意系统的各个软硬件的初始化顺序
        * 小心死锁问题,死锁也能导致死机,发生原因一般是嵌套锁
        * 建议加看门狗,小心看门狗初始化失败时可能仍然生效,如果此时你认为看门狗初始化失败而不喂狗那么软件就会一直复位
        * 测试代码的时候要记录栈使用情况,防止栈溢出
        * 小心C语言的宏定义,比如这个:#define SET_MODE(hd, mode)     led_handles[(hd)].mode = (mode)
          当你这样调用时:SET_MODE(1, sta),展开成了led_handles[(hd)].sta = sta,你本想改变mode成员,结果改到sta成员去了
        * 拷贝大的全局变量时要注意共享问题
        * 大坑:把一个局部变量定义成const仍是局部变量,仍是放在栈里面的!!!
        * 小心float和double的精度问题
        * DMA访问内存不会更新Cache(可能还有别的类似的硬件),所以它可以导致Cache和实际内存不一致,所以DMA读写内存前后要做好Cache的同步措施
        * 小心字符串末尾的零。如果一个字符串末尾没有零,那么strcpy这种函数将是灾难性的
        * &(按位与)的优先级比+,-还低
        * 中断过于频繁地执行会导致程序卡慢甚至完全卡死,所以程序要限制中断的执行频率
        * 中断标志位如果不清除可能导致SOC无法进入普通模式
        * 用完的指针,句柄,ID等应当置为一个无效值,比如0,避免后面的代码误认为它仍然有效
        * 小心,不要去访问已经被释放掉的内存
        * 有些编译器,函数只声明,不定义,编译是通过的。这可能有问题
        * 小心父进程比子进程先退出
        * 小心C语言不带花括号的if-else。试看这个代码:
            #define DEBUG()    printf("aaa\n")

            int i = 1;

            if (i == 1)
                DEBUG();
            printf("bbb\n");

            
            当DEBUG被定义的时候打印:
                aaa
                bbb
            但是当DEBUG没被定义的时候打印:
                bbb
            这是不对的,bbb这个字符串本来是一定能打印的,可现在它需要i等于1才能打印了,这不对。正确的做法应该是:
                if (i == 1) {
                    DEBUG();
                }
                printf("bbb\n");


    不安全的C库函数
    ==============
        #ifndef BANNED_H
        #define BANNED_H

        /*
         * This header lists functions that have been banned from our code base,
         * because they're too easy to misuse (and even if used correctly,
         * complicate audits). Including this header turns them into compile-time
         * errors.
         */

        #define BANNED(func) sorry_##func##_is_a_banned_function

        #undef strcpy
        #define strcpy(x,y) BANNED(strcpy)
        #undef strcat
        #define strcat(x,y) BANNED(strcat)
        #undef strncpy
        #define strncpy(x,y,n) BANNED(strncpy)
        #undef strncat
        #define strncat(x,y,n) BANNED(strncat)

        #undef sprintf
        #undef vsprintf
        #ifdef HAVE_VARIADIC_MACROS
        #define sprintf(...) BANNED(sprintf)
        #define vsprintf(...) BANNED(vsprintf)
        #else
        #define sprintf(buf,fmt,arg) BANNED(sprintf)
        #define vsprintf(buf,fmt,arg) BANNED(sprintf)
        #endif

        #endif /* BANNED_H */


PYTHON
======

    编程例子:
    =========
        打印字母A到Z:
            for i in range(ord('A'), ord('Z')):
                print(chr(i))


emWin
=======
    MESSAGEBOX_Create, GUI_MessageBox都是创建消息框函数,区别是前者能重写回调函数(但是回调函数只接
    受WM_PAINT这一个消息),后者不能


STM32HAL库
==========

    怎样初始化一个外设
    ==================
        以串口3为例:
        1. 定义一个串口3的全局句柄USART_HandleTypeDef xxx,后续所有的操作都要用到它
        2. stm32f7xx_hal_msp.c文件的HAL_USART_MspDeInit函数添加串口3的默认初始化函数,一般是关时钟,中断和GPIO默认初始化
        3. stm32f7xx_hal_msp.c文件的HAL_USART_MspInit函数添加串口3外围的初始化函数,包括串口3的时钟和相应的GPIO时钟使能,
           GPIO初始化,串口3的中断初始化
        4. main.c或者其他哪个文件都行,写一个串口3真正的初始化函数,包括波特率,校验位那些

    怎样使用一个中断
    ================
        以串口3为例:
        1. stm32f7xx_it.c文件添加USART3_IRQHandler函数
        2. 重写HAL_UART_XXXtCallback函数
        3. 使用HAL_UART_Receive_IT函数注册中断接收数组
        4. 别忘了在HAL_UART_MspInit中使能串口中断

    其他
    ====
        * HAL库的头文件只需一个#include "stm32f7xx_hal.h"即可
        * 小心DMA,例如你在一个DMA传输函数执行结束后读取内存,这个内存可能仍被DMA控制


Keil MDK
========
    生成bin文件
    ===========
            $K\ARM\ARMCC\bin\fromelf.exe --bin --output=@L.bin !L         # 在工程目录下生成bin文件
            或者
            $K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L     # 在工程目录下的Bin文件夹生成bin文件

    重写函数
    =======
        int $Sub$$main(void)
        {
            __nop();
            $Super$$main();
        }

        知识点:
            * 上面的代码中,$Sub$${函数名}是重写一个函数的意思,$Super$${函数名}是原来的函数的意思(你如果想调用以前的函数就必须得加这个开头$Super$$)。不止于main函数,什么函数都可以这样
            * 利用这个功能,你可以在main函数之前做点其他的代码,比如初始化


    怎样重定向printf
    ===============
        1. 添加段代码
        ============
            #include <stdio.h>
            int fputc(int ch, FILE *f)
            {
                uint8_t temp[1] = {ch};
                HAL_UART_Transmit(&huart1, temp, 1, 2);
                return(ch);
            }

        2. USE Micro Lib
        ================
            点击魔术棒,找到这项勾上


GCC
====
    自动生成头文件依赖关系:
    ======================
        gcc -MM xxx.c        # 不包含库文件
        gcc -M  xxx.c        # 包含库文件

    生成so文件
    =========
        使用"-fPIC -shared",举例:gcc test.c -fPIC -shared -o libtest.so
        注意so文件名必须以lib开头,不然-ltest会报错

        编译时指定so搜索目录
        ====================
            格式:"-L/path/of/libfoo.so -lfoo"

            例子:
                $ cat Makefile
                all: libfaa.so
                    gcc -o test main.c  \
                        -L./lib \
                        -Wl,-rpath-link,./lib \
                        -lfaa

                libfaa.so: libfoo.so
                    gcc faa.c -fPIC -shared -o lib/libfaa.so -L./lib -lfoo

                libfoo.so:
                    gcc foo.c -fPIC -shared -o lib/libfoo.so

                clean:
                    rm -f test
                    rm -f lib/*.so

                # 当so之间出现相互依赖时,用"-Wl,-rpath-link,/path/to/lib/"

        小心cygwin的GCC和Linux的GCC在使用so时不一定是相同的。


    生成a文件
    ========
        使用ar命令把o文件生成a文件
        $ gcc -c foo.c -o foo.o
        $ ar r foo.a foo.o [foo2.o foo3.o ...]


    重定向printf
    ============
        可以参考ST的CubeMX生成代码,以后再写笔记


    链接
    ====
        链接并不是只能用LD,GCC也可以,有时甚至链接只能用GCC
        ================================================

        把一个变量放到指定的段内
        =========================
            __attribute__((section("name")))        # name即是你要指定的段,名字随便取,可以带'.'号

            例如:
                int david __attribute__((section(".user.char.1"))) = 0x123;  # 把david这个变量放到".user.char.1"段中

        利用链接脚本导出符号
        ====================
            在C语言里面,你若想导出一个符号,必然要用extern。但是GCC的链接脚本允许你不用那种方法:

                # 链接脚本中定义好".user.char.1"段
                SECTIONS
                {
                    .text :
                    {
                        ...
                        *(.user.char.1)
                        ...
                    }
                }

                # C工程中定义一个位于".user.char.1"段的变量
                int david __attribute__((section(".user.char.1"))) = 0x123;

                # 此时david这个变量就变成全局变量了,你可以在工程的任意一个位置访问它而不需要extern。

                # 实际上,你还可以把链接脚本改成这样:
                SECTIONS
                {
                    .text :
                    {
                        ...
                        user_start = .;
                        *(.user.char.1)
                        *(.user.char.2)
                        *(.user.char.3)
                        *(.user.char.4)
                        user_end = .;
                        ...
                    }
                }
                # 这种情况适用于当你的自定义段很多的时候,你可以通过获取user_start和user_end的地
                # 址(即&user_start, &user_end)来访问到这个“数组”。Linux内核里面有大量应用

GDB
===
    x/32xb buf   # 查看buf内存
    info locals  # 查看局部变量
    set args     # 设置程序参数
    show args    # 查看已设置的程序参数


ubuntu,树莓派,安卓
=====================
    查找历史命令
    ============
        ctrl+r        # 这个命令很普遍,连busybox都支持

    ubuntu允许root登陆
    ==================
        ubuntu的图形界面和SSH默认都是禁止root登陆的。

        SSH允许root登陆
        ===============
            首先确认你的root已经设置了密码,如果没有则需要设置:
                sudo su
                passwd
                注意你在安装ubuntu的时候设置的那个"root"密码并非真正的root密码,只有进入命令行以后才能设置密码

            sudo vim /etc/ssh/sshd_config
            把
                PermitRootLogin without-password
            改成
                #PermitRootLogin without-password
            再增加
                PermitRootLogin yes
            service ssh restart

        图形界面允许root登陆
        ====================
            vim /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf
            在文末添加这两句:
                greeter-show-manual-login=true
                allow-guest=false

            重新启动ubuntu用root登陆发现有错误,但是能进去:
                Error found when loading /root/.profile

            解决这个错误:
                vim /root/.profile
                把
                    mesg n
                改成
                    tty -s && mesg n

    ubuntu关闭和打开图形界面
    =========================
        暂时性:
            sudo service lightdm stop
            sudo service lightdm start

        永久性:
            sudo vim /etc/default/grub
            把
                GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
            改成
                GRUB_CMDLINE_LINUX_DEFAULT="text"
            update-grub

        经试验,发现在VirtualBOX中开关图形界面对内存占用影响并不大

    man
    ====
        man是分级的,如果你输入'man open'发现它给的是BASH的open而不是C语言的open,那么可以输入'man 2 open'
        1 用户命令, 可由任何人启动的。
        2 系统调用, 即由内核提供的函数。
        3 例程, 即库函数,比如标准C库libc。
        4 设备, 即/dev目录下的特殊文件。
        5 文件格式描述, 例如/etc/passwd。
        6 游戏
        7 杂项, 例如宏命令包、惯例等。
        8 系统管理员工具, 只能由root启动。
        9 其他(Linux特定的), 用来存放内核例行程序的文档。
        n 新文档, 可能要移到更适合的领域。
        o 老文档, 可能会在一段期限内保留。
        l 本地文档, 与本特定系统有关的。

    永久设置IP地址
    ===============
        方法一:
        vi /etc/profile
            /sbin/ifconfig eth0 192.168.1.100 netmask 255.255.255.0

        方法二(推荐):
        vi /etc/network/interfaces
            auto eth0
            iface eth0 inet static
            address 192.168.3.90
            gateway 192.168.3.1
            netmask 255.255.255.0
            #network 192.168.3.0
            #broadcast 192.168.3.255

        这个文件还可以设置DHCP:
            auto eth0
            iface eth0 inet dhcp

        然后重启或者sudo /etc/init.d/networking restart生效

        方法三:
            参见@设置开机启动的程序,在里面用ifconfig就行了


    设置开机启动的程序
    ==================
        方法一:
            改这几个文件:
                /etc/profile
                ~/.bash_profile
                ~/.bash_login
                ~/.profile


        方法二:
            $ vi /etc/rc.local


        方法三:
            1       获取runlevel
                    $ runlevel
                    或者
                    $ vi /etc/inittab
                        ...
                        id:5:initdefault:        # 这行指明了开机运行级别是5,所以后面得去/etc/rc5.d这个目录
                        ...

            2       $ cd /etc/rc5.d
                        touch S99myscript.sh        # S是start的意思,K是kill的意思,D是disable的意思
                                                    # 开机的时候,如果是S开头那么你的脚本会被传入一个start参数,如果是K则传入stop,如果是D则不执行
                                                    # 99是它的运行顺序,越大越后,剩下就是你的脚本名字了
                        chmod +x S99myscript.sh


    查看块设备
    ==========
        lsblk
        ls /sys/block
        cat /proc/devices


    查看mtd设备
    ===========
        cat /proc/mtd
        lsblk


    安装eclipse
    ===========
        apt-get install eclipse-platform    # eclipse-platform和eclipse不同,前者没有任何语言开发插件;后者带有JAVA开发插件
        apt-get install eclipse-cdt         # 安装C/C++的开发插件

        想用新版eclipse只能手动安装。新一点的ubuntu应该都是可以的,我在ubuntu12上安装的时候,明明JRE是1.8版本,但是eclipse说它是
        1.6版本,跑不起来。


    卸载软件
    =======
        sudo apt-get remove xfce4*  # 把xfce4*改成你要卸载的软件
        sudo apt-get autoremove
        sudo apt-get clean


    查看ubuntu版本
    =============
        lsb_release -a
        cat /etc/issue


    开启和关闭ubuntu桌面
    ==================
        sudo service lightdm stop                       # 临时关闭
        sudo service lightdm start                      # 临时开启
        sudo systemctl set-default multi-user.target    # 永久关闭
        sudo systemctl set-default graphical.target     # 永久开启


    安装桌面
    ========
        lxde:   sudo apt-get install lxde
        xfce:    sudo apt-get install xfce4

        有些桌面一旦安装好,重启电脑即可看到,如果没有,使用命令startx启动桌面,使用pkill Xorg关闭
        桌面(先用ctrl+alt+F2切到另一个SSH)

        如果startx启动桌面后一片空白:
            # 按下ctrl + alt + f1
            mv ~/.config ~/.config_bak
            reboot


    树莓派技巧
    ==========
        用SD卡开启SSH(仅适用于官方系统):在SD卡根目录新建一个空文件SSH
        用SD卡配置WIFI(仅适用于官方系统):在SD卡根目录新建wpa_supplicant.conf文件,填入
            ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
            network={
              ssid="你的WIFI名"
              psk="WIFI密码"
            }

    查看系统是多少位
    ===============
        getconf LONG_BIT

    su失败的解决办法
    ================
        问题:
            pi@raspberrypi:/ $ su
            密码:
            su:鉴定故障

        解决办法:
            sudo su

    开启SSH服务
    ==========
        ubuntu默认是不使能SSH的,你得自己安装配置(树莓派是安装了SSH的,只是没有使能)

        sudo apt-get install openssh-server
        sudo systemctl restart ssh

        其他:
            当你用ssh登录的时候会发现终端变成黑白的,那是因为ubuntu默认就是关闭ssh的彩色显示的。这时你可以编辑~/.bashrc文件,把force_color_prompt赋值yes即可

    挂载U盘
    =======
        $ cat /proc/partitions      # 或者lsblk
            major minor  #blocks  name

               8        0   62522712 sda
               8        1     524288 sda1
               8        2   60996608 sda2
               8        3    1000448 sda3
               8       16   31326208 sdb    # 这是U盘
               8       17   31326176 sdb1   # 这是U盘的分区
        $ sudo mount /dev/sdb1 /mnt
        $ sudo umount /mnt # 或者 sudo umount /dev/sdb1

    udev语法简单讲解
    ===============
        用户配置目录位于/etc/udev/rules.d/中,系统预置位于/lib/udev/rules.d/中。文件名必须以.rules结尾。
        rules文件由一系列的规则组成,每个规则又由一系列键值对组成。一个规则就是一行,如果一个规则包含多个键值对,则各个键值对之间要用逗号隔开。
        键值对的逻辑有点像C语言的if else。简单说就是,如果第一个键值对使用了==且成立,那么该规则后面的所有键值对就得以执行,其他诸如!=亦是同理。
        举例:(这是网上截取别人的,并不好用)
            ####################################################################################
            1  KERNEL!="sd[b-z]*", GOTO="exit"
            2  ACTION=="add", PROGRAM!="/sbin/blkid %N", GOTO="exit"
            3
            4  # import some useful filesystem info as variables
            5  IMPORT{program}="/sbin/blkid -o udev -p %N"
            6
            7  # get the label if present, otherwise assign one based on device/partition
            8  ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}"
            9  ENV{ID_FS_LABEL}=="", ENV{dir_name}="flash_drive_%k"
            10
            11 # create the dir in /media and symlink it to /mnt
            12 ACTION=="add", RUN+="/bin/mkdir -p '/media/%E{dir_name}'"
            13
            14 LABEL="exit"
            ####################################################################################
            1. 如果设备名不等于sd[b-z]*,那就跳到exit标签,也就是退出整个rules文件。KERNEL是设备的内核名称,%k是它的简写。
            2. 如果第1行成立,那么如果设备是插入,那就执行"/sbin/blkid %N", GOTO="exit"。PROGRAM!=的意思是需要检查返回值。
            5. 把"/sbin/blkid -o udev -p %N"的执行结果导入program这个变量。%N是设备节点的名称(也就是设备文件的名称)。
            8. 如果ID_FS_LABEL这个环境变量非空,那么把变量ID_FS_LABEL赋给dir_name。%E{*}等价于ENV{*},就是取某个环境变量的意思。
            9. 和第8行很类似,不同的是dir_name被赋值为flash_drive_%k。%k是设备的内核名称,比如/dev/sdb1
            12. 如果是插入动作则执行"/bin/mkdir -p '/media/%E{dir_name}'"。
            14. 一个标签,跟C语言的标签是一个意思。

            # udev的语法太复杂,自己写的话要是写不好可能会导致系统崩溃,所以建议安装XFCE等桌面应用来实现U盘自动挂载


    udev自动挂载U盘和SD卡
    ====================
        这是一个简单的udev挂载U盘的rules文件,使用时注意/etc/udev/rules.d/99-mount-sam.rules中的sd[b-z],它不支持名为sda的设备,因为
        sda一般是系统硬盘,一旦这个rules文件挂载了系统硬盘可能会使整个系统崩溃。所以,在使用前要看系统本身的硬盘是什么,然后修改这个文件,使
        这个文件不会动系统硬盘。

        $ sudo vim /etc/udev/rules.d/99-mount-sam.rules     # 以99开头是因为我想让系统的rules文件先执行
            ACTION=="add", KERNEL=="sd[b-z][0-9]", RUN+="/home/sam/.mount.sh %k"
            ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd[b-z][0-9]", RUN+="/home/sam/.umount.sh %k"

        $ vim /home/sam/.mount.sh
            #!/bin/sh

            # if the device had been mounted already, no need to mount again
            df | grep -E "^/dev/$1" 1>/dev/null 2>&1
            if [ $? -eq 0 ]; then
                exit
            fi

            mkdir -p /mnt/$1
            mount /dev/$1 /mnt/$1
            sync

        $ vim /home/sam/.umount.sh
            #!/bin/sh
            umount /mnt/$1
            rmdir /mnt/$1
            sync

        $ chmod +x /home/sam/.mount.sh /home/sam/.umount.sh

        $ /etc/init.d/udev restart

    树莓派改软件源
    =============
        sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
        sudo vi /etc/apt/sources.list
            把以前的都删掉,加入deb http://mirrors.ustc.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi
            保存退出
        sudo cp /etc/apt/sources.list.d/raspi.list /etc/apt/sources.list.d/raspi.list.bak
        sudo vi /etc/apt/sources.list.d/raspi.list
            把以前都删了,加入deb http://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian/ stretch main ui
            保存退出
        sudo su
        apt-get update && apt-get upgrade

    ubuntu软件源
    ============
        设置国内软件源
        ==============
            1. 改source.list文件
                sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
                sudo vi /etc/apt/sources.list
                    # 打开https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/
                    # 选中自己的ubuntu版本
                    # 把网页内容粘贴进去
                    # 注意每个ubuntu版本都有自己的名字。通过“lsb_release -a”可以看到
                    # 检查/etc/apt/sources.list中的名字是否跟自己的ubuntu名字一致

                或者:
                    去中科大的官网下载一个sources.list文件(可以输入"ubuntu"查找),复制里面的内容到/etc/apt/sources.list就行了。
                    这个方法也不推荐,因为中科大的source.list屏蔽了一些东西,会导致一些问题。

                或者:
                    sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
                    这个方法简单粗暴,但不推荐,会导致一些问题。

            2. 更新
                sudo apt-get update
                sudo apt-get upgrade(可选)

        软件源更新失败
        ==============
            输入apt update报错:
                Certificate verification failed: The certificate is NOT trusted. The certificate chain uses expired certificate.  Could not handshake: Error in the certificate
            解决办法:
                /etc/apt/sources.list换回原来的
                apt update
                apt install ca-certificates
                /etc/apt/sources.list换回自己的
                apt update

    查看有哪些设备连入了本设备的热点
    =============================
        ip neigh

    搭建ftp服务器
    ============
        ubuntu:
            sudo apt-get install vsftpd
            sudo vim /etc/vsftpd.conf
                把#write_enable=YES改成write_enable=YES
                保存退出
            sudo service vsftpd restart     # 当然用sudo systemctl restart vsftpd或者sudo /etc/init.d/vsftpd restart也可以

        windows:
            ftp://<ubuntu的IP地址>  # 例如ftp://192.168.43.254
            账号:pi
            密码:<pi>的密码

        结果:
            ubuntu的/home/pi目录共享到了windows上。有关vsftp的所有配置都在/etc/vsftpd.conf文件中
            你可以用man vsftpd或者man vsftpd.conf获取帮助

    搭建samba服务器
    ==============
        ubuntu:
            sudo apt-get update
            sudo apt-get install samba samba-common-bin
            sudo vim /etc/samba/smb.conf
            # 在文件的最底部添加这段代码。注意"="两边可以带空格;好像不可以用tab来当空格
                [pi]                    # "pi"你可以换成自己的名字
                    path = /work
                    public = yes
                    writable = yes
                    available = yes
                    browseable = yes
                    guest ok = no
                    write list = Administrator  # 如果你要共享给windows,用户名一般是这个
                    valid users = Administrator
                保存退出

            #####################################
            注意属主和属组问题,否则可能不能修改文件
            adduser Administrator --force-badname  # 最好还是添加一个跟windows同名的用户Administrator,因为linux里面一般是root,一个文件被root改了就变成root的了
            adduser Administrator sudo
            chown -R Administrator:Administrator /work

            #####################################
            # 如果前面你没有指定'write list'和'valid users'那么你需要这步
            sudo chmod -R 7777 /home/pi # 7777中第1个7的意思是uid,gid和粘滞位都是1。
                                        # 粘滞位置1意味着other用户不能删除user和group创建的文件,置0就可以,也就是chmod 6777
                                        # 注意共享路径必须是整条路径都能访问,目标路径才能访问。比如共享路径是/A/B,用户能访问B但不能访问/A是不行的,必须
                                        # A也能访问才行

            #####################################
            # 不常用
            sudo smbpasswd -a pi        # 往samba中添加用户pi,系统会提示输入密码。注意这个用户必须是ubuntu真实存在的用户,你可能需要user add pi
                                        # 没有这步也可以,那么你只能用other用户登录,就没法删除或者更改pi创建的文件

            sudo service smbd restart   # 当然用sudo systemctl restart smbd或者sudo /etc/init.d/smbd restart也可以

        windows:
            文件管理器找到"网络>>RASPBERRYPI>>pi",输入账号pi以及他的密码即可

    搭建nfs服务器
    ============
        Windows挂载nfs非常卡,还不支持中文,不建议使用

        windows挂载nfs:
            控制面板--->程序--->打开或关闭Windows功能。把NFS服务子项全部勾选,"基于UNIX的应用程序子系统"勾选
            cmd窗口输入:mount \\192.168.43.254\home\pi x: 或者 文件管理器--->映射网络驱动器--->填入\\192.168.43.254\home\pi--->确定
            打开文件浏览器,你可以看到ubuntu的nfs挂载上来了,但是是只读的,尽管你的ubuntu已经规定可写了,网上有解决办法,但是我试了不成功
            如果卸载挂载,cmd窗口输入:umount -a -f

        ubuntu搭建nfs服务器:
            # 首先确保内核是使能nfsd的(不排除还有别的什么nfs的东西)
            sudo apt-get install nfs-kernel-server      # 这步你可能并不需要,因为很多Linux发行版都是自带nfs的。你可以先看看/etc/export是否存在,如果存在则说明安装了
            sudo vim /etc/exports
                末尾添加这个
                /home/pi    *(rw)     # "/home/pi"是你要共享出去的路径;"*"指的是任何网段的主机都可以访问你的nfs,你也可以写成192.168.1.*/24或者server0.example.com;"(rw)"指的是别人可读可写
                保存退出
            chmod 777 /home/pi
            sudo systemctl restart nfs-kernel-server.service    # 当然你用sudo service <服务名> restart或者sudo /etc/init.d/<服务名> restart也可以
            exportfs -rv        # 更新共享配置,一般不用

        linux挂载nfs:
            查看别人共享出了哪些目录:
                showmount -e 192.168.1.200

            临时挂载:
                mount -t nfs -o nolock 192.168.1.100:/nfs /mnt
                如果报错:
                    mount.nfs: an incorrect mount option was specified
                就加个选项:
                    mount -t nfs -o nolock,nfsvers=3 192.168.1.100:/nfs /mnt
                如果挂载等很久就这样:
                    mount -t nfs -o nfsvers=3 192.168.1.100:/nfs /mnt

            开机自动挂载:
                vi /etc/fstab
                添加:
                    192.168.1.100:/nfs        /mnt    nfs        _netdev,v3        0    0    # _netdev意思是等网络起来了再挂。",v3"可以不要
                保存退出
                mount -a        # 立即挂载/etc/fstab指定的文件系统

    搭建tftp服务器
    =============
        $ mkdir /share_tftp
        $ chmod -R 777 /share_tftp        # 这里不能填7777,否则不能上传

        $ apt-get install tftp-hpa tftpd-hpa xinetd
        $ vim /etc/xinetd.d/tftp
            service tftp
            {
                socket_type     = dgram
                protocol        = udp
                wait            = yes
                user            = root
                server          = /usr/sbin/in.tftpd
                server_args     = -s /share_tftp -c
                disable         = no
                per_source      = 11
                cps             = 100 2
                flags           = IPv4
            }

        $ vim /etc/default/tftpd-hpa
            # /etc/default/tftpd-hpa
            TFTP_USERNAME="tftp"
            TFTP_DIRECTORY="/share_tftp"
            TFTP_ADDRESS="0.0.0.0:69"
            TFTP_OPTIONS="-l -c"        # 假设-s就不能上传了

        $ service tftpd-hpa start/restart

        # 测试(另一台电脑)
        $ tftp x.x.x.x
        put 123.txt /share_tftp/123.txt
        get /share_tftp/123.txt 123.txt
        quit

eclipse
========
    * 在Windows下,当你用eclipse编译c代码时,工具链(gcc,gdb,make等)得自己准备,网上工具链一大把,推荐使用Qt自带的MinGW,别用CodeBlocks的MinGW,虽然它更全,但是在eclipse下不能正常工作。
    * eclipse在设置工具链时有时不能马上见效,可以删除工程,删除文件系统里的.settings,.project,.cproject文件再重新创建工程


zsh美化
========
    Linux一般使用bash作为SHELL,但除了bash,还有个zsh,ohmyzsh就是一个美化zsh的软件

    安装
    ====
        1.
            sudo apt-get install zsh
            sudo chsh -s $(which zsh)

        2.
            有了zsh,你还得安装它的配置文件,这样终端才会五彩斑斓起来。这个配置文件最流行的就是"Oh My Zsh":
            sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
            有时,这个命令并不管用,你可以试试这个:
            git clone https://github.com/ohmyzsh/ohmyzsh.git
            ./ohmyzsh/tools/install.sh      # 小心这步执行到一半会让你输入密码(如果你选择了把zsh作为默认shell的话),如果你输错密码就得执行uninstall.sh重来

bash美化
=======
    类似于zsh,bash也有美化软件。如果你不适应zsh的语法,又不喜欢bash默认的配色,可以试试使用bash美化软件,oh-my-bash就是其中之一。
    地址:https://github.com/ohmybash/oh-my-bash.git。使用跟ohmyzsh很类似。
    使用官网推荐的curl和wget经常无效,所以推荐手动安装:
    git clone git://github.com/ohmybash/oh-my-bash.git ~/.oh-my-bash
    cp ~/.bashrc ~/.bashrc.orig
    cp ~/.oh-my-bash/templates/bashrc.osh-template ~/.bashrc
    # 然后你根据自己的需要编辑~/.bashrc
    source ~/.bashrc
    # 我发现这个软件有些主题显示的时间是12小时,很不习惯,可以这样改:
    #   vim ~/.oh-my-bash/themes/candy/candy.theme.sh
    #       THEME_CLOCK_FORMAT="%H:%M:%S"   # 把原来的THEME_CLOCK_FORMAT赋值语句删除,添加这句


设置网络
=======
    nmtui                   字符GUI工具,进去以后你自然懂得怎么用。推荐
    nm-connection-editor    图形GUI工具,进去以后你自然懂得怎么用。在没有桌面环境和x-windows的系统中没法用
    nmcli                   纯字符工具,操作超级复杂,不记了
    ifconfig                纯字符工具,操作比较复杂,不记了


查找一个软件在哪个包
==================
    apt-cache search xxx       # 只是列出一些跟xxx相关的软件,具体要哪个还得你自己定


设置时区
=======
    sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime


终端分屏工具terminator
======================
    $ sudo apt-get install terminator
    $ terminator

    ctrl+shift+o            水平分割
    ctrl+shift+e            垂直分割
    ctrl+shift+上下左右     调整窗口大小
    ctrl+tab                切换窗口
    ctrl+shift+w            关闭窗口


MobaXterm
==========
    解决有时X11启动不来的问题:
        1. 使用Ubuntu默认用户登录
        2. cp /home/<能用X11的用户>/.Xauthority /home/<不能用X11的用户>/.Xauthority


X11
====
    X11有时不能打开:
        确认你的X11登陆的用户跟SSH登陆的用户一致,比如你用非root用户sam登陆X11和SSH,然后SSH中你用sudo su切成root就不行了。
        解决办法是(不一定奏效):
            cp /home/sam/.Xauthority /root
        或者直接改用root登陆X11,可这在ubuntu是不允许的,ubuntu默认不允许root直接登陆,只能用普通用户登陆后sudo su切成root。
        这时你可以看看本文的另一个主题:
            SSH允许root登陆


    MobaXterm启动X11有时会报这个错误:
        Putty X11 proxy: unable to connect to forwarded X server: Network error:connection refused
        X11 authentication failed

        解决思路是使用其他的X11服务器,比如Xming,XManager(XShell的兄弟产品)

        方法一:putty + Xming
            其中Xming服务器的使用配置如下:
            - Launch XMing on Windows client
            - Launch Putty
                * Fill in basic options as you know in session category
                * Connection -> SSH -> X11
                    -> Enable X11 forwarding
                    -> X display location = :0.0
                    -> MIT-Magic-Cookie-1
                    -> X authority file for local display = point to the Xming.exe executable

        方法二:MobaXterm + Xming
            跟方法一差不多。

        方法三:Xshell + Xmanager
            1.  安装Xmanager,这个跟Xshell不是一起的。
            2.  运行Xstart
                填好主机IP,用户名,密码。命令那一栏填上:
                    /usr/bin/gnome-session --display $DISPLAY
                如果不行就点旁边的">"键,选择其他的图形界面
                保存
                运行
            3.  打开Xshell
                当前会话属性
                    连接
                        SSH
                            隧道
                                X11转移
                                    转发X11连接到:
                                        Xmanager

        方法四:Xshell + Xming
             当前会话属性
                连接
                    SSH
                        隧道
                            X11转移
                                转发X11连接到:
                                    X DISPLAY   (localhost0.0)

    解决X11文字不能复制粘贴的问题:
        这个只能重启了,很多X11服务器都有这个问题。


    Xmanager输入字符会重复一次的问题:
        参考这篇文章,管用:
            https://blog.csdn.net/qq_45832958/article/details/105179342

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值