c语言变参的宏定义

#define debug(format, ...) fprintf(stderr, format, __VA_ARGS__)

等价于:

#define debug(format, args...) fprintf (stderr, format, args)

C99规范支持了可变参数的宏,可以用VA_ARGS传递…。
用法如下:

#include <stdio.h>

#define DEBUG(format, ...) printf(format, __VA_ARGS__)

int main(){
        int turn=1;

        printf("enter %s\n", __func__);
        DEBUG("test:%d\n",turn);
        return  0;
}

但这样又引入了一个新的问题,当…为空时,会导致链接错误。这里引入’##’操作。如果可变参数被忽略或为空,’##’操作将使预处理器(preprocessor)去除掉它前面的那个逗号。

#include <stdio.h>

#define DEBUG(format, ...) printf(format, ##__VA_ARGS__)

int main(){
        int turn=1;

        printf("enter %s\n", __func__);
        DEBUG("test:%d\n",turn);
        DEBUG("test\n");
        return  0;
}

参考:
宏中"#"和"##"的用法

docker常用操作命令

  • volume相关指令
    Command Description
    docker volume create Create a volume
    docker volume inspect Display detailed information on one or more volumes
    docker volume ls List volumes
    docker volume prune Remove unused local volumes
    docker volume rm Remove one or more volumes
    docker volume update Update a volume (cluster volumes only)

  • 查询依赖

    docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=xxxxxx)

    xxxxxx标识的就是被依赖的image id

  • this deletes ALL STOPPED containers, images, volumes.

    docker system prune --all --volumes
  • 创建
    可以在hub.docker.com上创建一个新的repo,或者自己复制一个现有的repo.
    绑定github后,也可以直接通过Dockerfile来发生成最新的镜像

  • 查看镜像列表

    docker images
  • 查看容器列表

    docker ps
  • 查看所有的容器

    docker ps -a
  • 查看最近创建的容器

    docker ps -l
  • 查找在指定 image 之后创建的 image 中的父 image

    docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=xxxxxx)
  • 获取镜像

    docker pull name/reponame
  • 删除

    docker rm          Remove one or more containers
    docker rmi         Remove one or more images
  • 删除已经退出运行的容器

    docker ps -a | grep "Exited" | awk '{print $1 }'|xargs docker rm
  • 删除none镜像

    docker images |grep none |awk '{print $3}'|xargs docker rmi
  • 要删除全部image的话

    docker rmi $(docker images -q)
  • 停止所有容器
    停止容器,才能删除所包含的image

    docker stop $(docker ps -a -q)
  • ssh登录docker
    在开发阶段免不了需要ssh上去进行现场配置。
    从ssh启动docker:

    docker run -it  username/imagename /bin/bash

    ssh登陆,并挂载本地目录,默认读写权限:

    docker run -it -v 宿主绝对路径:docker绝对路径 username/imagename /bin/bash

    这样启动的入口进程是bash,整整需要启动的服务进程,再此模式下要自己手动启动

  • attach

    docker attach --sig-proxy=false $CONTAINER_ID

    如果当前的不是bash,attach上去也无法进行ssh,只有是上面这种直接启动ssh并后台运行的情况,attach上去,才进入ssh

  • exec
    在Docker1.3版本开始,提供了一个更方便的命令exec。可以直接在容器内运行命令。所以我们ssh登录docker最方便的是exec:

    docker exec -ti 7f16264f7ab4 /bin/bash
  • detach
    docker没有专用的命令,目前不能使用eixit,因为会导致主进程退出。使用ctl+p+ctl+q来detach当前的进程

  • 查看底层信息,比如IP等

    docker inspect --format='{{.NetworkSettings.IPAddress}}' $CONTAINER_ID
  • commit
    docker commit CONTAINER_ID docker_hub用户名/镜像名:tagname

  • push

    docker push docker_hub用户名/镜像名:tagname
  • docker hub的使用
    如果你使用hub.docker.com来管理你的镜像,那么就涉及到账户登录,如果你docker pull时遇到如下的提示:

    unauthorized: incorrect username or password

    那么说明你登录信息过期了,需要重新登录:

    docker login
  • 查看日志
    查看最新30分钟的日志:

    docker logs --since 30m CONTAINER_ID

    其他需求,请详细查看logs 的参数

  • 查看详细信息
    docker inspect id|name

  • 其他参考
    资深专家都知道的 Docker 常用命令---源英文:Top Docker Commands Any Expert Should Know
    Docker之容器的创建、启动、终止、删除、迁移等
    docker入门——构建镜像

关于DEBIAN_FRONTEND noninteractive的正确用法

DEBIAN_FRONTEND这个环境变量,告知操作系统应该从哪儿获得用户输入。如果设置为"noninteractive",你就可以直接运行命令,而无需向用户请求输入(所有操作都是非交互式的)。
这在运行apt-get命令的时候格外有用,因为它会不停的提示用户进行到了哪步并且需要不断确认。非交互模式会选择默认的选项并以最快的速度完成构建。
请确保只在Dockerfile中调用的RUN命令中设置了该选项,而不是使用ENV命令进行全局的设置。因为ENV命令在整个容器运行过程中都会生效,所以当你通过BASH和容器进行交互时,如果进行了全局设置那就会出问题。

正确的做法 - 只为这个命令设置ENV变量

RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3

错误地做法 - 为接下来的任何命令都设置ENV变量,包括正在运行地容器

ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y python3

我的示例如下:

FROM ubuntu:trusty
MAINTAINER mryqu 
RUN \
 DEBIAN_FRONTEND=noninteractive apt-get update && \
 DEBIAN_FRONTEND=noninteractive apt-get -y install wget curl && \
 DEBIAN_FRONTEND=noninteractive apt-get -y autoremove && \
 DEBIAN_FRONTEND=noninteractive apt-get clean

ameba sdk的debugger设置

  1. setup

    两个配置项的内容:
$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\preload.mac
$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\8195a.ddf
  1. download

    配置内容:
    $PROJ_DIR$\tmp.board
  2. extra options

    配置内容:
    --drv_vector_table_base=0x0
    此选项如果没有设置,会出现 __vector_table 问题

Ameba-Z开发板jlink连接问题

拿到的是Ameba-Z_DEV01_1V0开发板,开发时,发现jlink V9一直连接不上
解决:按下J17的按键,就可以连接成功

于是找了下原理图,分析下根本原因:
J17连接的是cmsis-dap芯片的reset,所以原因就是cmsis-dap对swd影响造成的。

更彻底的可以物理上断开swd和断开cmsis-dap芯片的供电,断开R5和R29。

STM32Cube 扩展包开发 checklist

STMCube 包括:
- STM32CubeMX,一个图形化配置工具软件,可以生成c语言的配置代码。
- 一个详尽的STM32Cube MCU开发包,为每个STM32系列提供:

-- STM32Cube HAL,涵盖所有外设。
-- Low-layer APIs,提供一个轻量快速的面向专家的接口层,LL只包含部分外设。
-- 一系列中间件,例如RTOS,USB,TCP/IP和图形界面等。
-- 一系列应用和示例。

简介

STM32Cube MCU Package 运行在 cortex-M 处理器上。

质量规范

  1. 确保所有工具兼容,不能有error和warnings。
  2. 开发要符合 MISRA C 编码标准,并且要进行静态代码分析。

封装规范

  1. 本地的STM32Cube MCU 组件包不能修改。release note 不能删除,不用的文件也不能删除,源代码也不能被修改。
  2. release notes应该包含以下的内容:
    -- 主要变更点
    -- 变更的内容,包括新开发的组件和被引用的组件。
    -- 工具链,编译器
    -- 支持的设备或者开发板
    -- 局限性说明
  3. 每一个组件都应该有release note
  4. 文件或者相关的软件都要版本管理,并且在文件头或者release note中添加修改日期。
  5. 要求license 说明。
  6. 新的BSP驱动应该添加在 \Drivers\下。如果要添加新的组件,应该添加在\drivers\BSP\Components下。
  7. 新的中间件应该添加在 \Middlewares\Third_Party目录下。
  8. 用户示例应该添加到\Projects\下 ,并且遵循以下归类:
    -- examples: 只使用HAL和BSP的例子。
    -- Applications:使用中间件的例子。
    -- Demonstration:全部使用的例子。
  9. example目录组织如下:
    -- \Inc for header files
    -- \Src for source files
    -- \ toolchain preconfigured project, all temporary files haveto be deleted
    -- ApplicationN_Name.ioc: STM32CubeMX
    project file
    -- .extSettings: STM32CubeMX project
    additional settings file (optional, if needed)
    -- \Binary containing binary file, using thisnaming format "USER_BOARD_REF_ApplicationN_Nam
    e_VX.Y.Z.bin"
  10. 使用TMC32CubeMX生成的例子
  11. .ioc .mxproject 和.extSettings文件必须和readme.txt文件在同一级目录,都在示例工程的根目录下。
  12. ioc 文件遵循 ApplicationN_Name.ioc格式的命名格式。
  13. 媒体文件应该放在 \Utilities\Media目录下。
  14. readme文件包含了每一个资源的copyright、license,务必要添加。
  15. 所有的上位机PC软件必须放在 \uTILITIES\pc+Software 下。
  16. 要为每一个示例添加预配置的EWARM,MDK-ARM,SW4STM32工程文件
  17. 顶层目录命名应该符合规范:STM32CubeExpansion____VX.Y.Z
  18. 发布的bin或者lib必须遵循:
    -- 包含对应的头文件
    -- release note
    -- lib如果跟编译器相关,要遵循命名规则:LibraryNameV_CMx_C_O.a

中间件规范

  1. RTOS工程必须包含 CMSIS-RTOS API
  2. 一个新的中间件应该和硬件或者平台无关,并且有相应的接口层支持。
  3. 中间件要有中间件接口文件,可供用户定制或者更新。

文档规范

  1. 新增的组件都要有相应的API文档
  2. 每一个示例都应该有详尽的注释,功能描述和硬件设置说明。

go build介绍

写的很好:go build -- 极客学院

go build
go build命令用于编译我们指定的源码文件或代码包以及它们的依赖包。

例如,如果我们在执行go build命令时不后跟任何代码包,那么命令将试图编译当前目录所对应的代码包。例如,我们想编译goc2p项目的代码包logging。其中一个方法是进入logging目录并直接执行该命令:

hc@ubt:~/golang/goc2p/src/logging$ go build
因为在代码包logging中只有库源码文件和测试源码文件,所以在执行go build命令之后不会在当前目录和goc2p项目的pkg目录中产生任何文件。

插播:Go语言的源码文件有三大类,即:命令源码文件、库源码文件和测试源码文件。他们的功用各不相同,而写法也各有各的特点。命令源码文件总是作为可执行的程序的入口。库源码文件一般用于集中放置各种待被使用的程序实体(全局常量、全局变量、接口、结构体、函数等等)。而测试源码文件主要用于对前两种源码文件中的程序实体的功能和性能进行测试。另外,后者也可以用于展现前两者中程序的使用方法。

另外一种编译logging包的方法是:

hc@ubt:~/golang/goc2p/src$ go build logging
在这里,我们把代码包logging的导入路径作为参数传递给go build命令。另一个例子:如果我们要编译代码包cnet/ctcp,只需要在任意目录下执行命令go build cnet/ctcp即可。

插播:之所以这样的编译方法可以正常执行,是因为我们已经在环境变量GOPATH中加入了goc2p项目的根目录(即~/golang/goc2p/)。这时,goc2p项目的根目录就成为了一个工作区目录。只有这样,Go语言才能正确识别我们提供的goc2p项目中某个代码包的导入路径。而代码包的导入路径是指,相对于Go语言自身的源码目录(即$GOROOT/src)或我们在环境变量GOPATH中指定的某个目录的src子目录下的子路径。例如,这里的代码包logging的绝对路径是~/golang/goc2p/src/logging。而不论goc2p项目的根文件夹被放在哪儿,logging包的导入路径都是logging。显而易见,我们在称呼一个代码包的时候总是以其导入路径作为其称谓。

言归正传,除了上面的简单用法,我们还可以同时编译多个Go源码文件:

hc@ubt:~/golang/goc2p/src$ go build logging/base.go logging/console_logger.go logging/log_manager.go logging/tag.go
但是,使用这种方法会有一个限制。作为参数的多个Go源码文件必须在同一个目录中。也就是说,如果我们想用这种方法既编译logging包又编译basic包是不可能的。不过别担心,在需要的时候,那些被编译目标依赖的代码包会被go build命令自动的编译。例如,如果有一个导入路径为app的代码包,同时依赖了logging包和basic包。那么在执行go build app的时候,该命令就会自动的在编译app包之前去检查logging包和basic包的编译状态。如果发现它们的编译结果文件不是最新的,那么该命令就会先去的编译这两个代码包,然后再编译app包。

注意,go build命令在编译只包含库源码文件的代码包(或者同时编译多个代码包)时,只会做检查性的编译,而不会输出任何结果文件。

另外,go build命令既不能编译包含多个命令源码文件的代码包,也不能同时编译多个命令源码文件。因为,如果把多个命令源码文件作为一个整体看待,那么每个文件中的main函数就属于重名函数,在编译时会抛出重复定义错误。假如,在goc2p项目的代码包cmd(此代码包仅用于示例目的,并不会永久存在于该项目中)中包含有两个命令源码文件showds.go和initpkg_demo.go,那么我们在使用go build命令同时编译它们时就会失败。示例如下:

hc@ubt:~/golang/goc2p/src/cmd$ go build showds.go initpkg_demo.go
# command-line-arguments
./initpkg_demo.go:19: main redeclared in this block
        previous declaration at ./showds.go:56
请注意上面示例中的command-line-arguments。在这个位置上应该显示的是作为编译目标的源码文件所属的代码包的导入路径。但是,这里显示的并不是它们所属的代码包的导入路径cmd。这是因为,命令程序在分析参数的时候如果发现第一个参数是Go源码文件而不是代码包,则会在内部生成一个虚拟代码包。这个虚拟代码包的导入路径和名称都会是command-line-arguments。在其他基于编译流程的命令程序中也有与之一致的操作,比如go install命令和go run命令。

另一方面,如果我们编译的多个属于main包的源码文件中没有main函数的声明,那么就会使编译器立即报出“未定义main函数声明”的错误并中止编译。换句话说,在我们同时编译多个main包的源码文件时,要保证其中有且仅有一个main函数声明,否则编译是无法成功的。

现在我们使用go build命令编译单一命令源码文件。我们在执行命令时加入一个标记-v。这个标记的意义在于可以使命令把执行过程中构建的包名打印出来。我们会在稍后对这个标记进行详细说明。现在我们先来看一个示例:

hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg_demo.go
hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -v initpkg_demo.go 
command-line-arguments
hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg_demo  initpkg_demo.go
我们在执行命令go build -v initpkg_demo.go之后被打印出的command-line-arguments”`就是命令程序为命令源码文件initpkg_demo.go生成的虚拟代码包的包名。顺带说一句,

命令go build会把编译命令源码文件后生成的结果文件存放到执行该命令时所在的目录下。这个所说的结果文件就是与命令源码文件对应的可执行文件。它的名称会与命令源码文件的主文件名相同。

顺便说一下,如果我们有多个声明为属于main包的源码文件,且其中只有一个文件声明了main函数的话,那么是可以使用go build命令同时编译它们的。在这种情况下,不包含main函数声明的那几个源码文件会被视为库源码文件(理所当然)。如此编译之后的结果文件的名称将会与我们指定的编译目标中最左边的那个源码文件的主文件名相同。

其实,除了让Go语言编译器自行决定可执行文件的名称,我们还可以自定义它。示例如下:

hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -o initpkg initpkg_demo.go 
hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg    initpkg_demo.go
使用-o标记可以指定输出文件(在这个示例中指的是可执行文件)的名称。它是最常用的一个go build命令标记。但需要注意的是,当使用标记-o的时候,不能同时对多个代码包进行编译。

标记-i会使go build命令安装那些编译目标依赖的且还未被安装的代码包。这里的安装意味着产生与代码包对应的归档文件,并将其放置到当前工作区目录的pkg子目录的相应子目录中。在默认情况下,这些代码包是不会被安装的。

除此之外,还有一些标记不但受到go build命令的支持,而且对于后面会提到的go install、go run、go test等命令同样是有效的。下表列出了其中比较常用的标记。

表0-1 go build命令的常用标记说明

标记名称    标记描述
-a  强行对所有涉及到的代码包(包含标准库中的代码包)进行重新构建,即使它们已经是最新的了。
-n  打印编译期间所用到的其它命令,但是并不真正执行它们。
-p n    指定编译过程中执行各任务的并行数量(确切地说应该是并发数量)。在默认情况下,该数量等于CPU的逻辑核数。但是在darwin/arm平台(即iPhone和iPad所用的平台)下,该数量默认是1。
-race   开启竞态条件的检测。不过此标记目前仅在linux/amd64、freebsd/amd64、darwin/amd64和windows/amd64平台下受到支持。
-v  打印出那些被编译的代码包的名字。
-work   打印出编译时生成的临时工作目录的路径,并在编译结束时保留它。在默认情况下,编译结束时会删除该目录。
-x  打印编译期间所用到的其它命令。注意它与-n标记的区别。
我们在这里忽略了一些并不常用的或作用于编译器或连接器的标记。在本小节的最后将会对这些标记进行简单的说明。如果读者有兴趣,也可以查看Go语言的官方文档以获取相关信息。

下面我们就用其中几个标记来查看一下在构建代码包logging时创建的临时工作目录的路径:

hc@ubt:~/golang/goc2p/src$ go build -v -work logging
WORK=/tmp/go-build888760008
logging
上面命令的结果输出的第一行是为了编译logging包,Go创建的一个临时工作目录,这个目录被创建到了Linux的临时目录下。输出的第二行是对标记-v的响应。这意味着此次命令执行时仅编译了logging包。关于临时工作目录的用途和内容,我们会在讲解go run命令和go test命令的时候详细说明。

现在我们再来看看如果强制重新编译会涉及到哪些代码包:

hc@ubt:~/golang/goc2p/src$ go build -a -v -work logging
WORK=/tmp/go-build929017331
runtime
errors
sync/atomic
math
unicode/utf8
unicode
sync
io
syscall
strings
time
strconv
reflect
os
fmt
log
logging
怎么会多编译了这么多代码包呢?可以确定的是,代码包logging中的代码直接依赖了标准库中的runtime包、strings包、fmt包和log包。那么其他的代码包为什么也会被重新编译呢?

从代码包编译的角度来说,如果代码包A依赖代码包B,则称代码包B是代码包A的依赖代码包(以下简称依赖包),代码包A是代码包B的触发代码包(以下简称触发包)。

go build命令在执行时,编译程序会先查找目标代码包的所有依赖包,以及这些依赖包的依赖包,直至找到最深层的依赖包为止。在此过程中,如果发现有循环依赖的情况,编译程序就会输出错误信息并立即退出。此过程完成之后,所有的依赖关系也就形成了一棵含有重复元素的依赖树。对于依赖树中的一个节点(代码包)来说,它的直接分支节点(前者的依赖包),是按照代码包导入路径的字典序从左到右排列的。最左边的分支节点会最先被编译。编译程序会依此设定每个代码包的编译优先级。

执行go build命令的计算机如果拥有多个逻辑CPU核心,那么编译代码包的顺序可能会存在一些不确定性。但是,它一定会满足这样的约束条件:依赖代码包 -> 当前代码包 -> 触发代码包。

标记-p n可以限制编译过程中任务执行的并发数量,n默认为当前计算机的CPU逻辑核数。如果在执行go build命令时加入标记-p 1,那么就可以保证代码包编译顺序严格按照预先设定好的优先级进行。现在我们再来编译logging包:

hc@ubt:~/golang/goc2p/src$ go build -a -v -work -p 1 logging
WORK=/tmp/go-build114039681
runtime
errors
sync/atomic
sync
io
math
syscall
time
os
unicode/utf8
strconv
reflect
fmt
log
unicode
strings
logging
我们可以认为,以上示例中所显示的代码包的顺序,就是logging包直接或间接依赖的代码包按照优先级从高到低排列后的排序。

另外,如果在命令中加入标记-n,那么编译程序只会输出所用到的命令而不会真正运行。在这种情况下,编译过程不会使用并发模式。

在本节的最后,我们对一些并不太常用的标记进行简要的说明:

-asmflags
此标记可以后跟另外一些标记,如-D、-I、-S等。这些后跟的标记用于控制Go语言编译器编译汇编语言文件时的行为。

-buildmode
此标记用于指定编译模式,使用方式如-buildmode=default(这等同于默认情况下的设置)。此标记支持的编译模式目前有6种。借此,我们可以控制编译器在编译完成后生成静态链接库(即.a文件,也就是我们之前说的归档文件)、动态链接库(即.so文件)或/和可执行文件(在Windows下是.exe文件)。

-compiler
此标记用于指定当前使用的编译器的名称。其值可以为gc或gccgo。其中,gc编译器即为Go语言自带的编辑器,而gccgo编译器则为GCC提供的Go语言编译器。而GCC则是GNU项目出品的编译器套件。GNU是一个众所周知的自由软件项目。在开源软件界不应该有人不知道它。好吧,如果你确实不知道它,赶紧去google吧。

-gccgoflags
此标记用于指定需要传递给gccgo编译器或链接器的标记的列表。

-gcflags
此标记用于指定需要传递给go tool compile命令的标记的列表。

-installsuffix
为了使当前的输出目录与默认的编译输出目录分离,可以使用这个标记。此标记的值会作为结果文件的父目录名称的后缀。其实,如果使用了-race标记,这个标记会被自动追加且其值会为race。如果我们同时使用了-race标记和-installsuffix,那么在-installsuffix标记的值的后面会再被追加_race,并以此来作为实际使用的后缀。

-ldflags
此标记用于指定需要传递给go tool link命令的标记的列表。

-linkshared
此标记用于与-buildmode=shared一同使用。后者会使作为编译目标的非main代码包都被合并到一个动态链接库文件中,而前者则会在此之上进行链接操作。

-pkgdir
使用此标记可以指定一个目录。编译器会只从该目录中加载代码包的归档文件,并会把编译可能会生成的代码包归档文件放置在该目录下。

-tags
此标记用于指定在实际编译期间需要受理的编译标签(也可被称为编译约束)的列表。这些编译标签一般会作为源码文件开始处的注释的一部分,例如,在$GOROOT/src/os/file_posix.go开始处的注释为:

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
最后一行注释即包含了与编译标签有关的内容。大家可以查看代码包go/build的文档已获得更多的关于编译标签的信息。

-toolexec
此标记可以让我们去自定义在编译期间使用一些Go语言自带工具(如vet、asm等)的方式。

go安装或者版本升级

之前go还停留在1.5现在要升级到最新版。
go是通过环境变量来关联版本的,所以安装和升级其实是一个流程。
对于升级,直接删除或者重命名原来的目录,将新包解压到原来的位置就可以了。
对于安装,就是直接解压,然后配置环境变量,参看 mac下零基础学习go语言-2-开发环境的搭建

下载

golang 官方下载

安装

tar -C /usr/local -xzf go1.11.2.linux-amd64.tar.gz

结束