当前位置:首页 > 内核
  • Linux 5.6内核将支持USB4:代码不足2000行

    Linux 5.6内核将支持USB4:代码不足2000行

    据最新消息,Linux 5.6版内核会正式支持USB4,预计将在2020年4月份发布。 USB4标准是今年9月刚公布的,基于Intel开放出来的雷电协议,带宽再次翻番到40Gbps,与雷电3完全相同,同时向下兼容雷电3、USB 3.x/2.0,而且只有Type-C一种接口。 两个多月前,Intel开源工程师就向Linux内核提交了支持USB4的代码和补丁,新代码还不到4000行,因为USB4基本就是将雷电3的翻版,很多代码也都是通用的。 而最终呈现在Linux 5.6内核里的USB4相关代码,只有不足2000行,非常紧凑,但要啥有啥,包括主机与设备支持、源代码和一系列其他功能。 Windows 10何时原生支持USB4还没有说法,但肯定也不会多么复杂。

    时间:2020-01-15 关键词: 内核 Linux 代码 雷电3 usb4

  • linux网络——iptables与网关

    linux网络——iptables与网关

    概述 最近做关WiFi家庭网关项目,接触到很多新的知识,本文对linux的iptables、网关中的NAT转换、DMZ、端口映射和端口触发等概念进行总结 linux iptables linux iptables是linux2.6内核以后使用的一套网络工具,对其进行合理的配置,可实现防火墙、NAT转换等网关功能 好,那么iptables怎么配置,这些配置是如何实现的呢 iptables配置方法 应用层的iptables是一个可执行程序,iptabltes- h可看到命令帮助,这里首先要说明一下iptables中的3个层次递进的概念:表(table)、链(chain)和规则(target),iptables应用程序命令就是对某个表的某个链的某个规则操作 iptables默认有4张表,nat、filter、mangle和raw,项目中使用了nat和filter,所以这里只说nat和filter 每个表都有几条默认的链,nat默认有PREROUTING、INPUT、OUTPUT、POSTROUTING4个链,filter默认有INPUT、FORWARD、OUTPUT3个链,表不可增减,但是表中的链可自定义增加或删除 命令格式 : iptables -t [table] -[opt] [chain] -j ... 命令的格式变化太多,且无需记住所有命令,关键是实际中如何使用,常用哪些,后续再说 iptables内核实现 这里不会分析源码,只注重大局和框架,上图最方便 上图表示的是网络数据从网卡驱动接收程序中跳转到协议层后,都经历了哪些东东,这些过程属于linux的net子系统,与设备无关 所有报文都经过这个图,从PREROUTING这里,报文的mac已经被剥离,即从以后是三层处理,主要关注报文的ip地址,先抛开这些点是干嘛的,理一理数据报的流向 PREROUTING点表示还未进行路由之前,经过路由后,报文的流向无非是两个结果,要么该报文是发往本机的,即上面一条路,要么是发往其他设备的,走下面一条路 如果是发往本机,则经过IUPUT点,到达Localhost,即发往上层协议处理,处理之后,还会经过一个路由,判断发往哪里 如果是发往其他设备,则经过FORWARD点进行转发 最后所有数据报都会经过POSTROUTING点,出设备 这些点和iptables中的表和链是什么关系?此即彼 iptables在内核中的实现是通过netfilter完成的,本质上是在这些预先设置好的点上添加各种内核钩子函数,当程序执行到这些点上时,按照iptables应用程序配置的规则进行处理,是接收、丢弃、转发,还是NAT转换…,iptables和内核通信通过netlink完成,当然出于某些特殊需求,你也可以自己添加钩子函数,怎么添加,看/linux-2.6-xxx/net/netfilter/下的几个源码自然会了 NAT转换 NAT转换是干嘛?转换ip地址,为什么要转换?一是地址不够了,二是保护内网 地址不够的问题就要涉及到网络地址划分了,不展开说,地址分为A、B、C、D、E五类,所有地址中选取一些地址作为私有地址,只用于内网,它们是 10.0.0.0 ~ 10.255.255.255 172.16.0.0 ~ 172.31.255.255 192.168.0.0 ~ 192.168.255.255 怎么保护,外网不可访问内网,内网可以访问外网 iptables的表nat专门用于设置和处理NAT转换的规则,内网访问外网资源时,将源ip地址转换为一个外网地址,该过程叫SNAT,iptables设置后,也可以让外网访问内网部分资源,将目的ip地址转换为一个内网地址,该过程叫DNAT,说白了,核心在于对网络结构的理解,对iptables进行配置,以控制数据流的走向 DMZ DMZ是干嘛的,官话是位于外网和防火墙之间的一个区域,可放置一些私人服务器资源,供外网访问,上图来理解 上图中,共有三个区域,外网、DMZ和内网,一般用户家中如果不放置私人服务器等东东,没有DMZ 外网在网关左侧,DMZ和内网在网关右侧,表明DMZ和内网都是私有地址,内网包括用户会使用的PC、手机等设备,这些设备必须与外网严格隔离,不可被外网入侵,DMZ区域放置一些可以被外网访问的服务器 一般的规则是:外网可访问DMZ,不可访问内网,DMZ不可访问外网和内网,内网可访问外网和DMZ 但是规则都是人定的,关键在于iptables的配置,怎么玩都行其实

    时间:2019-11-21 关键词: 内核 防火墙

  • 内核启动流程分析

    内核启动流程分析

    内核启动流程分析之编译体验 Linux源码编译过程 方法: 解压缩下载好的linux-2.6内核源代码 ; 给内核打补丁 ; 打补丁命令:patch -p n 会生成一个.config文件->再执行内核配置命令:make menuconfig 就会在默认配置基础之上,出现一个配置菜单,就可以此基础之上进行配置了。 使用厂家提供的设置文件 把厂家的配置文件复制成.config文件,再make menuconfig. cp config_厂家 .config -> make menuconfig 补充: menuconfig 中的基本操作: 1. 高亮字母是索引,直接敲相应字母会跳转到相应索引 2. [ ] 中输入’?’ 会跳到帮助信息 3. 输入‘/’ 会跳到搜索功能 编译内核 直接执行:make 会编译生成 zImage 或者执行:make uImage 就会生成uImage uImage就是 :头部+ 真正的内核(vmlinux) 内核启动流程分析之配置 配置的结果是生成.config文件,打开这个.config文件,可以看到有很多的配置项,配置成y或M。 我们以一个配置项:CONFIG_DM9000 为例,看一下谁在使用这个配置。 搜索一下:grep 'CONFIG_DM9000' * -nR 可以看到使用的包括几类: C源码: CONFIG_DM9000 一个宏,一般赋值为1 来源于include/linux/autoconf.h 子目录Makefile :drivers/net/Makefile 在这里体现y和M的区别 内核子目录Makefile 格式: obj-y +=xxx.o //表示xxx.c文件会被编译到内核里面去 obj-m +=xxx.o //表示xxx.c文件会被编译成内核模块.ko文件 include/config/auto.conf 也是来源于.config,也是自动生成的 这个文件被顶层的Makefile包含 include/linux/autoconf.h 这个文件是自动生成的,来源于 .config 执行:make uImage 时执行的操作 1. .config自动生成include/linux/autoconf.h文件 2. .config自动生成include/config/auto.conf文件 3. autoconf.h给C源码使用 4. auto.conf文件被包含到顶层的Makefile中,然后在子目录Makefile中被用到: obj-$(CONFIG_DM9000 ) +=dm9000.o `auto.conf中 CONFIG_DM9000 = y` 当变量$(CONFIG_DM9000 )等于y时,就会编译进内核;等于M,会编译成模块;如果没有设置的话,也不处理,表示没有使用。 内核启动流程分析之Makefile 子项目下的Makefile 已知有文件:a.c b.c 要在Makefile中设置成编译进内核,可用:obj-y +=a.o b.o 实现 如果用a.c b.c 组合成一个模块呢? 可用: obj -m +=ab.o ab-objs :=a.o b.o 这样,编译时 a.c 会编译成 a.o ; b.c 编译成 b.o ; 然后 a.o 和 b.o 链接成ab.ko 这个在Documentationkbuildmakefiles.txt中都有说明 架构相关的Makefile 执行:make uImage 时,Makefile会定位到 arch/arm/Makefile 这个Makefile肯定会被包含到顶层Makefile中去。 果然在顶层目录的Makefile中包含这句:include $(srctree) /arch/$(ARCH)/Makefile 顶层目录的Makefile 执行:make uImage 时,会依赖哪些文件呢,顺序是什么? 我们上面知道在arch/arm/Makefile中uImage的依赖为vmlinux->根据跟踪,得到如下索引: 执行:#make uImage 进行编译,可以看到这行命令: 给出了链接文件等等,依赖文件和上面分析的是一一对应的。 我们得到分析流程的两大结果: 第一个文件:arch/arm/kernel/head.S 链接脚本(定义内核中的文件怎么排布的): arch/arm/kernel/vmlinux.lds 内核启动流程分析之内核启动 篇幅较长,点我跳转

    时间:2019-10-01 关键词: 内核 内核启动流程分析

  • 锐龙3000处理器的CPU、IO温度揭秘 95°C高温怕不怕

    锐龙3000处理器的CPU、IO温度揭秘 95°C高温怕不怕

    现代的高性能处理器除了要拼性能之外,还有一个因素不容忽视,那就是CPU核心温度,导致温度高的原因很复杂,核心数、频率、工艺、功耗等都会影响,但我们能知道的就是温度高了很不好,会导致性能下降。 在锐龙3000系列处理器中,AMD宣传的一个重点就是不仅性能比友商酷睿处理器更好,而且温度、功耗也低。我们实际测试之后也知道,锐龙3000处理器高负载下的温度也不低。 那么锐龙3000系列的内部温度到底是怎样的呢?TH网站德国站的编辑Igor Walossek做了一期很有意思的视频,不放散热器的情况下用红外温度计测试了CPU内部的温度。 他测试的是一款不超过8核的锐龙3000处理器,里面有两部分组成—;—;上面较大的核心是12nm工艺的IO核心,下面较小的是7m CPU核心。 在没有散热器的情况下,他用红外温度计追踪了CPU内部的IO核心、CPU核心的温度。 首先看CPU核心的温度,为了保护CPU,AMD设置了热阈值,温度上限是94.5°C,顶盖内外的温差大概是16K,也就是16°C,所以他视频中看到的CPU核心温度在80°C左右。 上面的12nm IO核心没有热阈值,温差也不知道,观察到的温度达到了85°C。 上面的温度依然不是极限状态下的,因为没有散热器,Igor Walossek也不敢玩得太过,以免CPU被高温损毁。

    时间:2019-10-01 关键词: CPU 内核 温度 io 锐龙3000

  • 内核态与用户态通信方式——Nelink

    内核态与用户态通信方式 Linux下内核空间与用户空间进行通信的方式主要有system call、sysctl、procfs、模块参数、debugfs、relayfs、sysfs和netlink等。 Why NetlinK full-duplex 模块参数、sysfs、procfs、debugfs和relayfs都是基于文件系统,用于内核向用户发送消息;sysctl和system call是用户向内核发送消息。它们都是单工通信方式。netlink是一种特殊的通信方式,用于在内核空间和用户空间传递消息,是一种双工通信方式。使用地址协议簇AF_NETLINK,使用头文件include/linux/netlink.h。 simple to add 为新特性添加system call、sysctl或者procfs是一件复杂的工作,它们会污染kernel,破坏系统的稳定性,这是非常危险的。Netlink的添加,对内核的影响仅在于向netlink.h中添加一个固定的协议类型,然后内核模块和应用层的通信使用一套标准的API。 Netlink Socket APIs 用户空间 创建 int socket(int doamin, int type, int protocal); domain填AF_NETLINK,type为SOCK_RAW或者SOCK_DGRAM。protocal可选NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_ARPD、NETLINK_ROUTE6和NETLINK_IP6_FW,或者传入自定义协议类型。 绑定 int bind(int fd, (struct sockaddr *)&nladdr, sizeof(nladdr)); netlink地址结构如下 struct sockaddr_nl{ sa_family_t nl_family; //AF_NETLINK unsigned short nl_pad; //0 __u32 nl_pid; //进程pid __u32 nl_groups; //多播组掩码 } 如果进程仅仅使用一个netlink,nl_pid可以传入该进程的pid, my_pid = getpid(); 如果一个进程的多个线程需要不同的netlink,使用如下方法获得nl_pid: pthread_self nlmsg_len; msg.msg_iov = &iov; msg.msg_iovlen = 1; 接收 接收时需要创建足够的buf, struct sockadddr_nl nladdr; struct msghdr msg; struct iovec iov; iov.iov_base = (void *)nlh; iov.iov_len = MAX_NL_MSG_LEN; msg.msg_name = (void *)&nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; recvmsg(fd, &msg, 0); 内核空间 内核空间的API由net/core/af_netlink.c支持。 可以在netlink.h中添加自定义协议类型: #define NETLINK_TEST 17 创建 struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len)); unit是netlink.h中定义的协议类型,*input是一个回调函数指针,接收到消息会触发这个函数。 发送 发送消息使用struct sk_buff *skb。 用以下方式设置本地地址: NETLINK_CB(skb).groups = local_groups; NETLINK_CB(skb).pid = 0; 因为是在内核,pid = 0 用以下方式设置目的地址: NETLINK_CB(skb).dst_groups = dst_groups; NETLINK_CB(skb).dst_pid = dst_pid; 发送单播函数为 int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock); *sk为netlink_kernel_create()返回的结构体指针; skb->data指向消息; pid为应用程序的pid; block表明如果当接收缓冲非法时数据分块还是返回错误值。 发送多播函数为 void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation); group是所有需要接收多播消息的组掩码和; allocation是内核内存分配标志,中断上下文中使用GFP_ATOMIC,其他情况使用GFP_KERNEL。 销毁 sock_release(nl_sk->socket); example user-space 初始化 #define MAX_NLMSG_PAYLOAD_LEN 128 #define NETLINK_GROUP_MASK(group) (1 MAX_NLMSG_PAYLOAD_LEN)) { return -1; } memset((void*)&iov, 0x0, sizeof(iov)); memset((void*)&msg, 0x0, sizeof(msg)); msg_len = NLMSG_SPACE(data_len); nlh = (struct nlmsghdr *)malloc(msg_len); if (NULL = nlh) { return -2; } /* fill nlmsghdr */ memset(nlh, 0x0, msg_len); nlh->nlmsg_len = msg_len; nlh->nlmsg_pid = getpid(); nlh->nlmsg_type = msg_type; nlh->nlmsg_flags = 0; if (data) { memcpy(NLMSG_DATA(nlh), data, msg_len); } /* fill iovec */ iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; msg.msg_iov = &iov; msg.msg_iovlen = 1; if (sendmsg(fd, &msg, 0) < 0) { /* error process */ free(nlh); return -3; } free(nlh); return 0; } 接收 int usr_netlink_recv(int fd, char *buf, int len) { struct iovec iov = {buf, len}; struct sockaddr_nl nl_src_addr; struct msghdr msg; int recv_len; memset(&msg, 0x0, sizeof(struct msghdr)); memset(&nl_src_addr, 0x0, sizeof(struct sockaddr_nl)); msg.msg_name = (void *)&nl_src_addr; msg.msg_namelen = sizeof(nl_src_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; recv_len = recvmsg(fd, &msg, 0); if (recv_len < 0) { /* recv error */ } else if (recv_len == 0) { /* recv EOF */ } return recv_len; } kernel 初始化 static struct sock *sg_knl_xxx_sk = NULL; static int sg_knl_xxx_pid = -1; void knl_netlink_init() { struct net init_net; sg_knl_xxx_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, knl_neklink_recv, NULL, THIS_MODULE); } 接收 void knl_netlink_recv(struct sock *skb) { struct nlmsghdr *nlh = nlmsg_hdr(skb); sg_knl_xxx_pid = nlh->nlmsg_pid; } 发送 int group_mask(int group) { return (1 MAX_PAYLOAD_LEN)) { /* error process */ return; } skb = alloc_skb(msg_len, GFP_KERNEL); if (!skb) { /* erro process */ return; } memset((void *)skb, 0x0, msg_len); nlh = nlmsg_put(skb, 0, 0, msg_type, msg_len, 0); if(data) { memcpy(NLMSG_DATA(nlh), data, data_len); } NETLINK_CB(skb).pid = 0; /*from kernel */ NETLINK_CB(skb).dst_group = group_mask(group); /* if unicast */ netlink_unicast(sg_knl_xxx_sk, skb, sg_knl_xxx_pid, MSG_DONTWAIT); /* if mulicast */ //nlmsg_multicast(sg_knl_xxx_sk, skb, 0, group_mask(group), 0); }

    时间:2019-09-02 关键词: 通信 内核

  • 兆易创新推出GD32V系列RISC-V内核32位通用MCU新品

    兆易创新推出GD32V系列RISC-V内核32位通用MCU新品

    2019年8月22日,北京 — 业界领先的半导体供应商兆易创新GigaDevice(股票代码603986)宣布,在行业内率先将开源指令集架构RISC-V引入通用微控制器领域,正式推出全球首个基于RISC-V内核的GD32V系列32位通用MCU产品,提供从芯片到程序代码库、开发套件、设计方案等完整工具链支持并持续打造RISC-V开发生态。 作为GD32 MCU家族基于RISC-V内核的首个产品系列,全新的GD32VF103系列RISC-V MCU面向主流型开发需求,以均衡的处理效能和系统资源为RISC-V进入市场主流应用提供了高性价比的创新之选。新品首批提供了14个型号,包括QFN36、LQFP48、LQFP64和LQFP100等4种封装类型选择,并完整保持了与现有产品在软件开发和引脚封装方面的兼容性。这种前所未有的创新性设计在GD32的Arm®内核产品与RISC-V内核产品之间搭建起快速通道,将跨越处理器内核的产品选型和设计切换变得灵活自如,从而更易于实现代码移植并缩短开发周期。全面适用于工业控制、消费电子、新兴IOT、边缘计算、人工智能及垂直行业的深嵌入式市场应用。   全面优化的RISC-V处理器内核 GD32VF103系列MCU采用了全新的基于开源指令集架构RISC-V的Bumblebee处理器内核,是兆易创新(GigaDevice)携手中国领先的RISC-V处理器内核IP和解决方案厂商芯来科技(Nuclei System Technology)面向物联网及其它超低功耗场景应用自主联合开发的一款商用RISC-V处理器内核。 Bumblebee内核采用32位RISC-V开源指令集架构并支持定制化指令,更优化了中断处理机制。不仅配备了64位宽的实时计时器、可以产生RISC-V标准定义的计时器中断,还支持数十个外部中断源,具有16个中断级别和优先级,并支持中断嵌套和快速向量中断处理机制。低功耗管理可以支持两级休眠模式。内核支持标准JTAG接口及RISC-V调试标准,适用于硬件断点和交互式调试。Bumblebee内核也支持RISC-V标准的编译工具链,以及Linux/Windows图形化集成开发环境。 Bumblebee内核设计了二级变长流水线微架构,配备精简的指令预取单元和动态分支预测器,并融入多种低功耗设计方法。能够以二级流水线的代价,达到传统架构三级流水线的性能和频率,实现了业界一流的能效比与成本优势。这使得GD32VF103系列MCU在最高主频下的工作性能可达153 DMIPS,CoreMark®测试也取得了360分的优异表现,相比GD32 Cortex®-M3内核产品性能提升15%的同时,动态功耗降低了50%,待机功耗更降低了25%。 GD32VF103系列RISC-V 内核32位通用MCU   主流均衡的首发产品组合 GD32VF103系列RISC-V MCU提供了108MHz的运算主频,以及16KB到128KB的片上闪存和6KB到32KB的SRAM缓存,gFlash®专利技术支持内核访问闪存高速零等待。Bumblebee内核还内置了单周期硬件乘法器、硬件除法器和加速单元应对高级运算和数据处理的挑战。 芯片采用2.6V-3.6V供电,I/O口可承受5V电平。配备了1个支持三相PWM互补输出和霍尔采集接口的16位高级定时器可用于矢量控制,还拥有多达4个16位通用定时器、2个16位基本定时器和2个多通道DMA控制器。全新设计的中断控制器(ECLIC)提供了多达68个外部中断并可嵌套16个可编程优先级,以增强高性能控制的实时性。 为广泛的主流应用配备的多种外设资源,包含多达3个USART、2个UART、3个SPI、2个I2C、2个I2S、2个CAN2.0B和1个USB 2.0 FS OTG,以及外部总线扩展控制器(EXMC)。其中,全新设计的I2C接口支持快速Plus (Fm+)模式, 频率最高可达1 MHz (1MB/s),是以往速度的两倍。SPI接口也已经支持四线制并新增多种传输模式,还可方便扩展Quad SPI NOR Flash实现高速访问。内置的USB 2.0 FSOTG接口可提供Device、HOST、OTG等多种模式。外部总线扩展控制器(EXMC)更方便连接NOR Flash,SRAM等外部存储器。 新品集成了2个采样率高达2.6M SPS的12位高速ADC,提供了多达16个可复用通道,并支持16-bit硬件过采样滤波功能和分辨率可配置功能,还拥有2个12位DAC。多达80%的GPIO具有多种可选功能还支持端口重映射,持续以灵活丰富的连接性满足主流开发应用需求。 GD32VF103系列RISC-V内核通用32位MCU产品线 芯来科技CEO胡振波表示:“兆易创新是中国集成电路产业的标杆,也是中国通用MCU供应商的龙头。芯来科技目前已经具备领先的RISC-V处理器内核IP和工具链研发能力,并处于中国RISC-V嵌入式处理器研发与产业化的最前列。双方的合作必将让RISC-V落地生根,为中国通用MCU在AIoT时代带来新突破、形成新格局,并与广大用户携手共赢。” 兆易创新执行副总裁、MCU事业部总经理邓禹表示:“RISC-V体系已经在全球快速崛起,成为半导体产业及工业控制、物联网、智能终端等应用领域的迅猛发展趋势。兆易创新在行业内首家推出基于RISC-V架构的32位通用MCU产品并持续打造RISC-V开发生态,也将进一步满足市场对于开放性架构的差异化需求并有利于发挥成本优势,让不断丰富完善的GD32 MCU‘百货商店’持续为用户提供更多创新之选。”   持续打造的RISC-V开发生态 兆易创新一直在为GD32生态系统提供丰富和完善的支持,包括多种开发板和应用软件在内的RISC-V开发生态也已准备就绪,GD32V系列产品的使用者可以利用全新的开发工具和程序代码库轻松实现设计构想。新增的开发工具包括GD32VF103V-EVAL全功能评估板以及GD32VF103R-START、GD32VF103C-START和GD32VF103T-START入门级学习板,可以分别对应四种不同封装和管脚,方便用户进行开发调试。另外还提供了GD32VF103-BLDC电机控制开发板、GD-LINK调试量产工具以及一系列来自合作伙伴的GD32 RISC-V终端设计方案。 兆易创新还联合芯来科技为GD32V系列MCU提供了免费集成开发环境Nuclei Studio。这个全新IDE基于开源的Eclipse架构,并集成了GCC、OpenOCD等RISC-V相关工具。用户可以快速上手并方便的完成代码编写、交叉编译、在线调试、程序烧写等一系列开发流程。第三方合作伙伴也提供了更多IDE和工具选择,包括Huawei IoT Studio、SEGGER J-Link V10及Embedded Studio等均已支持。嵌入式操作系统包括μC/OS II、FreeRTOS、RT-Thread、Huawei LiteOS等也已全面适配并可以直接连接至云。这些都极大程度的简化了开发难度。   即刻开始RISC-V的开发体验 GD32V系列新品全部符合工业级高可靠性和温度标准,并提供至少十年的持续供货保证。芯片的静电防护(ESD)防护水平在人体放电(HBM)模式可达5KV,器件放电模式(CDM)可达2KV,远高于行业安全标准,从而适用于复杂环境并让终端产品更可靠耐用。 全新的GD32VF103系列RISC-V MCU现已即刻上市。样片和开发工具都可以通过GD32技术网站获取,各授权经销商渠道及GD32天猫旗舰店也已同步发售。

    时间:2019-08-23 关键词: 内核 MCU gd32v系列

  • 基于Arm Cortex-M3内核的微控制器

    基于Arm Cortex-M3内核的微控制器

    听说最近大家都迷上了看宫斗剧,还开始研究各种“宫心计”。其实呀,东芝也不例外,只不过东芝所研究的不是如何宫斗,而是如何攻克新产品。对东芝而言,只有掌握了“攻新计”,才能成就更好的自己,才能为市场带来更好的产品和服务。 废话不多说快来看看东芝又攻克了什么新产品! 近日,东芝宣布推出新品——“M3H族”微控制器,该产品基于全球标准的Arm Cortex-M3内核,专为电机控制而设计,可满足消费设备和工业设备的各种需求。新产品的出现扩大了 “TXZ?系列”微控制器的产品阵容。     哇,芝子对“攻新计”表示很期待!一起来看看新产品的亮点吧! 产品功能 根据产品功能,M3H族可以分为M3H族(1)和M3H族(2)两个产品组,前者提供标准功能,工作频率高达40MHz;后者提供更多封装和存储容量选项,并支持高速处理器(80MHz),还集成有“高级可编程电机驱动器(A-PMD)”。A-PMD实现低功耗并具备先进功能,同时集成有UART、I2C、TSPI和计时器等高度通用的外围电路,支持高端家用电器和大型系统。 主要特性 两个产品组共提供13种丰富的封装选项和32KB-512KB的闪存。 ◆ 高性能Arm?Cortex?-M3内核,工作频率高达80MHz ◆ 广泛的内存和封装选项,涵盖各种产品应用 ◆ 适用于广泛应用的通用型微控制器(消费和工业应用) 据说东芝将会继续扩大TXZ系列的产品阵容,以满足电机控制和全球传感器市场的需求。看来,不知道在未来东芝还会带来哪些令人惊喜的“攻新计”呢?让我们拭目以待吧!

    时间:2019-08-22 关键词: 内核 ARM 微控制器 cortex-m3 电源新品

  • 美国微芯科技公司推出的两大全新单片机系列

    美国微芯科技公司推出的两大全新单片机系列

    无论是用于入门级嵌入式开发,用作连接应用的主控制器,还是充当附加组件以减轻大型系统负荷,8位单片机(MCU)的作用都在不断扩大。 虽然从本质上讲,诸如独立于内核的外设(CIP)、智能模拟以及MPLAB?代码配置器等硬件和软件工具并不深奥也不难实现,但其作用不可忽视,它们可以提高处理能力,同时减少代码量、功耗以及快速上市所需的设计工作量。     有鉴于此,整合单片机、混合信号、模拟器件和闪存专利解决方案的供应商——MicrochipTechnologyInc.(美国微芯科技公司)推出了以客户创新为宗旨而设计的两大全新单片机系列:PIC16F18446系列和ATmega4809。 全新推出的PIC16F18446系列单片机是用于传感器节点的理想器件。考虑到灵活性,PIC16F18446及其集成的具有计算功能的模数转换器(ADC2)的工作电压范围为1.8~5V,并兼容大多数的模拟输出传感器和数字传感器。该12位ADC2可自动进行滤波处理,提供更精确的模拟传感器读数,并最终提供更高质量的终端用户数据。 由于这种ADC2能按需唤醒内核,而不是按照预定的时间表,这样就降低了系统的功耗,使得该MCU非常适用于电池供电型应用。此外,这一功能也使得传感器节点能够以小型电池为动力运行,从而减少了终端用户的维护成本和整体设计占用空间。 Microchip推出两大全新单片机系列 而此次发布的ATmega4809是新型megaAVR?单片机系列,可创建高响应命令与控制应用。其集成的高速模数转换器(ADC)拥有强大的处理能力,能加快模拟信号转换速度,从而产生确定性的系统响应。 作为首款包含独立于内核的外设(CIP)的megaAVR器件,ATmega4809可以通过硬件而不是软件来执行任务。这样就减少了代码量,并大幅降低了软件工作量,从而加快了产品上市时间。例如,可配置自定义逻辑(CCL)外设无需中断内核即可通过硬件将ADC连接至自定义的外部触发器组合,从而在缩短响应时间的同时降低了功耗。 此外,ATmega4809还可以被添加至系统中,从而实现更复杂的基于微处理器(MPU)的设计才能提供的各种功能。通过在MCU中使用CIP来执行命令与控制任务而不使用MPU,响应延迟的风险大大降低,从而带来更好的最终用户体验。 另外,ATmega4809被选为新一代Arduino开发板的板载单片机。在该开发板上添加ATmega4809器件,可以节省开发人员编写代码的时间从而让他们能有更多的时间专注于创新领域。而基于硬件的CIP使设计的创建更为高效,同时从项目开发到量产阶段的过渡也变得比以往更加容易。 Microchip8位MCU产品部副总裁SteveDrehobl表示:“新一代Arduino开发板选用ATmega4809器件,不仅加强了两家公司的合作关系,还为Arduino平台带来了CIP和智能模拟的优势”。 开发工具 新的PIC16F18446单片机系列可兼容MPLABPICkit?4(部件编号:PG164140),后者是Microchip针对低成本编程和调试推出的最新在线工具。 Curiosity开发板(部件编号:DM164137)是一款功能丰富的快速原型开发板,也可帮助设计人员基于这些MCU来进行开发。两种开发工具都获得了MPLABX集成开发环境(IDE)以及MPLABXpress云端IDE的支持。 此外,Microchip还提供了MPLAB代码配置器(MCC)。作为一款免费的软件插件,它提供了一个图形化界面来帮助设计人员为任意应用配置各种外设和功能。 借助ATmega4809XplainedPro(部件编号:ATmega4809-XPRO)评估工具包,设计人员可以基于ATmega4809快速完成原型开发工作。这一由USB供电的工具包拥有触摸按钮、LED、用于快速安装的扩展头、以及与AtmelStudio7集成开发环境(IDE)和AtmelSTART无缝集成的板载编程器/调试器。AtmelSTART这是一款免费的在线工具,可用于配置外设和软件以加快开发工作。 Curiosity开发板和ATmega4809XplainedPro评估工具包包含一个兼容mikroBUS?的插座,可轻松添加Mikroelektronikaclickboards?扩充板库中的传感器、执行器或通信接口。 供货 PIC16F18446及ATmega4809系列器件现已开始批量供货,提供多种存储容量、引脚数和封装选择。MPLABPICkit4编程器/调试器、Curiosity开发板以及ATmega4809XplainedPro评估工具包也已开始供货。

    时间:2019-08-13 关键词: 内核 芯片 单片机 电源新品

  • 加持鸿蒙的首发设备,荣耀智慧屏贵不贵?

    加持鸿蒙的首发设备,荣耀智慧屏贵不贵?

    在过去三天于东莞松山湖举办的华为开发者大会上,最受大家关注的新闻应该就是华为鸿蒙OS的发布。按照华为消费者业务CEO余承东先生的说法,这个基于微内核开发的分布式系统可以赋能几乎所有的设备,而荣耀智慧屏则成为华为旗下第一个搭载鸿蒙系统的设备。 虽然荣耀总裁赵明在发布会上一直强调,这不是一个电视,而是电视的未来,在家庭中会充当一个很重要的角色。其实从他们的硬件配置、制造工艺和升降式摄像头,乃至那个被赋予了更多意义的呼吸灯来看,荣耀智慧屏也的确在千篇一律的电视江湖里带来了一些不同的感觉。 尤其是那个无广告开关机的体验,更是在发布会现场引发了一场骚动。然而在赵明公布了这个产品的售价的时候,很多记者就在现场表示“打扰了”。在笔者的很多个群众也都有这种观点——荣耀的这个智慧屏卖得太贵了。但是这个智慧屏真的卖贵了吗? 我们首先从其设备配置开始,看看这个屏到底值不值这个价格。 据赵明介绍,这个智慧屏采用了华为海思自研的鸿鹄818芯片。其四核CPU+四核GPU的配置,能给电视提供强悍的性能保证。据业内人士告诉记者,华为海思的这颗芯片应该是他们在去年推出的811的升级版,后者是目前业内最高级的电视芯片,无论是在应用成熟度还是性能方面,在电视上面是绰绰有余。而这颗芯片说搭配的领先解码能力,也让荣耀智慧屏在显示上有了基本的保障。 赵明进一步指出,屏幕是决定画质的基线,算法是决定画质的上限。荣耀基于他们在技术方面的积累,借助动态画面补偿、高动态范围成像、超分算法、降噪算法、动态对比度增强、自动色彩管理和分区控光等七大魔法引擎,将荣耀智慧屏的显示质量提高了一个等级。 “举个例子,SR超分算法使得标清就有了接近高清的画质,而高清就拥有了接近超清的画质,这明显提升了我们整体体验”,赵明说。 另外,荣耀智慧屏的八种图像模式都经过了德国莱茵TUV低蓝光护眼认证,那就意味着整块屏的显示,都能给消费者的观看带来更好的体验。 “我们的智慧屏还加入了监测功能,能在小孩接近电视的时候,把电视的亮度和显示调整,避免对小孩的眼睛造成伤害”,赵明补充说。 在保证了显示质量之后,荣耀也为这个智慧屏的声音加入了更好的体验。从赵明的介绍我们得知,荣耀智慧屏使用的是6*10W的扬声器超级配置,这在其他同行的类似产品上是没有的。这个再搭配huaweiHisten专业音效,能够提供声场扩展、瞬态失真矫正、对白增强、智能低音和响度自适应等功能。 “那就意味着我们在获得更好的音频效果的同时,还避免了换台时声音忽大忽小的不好体验”,赵明强调。 在解决了作为一个电视的基本功能之外,荣耀还为智慧屏带来了更多的升级。例如配置了自研的WIFI芯片Hi1103,其160MHZ和2.4G/5G双频优选,让电视获得与旗舰手机同级别的WIFI体验。这就使得荣耀智慧屏即使在WIFI的连接环境下,也可以看4K大片毫不卡顿。 而蓝牙5.0的认证和全球首款通过无线HWA高清蓝牙协议的大屏设备的配置,也让这个智慧屏在使用的过程中,有了更多的选择。多种连接方式,能够为本人和周围环境带来更多的选择。 在这款智慧屏上,最引入瞩目的必然是属于那个可升降的的AI摄像头。赵明表示,这个摄像头支持1080P高清画面,随着网络的条件它还可以做自适应的调整。这颗摄像头拥有的五米拾音,10度俯仰调节等,能让你在任何位置都能获得不错的体验。同时这颗伸缩式的摄像头只有在你主动发起视频通话的时候它才能升起来,其他的时间都是落下来,这就给我们提供严格的隐私安全保护。在谈到应用上,视频是这颗摄像头的基本功能,而更多的应用,可以通过内置的NPU芯片赋能。 据介绍,华为自研NPU芯片的加持,让荣耀智慧屏摄像头能够做人脸识别、人像跟踪和姿势检测。上述谈到的小孩接近电视,电视自动调节屏幕亮度,就是通过这个NPU实现的。而这颗芯片的加入,也为荣耀智慧屏的未来应用提供了更高的灵活性。 以上说到的都是荣耀智慧屏的硬件性能。软件方面,则是智慧屏得到了电视同行认可的另一方面。 首先在内容源方面,华为与四大视频平台建立了深度合作,为消费者提供了丰富的的内容体验。在音频方面他们甚至还提供了无损的音频内容选择,这可以将他们扬声器方面的特性更好地展现出来。 当然,作为一个未来的电视,一个智能家居的控制中心,语音控制也是荣耀智慧屏必不可少的另一个特征。消费者可以通过“你好YOYO”来唤醒智慧屏,对智慧屏和连接到智能家居中的产品进行控制。 其他诸如通过触碰NFC贴纸将华为设备上(如手机)的内容推送到智慧屏,智慧屏充当家庭留言板和相册以及远程投屏播放,也让智慧屏有了前所未有的提升。 据电视行业资深从业人员告诉笔者,从硬件来看,荣耀智慧屏价格其实是相对合理的,光看他们的金属边框工艺,这在电视行业都是值得称赞的。如果说还有什么值得提升的空间的话,就是他们的2G DDR2着实不高,同时整个智慧屏相对较为传统。 他同时指出,对于荣耀智慧屏来说,如果只在国内卖问题不大,但一旦要出海,就需要考虑谷歌认证和neflix的认证问题,这对于华为鸿蒙来说,是一个重要考验。 而在笔者看来,华为在荣耀智慧屏上试水鸿蒙是一个不错的选择。毕竟电视上面用的APP就那么几个,华为需要去做的生态匹配也不多。就连三星也都是在其电视上实验Tizen系统。 换而言之,对荣耀智慧屏和鸿蒙来说,依然还有很长的路需要走。

    时间:2019-08-12 关键词: 华为 内核 鸿蒙 厂商动态

  • BLDC 电机兴起,MCU 芯片厂商如何抓住机遇?

    BLDC 电机兴起,MCU 芯片厂商如何抓住机遇?

    BLDC电机发展至今,已经超过50年。随着半导体技术的发展,MCU、驱动组件的普及,使得BLDC电机的成本降低了很多,电机的行业应用也正随着自动化水平的提高而不断提升,相信到2020年会迎来一大波新增长。 据统计和估算,2018年全球BLDC电机市场规模为153.6亿美元,这里的市场规模包括电机本体和电机驱动部分一起。预计未来4年将会以6.5%的年复合增长率增长,预计到2022年将会达到197.6亿美元。 BLDC市场增长的原因,与BLDC电机本身的特性密不可分,比如BLDC电机具有体积小、重量轻、噪声小、转矩特性好、启动转矩大、调速范围广、效率高,以及寿命长等特点。 当然,市场的增长离不开具体应用的推动,BLDC的应用场景有很多,目前增速比较快的应用主要有工业控制、家用电器、消费类电子、机器人、物流自动导航车、汽车、无人机等等。市场热点主要关注以下几个领域: Arm 32位MCU助力舵机行业产品升级 目前市面上可供选择的MCU有很多,但应用最多的是32位Arm内核MCU。从2018年中国市场Arm Cortex 内核MCU的累积出货排名可以看到市场份额的逐年变化趋势。 根据IHS Markit最新的调研统计数字,2018年中国Arm Cortex内核MCU市场大多数份额为国际MCU厂商所占据,但好消息是中国通用MCU厂商GigaDevice(兆易创新)成功保持在了市场前三的位置,市占率达到9.4%。相比2017年而言,排名第一的STMicroelectronics占比下降1%,第二名的NXP占比下降2.8%,TOP3的GigaDevice则上升了3.6%,也显示出中国半导体发展的强劲态势。 目前机器人市场主要分为工业机器人、商用服务机器人,以及消费级机器人。随着机器视觉、语音识别以及深度学习等人工智能技术的发展,消费级机器人已经快速进入市场并在智慧家庭、看护陪伴等领域迅速普及。机器人产品核心部件之一就是舵机,这也是机器人中使用最频繁的部分。舵机的大小和数量决定了机器人做动作的灵活性。据估计舵机部分将会占到机器人成本的40%以上。舵机通过电位器(或其它角度传感器)检测输出轴转动角度,MCU控制板根据电位器的信息能比较精确的控制和保持输出轴的角度。这样的电机控制方式叫闭环控制,所以舵机更准确的说是伺服马达。 机器人舵机主要由舵盘、减速齿轮组、位置反馈电位器、直流电机和控制电路板等部件组成。从图1的BLDC电机驱动框架图可以看出,每个舵机都需要一颗MCU来进行控制。 图1:BLDC电机驱动框架图 谈到兆易创新的32位Arm内核MCU产品,不得不提到量产多年,在电机控制产品圈热销的GD32F130系列产品。GD32F130 产品是兆易创新于2014年推出的基于Cortex-M3内核的MCU,为工程师提供了电机控制的主流解决方案。2017年,基于Cortex-M4内核的GD32F330产品则进一步以超值特性为成本敏感的电机控制应用开辟了道路。 2018年兆易创新又推出了基于最新Arm Cortex-M23内核的入门级产品,GD32E230和GD32E231系列MCU,片上集成了电机控制所需的多种模拟外设,包括能够输出三相互补PWM波形的高级定时器、高性能ADC、比较器和高速运放等,持续为轻量级的电机控制开发提供最优的解决方案。 家电变频化已成市场主流 随着中国家电行业执行5级能效标准,能效标准变得更加严格,变频电机市场开始转热。据市场机构预测,2010至2022年,家电变频电机中的半导体含量将实现8倍增长。 现在无论是我们日常生活常见的空调、洗衣机、冰箱、油烟机,还是当前普及率正在提升的洗碗机,智能吸尘器,破壁料理机等都开始实用变频电机。以正在兴起的变频冰箱为例,由于冰箱压缩机的转速决定了冰箱内部的温度,而变频冰箱压缩机的转速可根据温度来发生改变,就可以让冰箱根据当前的温度情况做出调整,让冰箱内的温度更好地保持恒定。这样,食物的保鲜效果将更好。由于变频冰箱压缩机大都选择BLDC电机,因此工作的时候效率更高,噪声更小,使用寿命也更高。 所谓的变频调速就是通过改变定子电源的频率来改变电机的转速,从而实现节能和提高效率的目的。要实现变频调速就需要变频器的帮助,现在的变频器一般由整流器、滤波器、驱动电路、保护电路,以及控制器(MCU/DSP)等部分组成。 笔者近期拿到了一块由深圳华芯控股科技设计的冰箱压缩机变频控制板,其中变频板的主控MCU正是采用了GD32F130产品,并已经被应用于国内外多家主流品牌冰箱产品中。GD32 MCU在家电变频行业的广泛应用也可见一斑。 手持云台大众化前景看好 近年来,随着视频直播越来越火,智能手机的拍照功能越来越强,Vlog制作、抖音神器等智能硬件已成为当下火爆的“刚需”。为了提升拍摄时的稳定性,手持云台正在走进日常生活并成为常见的摄影辅助工具。 目前从事手持云台产品开发的公司也有不少,一线品牌有大疆创新(DJI)的OSMO灵眸,以及线上热销的智云Smooth、飞宇G6等多家厂商。 云台产品至少要有三个BLDC电机,来控制XYZ三个方向上的运动,其次还有一个固定相机的装置,这个装置里面会有运动传感器来感测相机的位置变化。 云台产品的技术难点在于如何控制BLDC电机的运动。首先,电机需要用PMSM FOC算法来实现精准顺滑的伺服控制;其次,要保证相机的稳定性和各种跟随操作,必须将相机固定装置内传感器采集到的信号,实时地进行三维姿态解算和姿态控制。也就是说,做一个手持云台产品并不容易。 早期的国内云台产品基本都是采用俄罗斯人Aleksey Moskalenko的开源云台控制系统算法开发的。经过行业的不断发展和创新,现在中国也有很多厂商具备了研发能力,前面提到的三个主流品牌基本都是采用自有技术。目前IC国产化的趋势正日益明显,行业内的公司都开始着手准备国产化的替代方案。最常见的国产替代方案,就是采用GD32F4系列Cortex-M4 MCU产品去升级或重新开发早期的云台控制方案。 并且早期的云台控制原型是用一颗MCU控制一个BLDC电机的转动,通过中国工程师不断创新,已经实现了用一颗高性能的Cortex-M4F内核MCU同时控制两个轴向的BLDC电机。不仅可以简化云台产品的PCB设计,方便整机ID设计,也可以很好地控制产品的成本,从而更有利于大众化普及。 基于Arm MCU的BLDC电机控制算法研究热度不减 在逐渐兴起的BLDC电机控制应用中,磁场定向控制(FOC)技术正变得越来越流行,这样能够确保电机在任何时候都能以最佳的扭矩,高效地运行;其出色的动态相应可以实现精准的可变速度控制;再加上FOC的低扭矩波动可让电机在运行、启动和停止时,保持平稳运转。 FOC控制虽好,但要实现这些优势就需要MCU或者DSP有足够的计算能力来处理这些复杂的三角方程,以及电机转自状态观测。早年如果要实现高性能的电机控制算法,需要使用TI 的C2000系列DSP。近年来Arm公司推出Cortex-M4内核,集成了完整的DSP指令集、并行计算能力和专用单精度浮点运算单元(FPU),就可以有效满足FOC算法和电机转子位置的观测算法对MCU计算能力的需求,拉近了MCU和DSP在运算能力上的差异。包括STMicroelectronics、NXP、GigaDevice、ATMEL等MCU厂商也纷纷推出相应的MCU产品。 为了帮助客户使用MCU芯片评估FOC算法和电机状态观测器算法,GigaDevice(兆易创新)推出了GD32F450Z-BLDC评估板并提供了相应的开发工具包(SDK),GD32F450系列MCU具备了200MHz的Cortex-M4F内核和2.6M采样率高性能ADC等丰富的模拟外设,电机开发工程师就可以使用开发套件进行各类电机控制算法的研究。 结语 随着半导体技术的进步,BLDC电机开始兴起,并逐渐替代传统电机,特别是在工业控制、家用电器、消费类电子、机器人、物流自动导航车、汽车、无人机等应用领域。BLDC电机的兴起,对MCU的需求量将会持续增长,2018年全球MCU的出货量306亿颗,未来几年还将会保持高速增长。 从城市电摩骑行和即时配送的产业化升级、到消费家电产品的不断智能化创新,再到BLDC控制的变频化发展,一方面需要中国嵌入式工程师勇于创新,敢于突破现有的行业成规;另一方面芯片供应厂商也要持续不断提供性能和价格更好,同时方便开发者使用的新产品。这也给中国的MCU芯片厂家提供了广泛的市场机会和更大的发展动能。

    时间:2019-07-29 关键词: 半导体 内核 MCU 行业资讯

  • 嵌入式处理器的体系架构与内核,好比盖房子

    嵌入式处理器的体系架构与内核,好比盖房子

    随着半导体行业的发展。嵌入式系统已经广泛地应用到当今各个领域,与我们的生活息息相关,小到掌上的数字产品,大到汽车、航天飞机。 提到嵌入式系统我们很快会联想到单片机,不错,MCU是最基础和常用的嵌入式系统,但是目前像FPGA、ARM、DSP、MIPS等其他嵌入式系统应用越来越广泛。嵌入式系统与模拟电路或其他功能电路组成的SoC(System on Chip,片上系统)或SiP(System in Package,系统级封装)在手机、机顶盒等功能复杂的产品上的应用也越来越多。 总的来说,嵌入式系统发展呈现如下特点:由8位处理向32位过渡,由单核向多核过渡,向网络化功能发展,MCU、FPGA、ARM、DSP等齐头并进,嵌入式操作系统呈多元化趋势,所有的嵌入式处理器都是基于一定的架构的,即IP核(Intellectual Property,知识产权),生产处理器的厂家很多,但拥有IP核的屈指可数。有自己的IP核,光靠卖IP核即可坐拥城池。 嵌入式系统的架构有专有架构和标准架构之分,在MCU(微控制器)产品上,像瑞萨(Renesas)、飞思卡尔(Freescale)、NEC都拥有自己得专有IP核,而其他嵌入式处理器都是基于标准架构。 嵌入式处理器的体系架构与内核详解 当我们谈及嵌入式处理器的体系架构时,一般都是想到Intel的X86架构和ARM公司的ARM架构。X86架构和ARM架构最大的不同点就是使用的指令集不同,前者使用的CISC指令集,后者使用的是RISC指令集,还有一点就是X86架构使用的是冯诺依曼结构,ARM架构既使用冯诺依曼结构,也使用哈佛结构(已经成了一种趋势)。何为架构?这两个字看起来很简单,不就是“骨架+结构”,但是个人觉得并不好理解。百度上把架构分为逻辑架构,物理架构和系统架构。对于处理器,当起发展到一定程度时,这种物理架构不会有很大的变化。以CPU为例,它主要由逻辑部件+寄存器部件+控制部件组成,这种结构在发展稳定之后,就不会有大多的改变。那么人们常说的架构版本更新主要体现在哪里呢?指令集。当然还有如存储器管理特性改进等因素,以ARM处理器架构为例,如图1为其进化史。     可以看到其架构一路从V4→V7版本,其中V7版本分三路发展,A系列,R系列,M系列。再看图2看其处理器指令的演进史:     可以发现,在V4T版本时增加了Thumb指令集,刚好可以对应处理器的Thumb状态,到了V5E版本,加入了增强型的DSP指令,V6版本可谓是ARM指令演进史上的一件大事,有了Thumb-2的问世。其实之前的16位Thumb指令集就32位ARM指令集的一个功能子集,到了Thumb-2,以其强大,轻佻和高效,16位指令首次与32位指令并存,再也不需要因为处理器模式的变换而造成指令集变换。从ARM-Cortex-M3将全身的处理能力托付给Thumb-2指令集就可以看出,Thunmb-2指令集的强大。 处理器架构与处理器内核有什么区别?“处理器内核”中的处理器应该是MCU。“处理器架构”中的处理器可以是更广义的 Processing Unit。而当我们谈及内核时首先应该是针对某一系列的MCU。拿STM32系列MCU来说,它们是基于ARM-Cortex-M3为内核的。而内核主要谈其流水线和结构。如ARM7内核是0.9MIPS/MHZ的三级流水线和冯诺依曼结构,ARM9是1.1MIPS/MHZ的5级流水线哈佛结构。通过图3可以更直观的发现内核与MCU的关系。     ARM内核和架构都是什么意思,它们到底是什么关系? ARM产品越来越丰富,命名也越来越多。很多朋友提问: ARM内核和架构都是什么意思?内核和架构的关系是什么?比如ARMv7架构,这个架构指的是什么?小编选出了几个精彩回答!希望对嵌友们在选择设计电路时有所帮助。 1.ARM内核:从ARM7、ARM9到Cortex-A7、A8、A9、A12、A15再到Cortex-A53、A57等,总之不同版本 ARM 有不同的想法。比如为高速度设计的Cortex A8、A9都是ARMv7a 架构;Cortex M3、M4是ARMv7m架构;前者是内核,后者是指令集的架构。 2.ARM的架构都是基于RISC指令集而架构的,而其内核只是实现这一指令集的硬件架构的基础,Thumb-2指令集架构(ISA)的子集,包含所有基本的16位 和32位Thumb-2指令、 、哈佛处理器架构,在加载/存储数据的同时能够执行指令取指,带分支预测的三级流水线等。 3.好比你盖房子,刚开始因为水平低流行盖平房,这就是一种架构(V5T),然后这种平房架构你可以设计出一款独立卫生间的款式, 这叫ARM7内核。 然后其他人(芯片设计公司)想盖房子的就买你这个图纸去盖,接着过一段时间,有人觉得光独立卫生间还不够啊, 我还想有个小院子! 好吧,那ARM就满足你们的要求,出个带小院子的款式(ARM9)。 又过了很久, 这种平房的架构就随着大伙的需求一直改啊改啊,后来经过ARM研究发现: 现在大伙盖房子的能力duang duang直升啊(包括工艺、设计能力、时钟主频),只盖这种平房施展不开啊! 好吧,ARM为了不让这帮设计的人闲着,就推出一种二三层楼房的样式, 这因为跟平房设计结构完全不一样嘛, 那就叫一种新的架构(ARMv6),同样这种楼房样式ARM也为大家准备了带游泳池的和带车库的款式(ARM11),好吧继续改啊改啊, 改到后来大家已经开始有能力盖十层以上的大楼了。 ARM一如既往地出了新的款式(ARMv7架构), 这时ARM觉得以前名字都太土鳖了, 什么ARM5、 ARM6、 ARM7—又难听又难记, 我要取个看起来牛逼的名字, 咱至少也算个能设计摩天大楼的主儿了, 于是后面的内核都叫Cortex。 改名只是一部分, 随着这个架构出来后, ARM发现以前用咱们图纸盖出的楼也就做个民宅, 民宅图个啥? 实惠嘛(功耗低)。 现在不一样了,现在咱的图纸盖得楼不仅可以做民宅,还可以做军事基地、 还可以做高档写字楼, 以前这些高级功能的楼房可是只有小英(英特尔)才能设计出来的啊!为了满足这些不同的需求, ARM把这个架构设计出来的款式分成3个系列(M系列、R系列、A系列)。M系列是为民宅设计的, 因为老百姓图实惠嘛, 这种设计就设计个十层左右(功耗低); R系列是为军事基地设计的, 这种楼设计的也不高— 十层左右吧, 但是关键是要对特殊情况要有快速反应的能力(中断快); 最后A系列是给商业大佬用的, 那当然是要高端大气上档次, 就是要性能高,各种LED灯灯光秀啊都给我上。

    时间:2019-07-27 关键词: 内核 架构 处理器 嵌入式 嵌入式处理器

  • Linux Kernel 5.2正式发布

    Linux Kernel 5.2正式发布

    经历了7个RC候选版本之后,Linus Torvalds于今天正式宣布了Linux Kernel 5.2正式版。本次主要版本更新在改进驱动程序和核心组件之外,还引入了一些有趣的特性和增强功能。不过需要注意的是5.2并非长期支持(LTS)分支,因此小编推荐注重稳定的用户还是使用当前的LTS内核。     Linus Torvalds在一份邮件列表公告中写道:“我原本有计划推出第八个候选版本,毕竟在上周我花了几天出去旅游了。虽然核心编译时间毕竟晚了,但对代码进行审查之后发现没有再推出一个候选版本的必要,于是我按照常规路线图发布了5.2正式版。” Linux Kernel 5.2的亮点包括Sound Open Firmware,这是一个支持DSP音频设备的开源固件。此外还新增了用于挂载文件系统的新挂载API,面向ARM Mail设备的全新开源GPU驱动,在EXT4文件系统中支持不区分大小写,以及对BFQ I / O调度程序的性能改进。     Linux Kernel 5.2还为cgroups v2添加了一个冷冻控制器以释放资源,部署了新设备隐射“dust”目标来模拟读取失败或者扇区失败的设备,为 clone(2)添加了CLONE_PIDFD这个Flag从而在创建进程时候获取PID,能够通过pidfd_send_signal(2)使用,并在Android系统中提供了更高的资源监控。 在安全方面,Linux Kernel 5.2附带了一个全新的CPU BUG框架,用于保护设备免受英特尔MDS(微架构数据采样)硬件漏洞的影响,同时还有一个名为“mitigations=”独立于架构的启动选项,以便更容易启用或禁用缓解CPU缺陷。 Linux内核5.2还包括许多更新的和新的驱动程序,以获得更好的硬件支持,以及无数的bug和安全修复程序。目前用户可以前往Kernel.org网站进行下载。

    时间:2019-07-10 关键词: 操作系统 内核 Linux 行业资讯

  • 内核对象

    无论怎样创建内核对象,都要向系统指明将通过调用C l o s e H a n d l e 来结束对该对象的操作:BOOL CloseHandle(HANDLE hobj);该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可 以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0 ,该内核便从内存中撤消该内核对象。如果将一个无效句柄传递给C l o s e H a n d l e ,将会出现两种情况之一。如果进程运行正常,C l o s e H a n d l e 返回FA L S E ,而G e t L a s t E r r o r 则返回E R R O R _ I N VA L I D _ H A N D L E 。如果进程正在排除错误,系统将通知调试程序,以便能排除它的错误。在C l o s e H a n d l e 返回之前,它会清除进程的句柄表中的项目,该句柄现在对你的进程已经无效,不应该试图使用它。无论内核对象是否已 经撤消,都会发生清除操作。当调用C l o s e H a n d l e 函数之后,将不再拥有对内核对象的访问权,不过,如果该对象的使用计数没有递减为 0 ,那么该对象尚未被撤消。这没有问题,它只是意味着一个或多个其他进程正在使用该对象。当其他进程停止使用该对象时(通过调用C l o s e H a n d l e ),该对象将被撤消。假如忘记调用C l o s e H a n d l e 函数,那么会不会出现内存泄漏呢?答案是可能的,但是也不一定。在进程运行时,进程有可能泄漏资源(如 内核对象)。但是,当进程终止运行时,操作系统能够确保该进程使用的任何资源或全部资源均被释放,这是有保证的。对于内核对象来说,系统将 执行下列操作:当进程终止运行时,系统会自动扫描进程的句柄表。如果该表拥有任何无效项目(即在终止进程运行前没有关闭的对象),系统将关 闭这些对象句柄。如果这些对象中的任何对象的使用计数降为0 ,那么内核便撤消该对象。因此,应用程序在运行时有可能泄漏内核对象,但是当进程终止运行时,系统将能确保所有内容均被正确地清除。另外,这个情况适用于所有对象、 资源和内存块,也就是说,当进程终止运行时,系统将保证进程不会留下任何对象。   共享跨越进程边界的内核对象的第二种方法是给对象命名。许多(虽然不是全部)内核对象都是可以命名的。例如,下面的所有函数都可以创建命名的内核对象:HANDLE CreateMutex(   PSLCURITY_ATTRIBUTES psa,   BOOL bInitialOwner,   PCTSTR pszName); HANDLE CreateEvent(   PSECURITY_ATTRIBUTES psa,   BOOL bManualReset,   BOOL bInitialState,   PCTSTR pszName); HANDLE CreateSemaphore(   PSECURITY_ATTRIBUTES psa,   LONG lInitialCount,   LONG lMaximumCount,   PCTSTR pszNarne); HANDLE CreateWaitableTimer(   PSLCURITY_ATTRIBUTES psa,   BOOL bManualReset,   PCTSTR pszName); HANDLE CreateFileMapping(   HANDLE hFile,   PSECURITY_ATTRIBUTES psa,   DWORD flProtect,   DWORD dwMaximumSizeHigh,   DWORD dwMaximumSizeLow,   PCTSTR pszName); HANDLE CreateJobObject(   PSECURITY_ATTRIBUTES psa,   PCTSTR pszName);所有这些函数都有一个共同的最后参数p s z N a m e 。当为该参数传递N U L L 时,就向系统指明了想创建一个未命名的(匿名)内核对象。当创建一个未命名的对象时,可以通过使用继承性(如上一节介绍的那样)或D u p l i c a t e H a n d l e (下一节将要介绍)共享跨越进程的对象。若要按名字共享对象,必须为对象赋予一个名字。如果没有为p s z N a m e 参数传递M U L L ,应该传递一个以0 结尾的字符串名字的地址。该名字的长度最多可以达到M A X _ PAT H (定义为2 6 0 )个字符。但是,M i c r o s o f t 没有提供为内核对象赋予名字的指导原则。例如,如果试图创建一个称为“J e ff O b j ”的对象,那么不能保证系统中不存在一个名字为“J e ff O b j ”的对象。更为糟糕的是,所有这些对象都共享单个名空间。由于这个原因,对下面这个C r e a t e S e m a p h o r e 函数的调用将总是返回N U L L : 

    时间:2019-06-14 关键词: 内核

  • INF文件

    INF文件的节       INF文件是一个文本文件,由许多按层次结构排列的节组成,他们以方括号中的节名称开始,如[Version]、[Manufacturer]等,后面是改接所含有的各个项,如Signature、DriverVer等。节中各项的基本定义格式为entry=value[,value…]       其中,“entry”标示项名称,“value”标示该想的取值。节名和项名称都不区分大小写,且对于Windows 98,其必须小于28个字符;对于Windows 2000,其长度的最大值为255个字节。下表是INF文件中常用的节。INF文件中常用的节节说明VersionINF文件的开始SourceDisksNames指明去的那个程序所在的磁盘或CD-ROMSourceDisksFiles指明驱动程序的文件名Destination指明INF文件和驱动程序的目标文件Manufacturer指明供应商及其对应Models节的名称Models指明Install/DDInstall节的名称、设备的硬件ID等信息Install(Windows 98)Install(Windows 2000)指明需复制的文件、想注册表中添加的内容等信息DDInstall.Services仅适用于Windows 2000指明驱动程序安装的详细信息String指明一系列字符串 1Version节       Version节通常作为INF文件的开始,下表列出了其包含的各项。所有INF文件都必须含有Version节及其Signature项Version节中的各项项说明Signature指明驱动程序的签名,其取值为:$Windows NT$、$Windows 95$、$Chicago$、Class指明驱动程序所属的类别ClassGuid指明设备类的GUID,其格式为:{nnnnnnnn-nnnn-nnnn-nnnnnnnnnnnn}①Provider指明该INF文件的供应商LayoutFile仅由操作系统内部提供的INF文件使用CatalogFile指明数字签名文件的文件名,其扩展名为.catDriverVer指明驱动程序的版本信息,其格式为:mm/dd/yyyy[,x.y.v.z]注:①n代表一个16进制数       下面对主要的各项进行详细介绍。①.Signature项:指明驱动程序的签名。对于Windows 98和Windows 2000下的USB设备驱动程序,该项的取值通常为“$Chicago$”。②.Class和ClassGuid项:指明驱动程序所属的设备类别及其GUID,其取值情况见下表。常用设备类别及其GUIDClassClassGuid说明13946BDD1FC1-810F-11D0-BEC7-08002BE2092F1394主控制器CDROM4D36E965-E325-11CE-BFC1-08002BE10318CD-ROM驱动器DiskDrive4D36E967-E325-11CE-BFC1-08002BE10318磁盘驱动器Display4D36E968-E325-11CE-BFC1-08002BE10318显示适配器FDC4D36E969-E325-11CE-BFC1-08002BE10318软盘驱动器HDC4D36E96A-E325-11CE-BFC1-08002BE10318硬盘控制器HIDClass745a17a0-74d3-11d0-b6fe-00a0c90f57da人机接口设备Keyboard4D36E96B-E325-11CE-BFC1-08002BE10318键盘Modem4d36e96c-e325-11ce-bfc1-08002be10318调制解调器Monitor4d36e96e-e325-11ce-bfc1-08002be10318监视器Mouse4d36e96f-e325-11ce-bfc1-08002be10318鼠标Net4d36e972-e325-11ce-bfc1-08002be10318网络适配器Ports4d36e978-e325-11ce-bfc1-08002be10318端口(COM&LPT)Printer4d36e979-e325-11ce-bfc1-08002be10318打印机System4d36e97d-e325-11ce-bfc1-08002be10318系统设备TapeDrive6D807884-7D21-11CF-801C-08002BE10318磁带驱动器USB36FC9E60-C465-11CF-8056-444553540000USB③.DriverVer项:指明驱动程序的版本信息。其“mm/dd/yyyy”代表月/日/年;“x.y.v.z”代表驱动程序的版本号,它是可选的。2.SourceDisksNames节       SourceDisksNames节指明设备驱动程序所在的磁盘或CD-ROM,其各项的格式如下:diskid=disk-description,disk-label,disk-serial-number   (Windows 98)diskid=disk-description[,[tagfile][,unused,path]]  (Windows 2000)       其中,“disked”指出磁盘驱动器的编号,它是正整型数,通常从1开始,且不能重复;“disk-description”表示磁盘的描述信息,他通常为一个字符串。对于Windows 98,“disk-label”指出磁盘的卷标;“disk-serial-numbe”指出磁盘的序列号。对于Windows 2000,“tagfile”指出磁盘标签文件的文件名;“unused”保留未用;“path”指出驱动程序所在的路径;“[ ]”表示可选参数。3.SourceDisksFiles节       SourceDisksFiles节指明设备驱动程序的文件全路径,其各项格式如下:filename=diskid[,subdir] (Windows 98)filename=diskid[,[,subdir][,size]]   (Windows 2000)       其中,“filename”指出驱动程序的文件名;“diskid”指出磁盘驱动器的编号;“subdir”指出该文件在磁盘上的路径;“size”指出该文件未经压缩时的大小,以字节为单位。4.DestinationDirs节       DestinationDirs节指明INF文件和设备驱动程序的目标目录。当INF文件使用CopyFiles、DelFiles、RenFiles指令时,则必须包含该节。其各项的格式如下:DefaultDestDir=dirid[,subdir]File-list-section=dirid[,subdir]       其中,“DefaultDestDir”是一个项名称,代表文件复制、删除、重命名操作的缺色很难干目标目录;“file-list-section”指出CopyFiles、DelFiles、RenFiles指令所引用的节;“dirid”指出目标目录值,见下表;“subdir”指出dirid目录下的子目录。DestinationDirs节中的常用diriddirid目标目录dirid目标目录10Windows目录:%windir%17INF目录11系统目录Windows 98:%windir%/systemWindows 2000:%windir%/system3218帮助目录20字体目录12驱动程序目录Windows 98:%windir%/system/IoSubsysWindows 2000:%windir%/system32/drivers23颜色目录24应用程序目录(Windows2000)13命令目录(Windows98)25共享目录14控制面板目录(Windows98)50%windir%/system(Windows2000)15打印机目录(Windows98)54Ntldr.exe和osloader.exe所在的目录(Windows2000)16工作组目录(Windows98)55打印处理器目录(Windows2000)5.Manufacturer节       Manufacturer节指明供应商及其对应Models接的名称,其各项的格式如下:%strkey%=models-section-name       其中,“strkey”代表设备制造的名字,其字符串值在String节中定义;“models-section-name”指出Models节的名称。6.Models节       Models节指明Install/DDInstall节的名称、设备的硬件ID和兼容ID等信息,其节名称由Manufacturer节指定。其各项的格式如下:device-description=install-section-name,hw-id[,compatiable-id…]       其中,“device-description”指出设备的表述信息,他可以是一个字符串,也可以使一个%strkey%;“install-section-name”指出Install/DDInstall节的名称;“hw-id”指出设备的硬件ID;“compatiable-id”指出设备的兼容ID。7. Install/DDInstall节       Install/DDInstall节指明需复制的文件、想注册表中添加的内容等信息,其节名称由Models节指定。下表列出了其包含的常用项。Install/DDInstall节中的常用项项说明DriverVer指明驱动程序的版本信息,格式为:mm/dd/yyyy[,x.y.z]CopyFiles指明需复制的文件,格式为:CopyFiles=@filename|file-list-section[,file-list-section]...AddReg指明向注册表中添加的其他INF文件按,格式为:AddReg=add-registry-section[,add-registry-section]…Include指明安装时需要的其他INF文件,格式为:Include=filename.inf[,filename2.inf]…Needs指明安装时所需的特定INF文件,格式为:Needs=inf-section-name[,inf-section-name]…Delfiles指明需删除的文件,格式为:Delfiles=inf-section-name[,inf-section-name]…RenFiles指明需重命名的文件,格式为:Renfiles=inf-section-name[,inf-section-name]…DelReg指明需删除的注册表内容,格式为:DelReg=del-registry-section[,del-registry-section]…       Install/DDInstall节中的常用项的详细介绍。①     CopyFiles项:它是Install/DDInstall节中的一个基本指令,用于指明需要复制的文件。其“filename”指出目标文件名;“file-list-section”是其创建的文件列表节,该节格式为[file-list-section]destination-file-name[,source-file-name][,temporary-file-name][,flag]       其中,“destination-file-name”指出目标文件名;“source-file-name”指出源文件名,如果其和目标文件名相同,则可忽略该参数;“temporary-file-name”指出复制操作过程中所需要的临时文件名,他只适用于Windows 98;“flag”指出该文件的一些处理方法,其取之情况如下表。CopyFiles项中的flag参数值符号说明0x00000400COPYFLG_REPLACEONLY仅替换目标目录中的文件0x00000800COPYFLG_NODECOMP复制并不解压缩0x00000008COPYFLG_FORCE_FILE_IN_USE将源文件赋值为临时文件名,以重新命名0x00000010COPYFLG_NO_OVERWRITE不替换原有文件0x00001000COPYFLG_REPLACE_BOOT_FILE强制用户重新引导系统0x00002000COPYFLG_NOPRUNE强制复制文件0x00000020COPYFLG_NO_VERSION_DIALOG不覆盖新版本的文件0x00000004COPYFLG_NOVERSIONCHECK可覆盖任何版本的文件0x00000040COPYFLG_OVERWRITE_ORDER_ONLY仅覆盖旧版本的文件0x00000001COPYFLG_WARN_IF_SKIP当用户跳过文件时发出警告0x00000002COPYFLG_NOSKIP不允许用户跳过文件②     AddReg项:它是Install/DDInstall节所必须包含的指令,用于指明需项注册表中添加的内容。其“add-registry-section”是它创建的添加注册表节,该节的格式为[add-registry-section]reg-root, [subkey], [value-entry-name], [flags], [value]其中,“reg-root”指出注册表树的根目录,它的取值在下表中;“subkey”指出reg-root下的子目录(或称子键);“value-entry-name”指出要增加的注册表值;“flags”指出其对注册表的一些处理方法,取值在下面的flags表中;“value”指出新增加注册表值的数据。AddReg项中的reg-root参数值说明HKCRHKEY_CLASSES_ROOTHKCUHKEY_CURRENT_USERHKLMHKEY_LOCAL_MACHINEHKUHKEY_USERHKR被安装设备的注册表键AddReg项中的flags参数值符号说明0x00000000FLG_ADDREG_TYPE_SZvalue为REG_SZ类型0x00000001FLG_ADDREG_BINVALUETYPEvalue为2进制数据0x00000002FLG_ADDREG_NOCLOBBER不替换已有注册表中的value0x00000004FLG_ADDREG_DELVAL从注册表中删除subkey或value-entry-name0x00000010FLG_ADDREG_KEYONLY仅建立subkey,忽略value-entry-name和value0x00000020FLG_ADDREG_OVERWRITEONLY仅当value-entry-name时替换value0x00010000FLG_ADDREG_TYPE_MULTI_SZvalue为REG_MULTI_SZ类型0x00000008FLG_ADDREG_APPEND添加value至已有注册表值0x00020000FLG_ADDREG_TYPE_EXPEND_SZvalue为REG_EXPAND_SZ类型0x00010001FLG_ADDREG_TYPE_DWORDvalue为REG_DWORD类型0x00020001FLG_ADDREG_TYPE_NONEvalue为REG_NONE类型(Windows2000)③     DelFiles项:它是Install/DDInstall节中的可选指令,用于指明需删除的文件。其“file-list-section”是它创建的文件列表节,该节的格式为[file-list-section]destination-file-name[,,,flag]       其中,“destination-file-name”指出目标文件名;“flag”指出该文件的一些处理方法,其取值如下表。DelFiles项中的flag参数值符号说明0x0000001DELFLG_IN_USE在其安装处理操作完成后再删除该文件0x0001000DELFLG_IN_USE1功能与DELFLG_IN_USE一样,仅适用Windows2000④RenFiles项:它是Install/DDInstall节中的可选指令,用于指明需重命名的文件。其“file-list-section”是它创建的文件列表节,该节的格式为[file-list-section]new-dest-file-name, old-source-file-name       其中,“new-dest-file-name”指出该文件的新文件名;“old-source-file-name”指出其原有文件名。⑤DelReg项:它是Install/DDInstall节中的可选指令,用于指明需从注册表中删除的内容。其“del-registry-section”是它创建的删除注册表节,该节的格式为[del-registry-section]reg-root, subkey [,value-entry-name]       其中,“reg-root”指出该注册表树中的根目录,取值与AddReg的ret-root相同; “subkey”指出reg-root下的子目录(或称子键);“value-entry-name”指出要删除的注册表值。8.DDInstall.Services节       DDInstall.Services节指明驱动程序安装的详细信息,他只是用于Windows 2000.他的节名称为[install-section-name.Services],其“install-section-name”由Models节指定。下表列出了其包含的常用项。DDInstall.Services节中的常用项项说明AddService控制驱动程序的安装过程,格式为AddService=ServiceName,[flags],service-install-section[,event-log-install-section[,[EventLogType][,EventName]]]…DelService删除一个或多个已有的驱动程序,格式为DelService=ServiceName [,[flags][,[EventLogType][,EventName]]]…Include指明安装时所需的其他INF文件,格式为Include =filename.inf [,filename2.inf]…Needs之敏感转世所需要的特定INF文件,格式为Needs = inf-section-name[,inf-section-name]…主要项进行详细介绍。①     AddService项:它是DDInstall.Services节所必须包含的指令,用于控制设备驱动程序的安装过程。其“ServiceName”指出驱动程序的名字;“flags”指出一个或多个系统定义的标识,其取值情况如下表;“event-log-install-section”是其创建的事件日志安装,下表中列出了该节所包含的常用项;“service-install-section”是其创建的服务安装节,下表中列出了该节所包含的常用项;“EventLogType”指出事件日志的类型,其取值为System、Security和Application;“EventName”指出事件日志的名字。AddService项的常用flags参数值符号说明0x00000002SPSVCINST_ASSOCSERVICE指明其为PnP功能驱动程序0x00000008SPSVCINST_NOCLOBBER_DISPLAYNAME不覆盖指定服务的名字0x00000100SPSVCINST_NOCLOBBER_DESCRIPTION不覆盖指定服务的描述0x00000010SPSVCINST_NOCLOBBER_STARTTYPE不覆盖指定服务的启动类型0x00000020SPSVCINST_NOCLOBBER_ERRORCONTROL不覆盖指定服务的差错控制值event-log-install-section节中的常用项项说明AddReg指明向注册表中添加的内容,格式如下AddReg=add-registry-section[,add-registry-section]…DelReg指明需删除的注册表内容,格式如下DelReg=del-registry-section[,del-registry-section]…BitReg有效但几乎无用,格式如下BitReg=bit-registry-section[,bit-registry-section]…DisplayName驱动程序的名字,为字符串值Description驱动程序的描述,为字符串值ServiceType驱动程序的类型,为特定16进制数StartType驱动程序的启动类型,为特定16进制数ErrorControl驱动程序的差错控制级别,为特定16进制数ServiceBinary驱动程序的完整路径名:%dirid% /filename一下是对event-log-install-section节中的常用项的主要项进行详细介绍。ServiceType项的取值值符号说明0x00000001SERVICE_BOOT_START内核模式驱动程序0x00000002SERVICE_KERNEL_FILE_SYSTEM_DRIVER上层网络驱动程序或文件系统0x00000010SERVICE_WIN32_OWN_PROCESSWin32应用程序0x00000020SERVICE_WIN32_SHARE_PROCESSWin32应用程序StartType项的取值值符号说明0x0SERVICE_BOOT_START由操作系统加载程序启动0x1SERVICE_SYSTEM_START由操作系统初始化过程中启动0x2SERVICE_AUTO_START由服务控制管理器启动0x3SERVICE_DEMAND_START由PnP管理器或服务控制管理器按需启动0x4SERVICE_DISABLE禁止启动ErrorControl项的取值值符号说明0x0SERVICE_ERROR_IGNORE不显示警告信息0x1SERVICE_ERROR_NORMAL显示警告信息0x2SERVICE_ERROR_SERVICE当驱动程序加载失败时,系统将使用其注册的LastKnownGood重试,并忽略其后的错误0x3SERVICE_ERROR_CRITICAL当驱动程序加载失败时,系统将使用其注册的LastKnownGood重试,并对其后的错误进行检验②     DelService项:它是DDInstall.Service节中的可选指令,用于删除一个或多个已有的设备驱动程序。其“ServiceName”指出驱动程序的名字;“flags”指出一个或多个系统定义的标识,取值见下表;“EventLogType”指出事件日志的类型,其取值为System、Security或Application;“EventName”指出事件日志的名字。DelService项的flags参数符号说明SPSVCINST_DELETEEVENTLOGENTRY删除与ServiceName有关的事件日志SPSVCINST_STOPSERVICE在删除之前首先停止服务9String节       String节指明一些列字符串,其所含各项的格式为strkey=[“]some string[“]其中,“strkey”指出字符串的名字,它只能包含字母和数字;“some string”为字符串的内容。附上《windows驱动开发技术详解》的一个例子[cpp] view plain copy ;--------- Version Section ---------------------------------------------------      [Version]   Signature="$CHICAGO$";   Provider=Zhangfan_Device   DriverVer=11/1/2007,3.0.0.3      ; If device fits one of the standard classes, use the name and GUID here,   ; otherwise create your own device class and GUID as this example shows.      Class=ZhangfanDevice   ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B0}         ;--------- SourceDiskNames and SourceDiskFiles Section -----------------------      ; These sections identify source disks and files for installation. They are   ; shown here as an example, but commented out.      [SourceDisksNames]   1 = "HelloWDM",Disk1,,      [SourceDisksFiles]   HelloWDM.sys = 1,MyDriver_Check,      ;--------- ClassInstall/ClassInstall32 Section -------------------------------      ; Not necessary if using a standard class      ; 9X Style   [ClassInstall]   Addreg=Class_AddReg      ; NT Style   [ClassInstall32]   Addreg=Class_AddReg      [Class_AddReg]   HKR,,,,%DeviceClassName%   HKR,,Icon,,"-5"      ;--------- DestinationDirs Section -------------------------------------------      [DestinationDirs]   YouMark_Files_Driver = 10,System32Drivers      ;--------- Manufacturer and Models Sections ----------------------------------      [Manufacturer]   %MfgName%=Mfg0      [Mfg0]      ; PCI hardware Ids use the form   ; PCIVEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd   ;改成你自己的ID   %DeviceDesc%=YouMark_DDI, PCIVEN_9999&DEV_9999      ;---------- DDInstall Sections -----------------------------------------------   ; --------- Windows 9X -----------------      ; Experimentation has shown that DDInstall root names greater than 19 characters   ; cause problems in Windows 98      [YouMark_DDI]   CopyFiles=YouMark_Files_Driver   AddReg=YouMark_9X_AddReg      [YouMark_9X_AddReg]   HKR,,DevLoader,,*ntkern   HKR,,NTMPDriver,,HelloWDM.sys   HKR, "Parameters", "BreakOnEntry", 0x00010001, 0      ; --------- Windows NT -----------------      [YouMark_DDI.NT]   CopyFiles=YouMark_Files_Driver   AddReg=YouMark_NT_AddReg      [YouMark_DDI.NT.Services]   Addservice = HelloWDM, 0x00000002, YouMark_AddService      [YouMark_AddService]   DisplayName = %SvcDesc%   ServiceType = 1 ; SERVICE_KERNEL_DRIVER   StartType = 3 ; SERVICE_DEMAND_START   ErrorControl = 1 ; SERVICE_ERROR_NORMAL   ServiceBinary = %10%System32DriversHelloWDM.sys      [YouMark_NT_AddReg]   HKLM, "SystemCurrentControlSetServicesHelloWDMParameters",   "BreakOnEntry", 0x00010001, 0         ; --------- Files (common) -------------      [YouMark_Files_Driver]   HelloWDM.sys      ;--------- Strings Section ---------------------------------------------------      [Strings]   ProviderName="Zhangfan."   MfgName="Zhangfan Soft"   DeviceDesc="Hello World WDM!"   DeviceClassName="Zhangfan_Device"   SvcDesc="Zhangfan"  

    时间:2019-06-11 关键词: 内核 kernel

  • Linux Kernel中AEP的现状和发展

    Linux Kernel中AEP的现状和发展

    AEP简介 AEP是Intel推出的一种新型的非易失Optane Memory设备,又被称作Apache Pass,所以一般习惯称作AEP。在这之前也有类似的设备称作NVDIMM或PMEM,目前Linux创建的AEP设备节点也是叫做pmem(如/dev/pmem0),所以本文中NVDIMM或PMEM都指AEP。但是本文不是为了科普AEP,如果想了解AEP的一些基本知识,可以参考以下几篇文章:NVDIMM Enabling in SUSE Linux Enterprise Part 1NVDIMM Enabling in SUSE Linux Enterprise Part 2Persistent Memory Wiki   DAX 目前Linux Kernel中主要把PMEM看成一个类似于磁盘的块设备,所以可以在PMEM设备上创建文件系统,使它看起来和一般的磁盘没什么区别。但是设备的具体物理属性完全不一样,比如读写的latency,PMEM可以达到和DRAM接近的程度,磁盘当然是望尘莫及的。所以,这就带来一个问题,众所周知,一般在Linux上常见的文件系统,比如ext4,xfs等,都是给磁盘设计的,都用到了page cache来缓存磁盘上的数据来提高性能。但是,对于PMEM设备来说,它的访问延迟已经和内存接近了,为什么还需要内存中的page cache呢?所以,目前Linux Kernel中对这一块最大的改进就是支持DAX(Direct Access)。一句话解释DAX,就是DAX bypass了page cache。无论读写都是直接操作PMEM上的数据。DAX需要在文件系统层面支持,如果要使用DAX,那么需要在mount文件系统时传入“-o dax”参数,比如: 1 /dev/pmem0 on /mnt type xfs (rw,relatime,seclabel,attr2,dax,inode64,noquota) DAX极大地提高了文件系统在PMEM设备上的性能,但是还有一些问题没有解决,比如:1. 文件系统的metadata还是需要使用page cache或buffer cache。2. “-o dax”mount option是对整个文件系统的,不能做更细粒度的控制。3. 没有一个API来告诉应用访问的文件是不是可以DAX访问的。虽然DAX还有这些问题,但是目前DAX还是Linux Kernel中的主流使用方式。 PMEM用作NUMA node 既然PMEM就是memory,只是带宽和latency上差一点,那么自然会想到能不能就把PMEM当做memory用呢?答案当然是可以的。目前支持SRAT或者HMAT的硬件,都可以把PMEM识别为一个或多个NUMA node。Dave Hansen的这组patch,Allow persistent memory to be used like normal RAM,就是通过memory hotplug的方式把PMEM添加到Linux的buddy allocator里面。 新添加的PMEM会以一个或多个NUMA node的形式出现,Linux Kernel就可以分配PMEM上的memory,这样和使用一般DRAM没什么区别。目前看这组patch已经没有什么blocking issues,不出什么问题的话,很快就会合并进入内核主线。但是,到这里只是解决了第一步的问题,怎么把PMEM“用好”的问题还没有解决。比如,当内核分配内存时,如果从PMEM上分配了memory,并且这块内存上的数据是被经常访问的,那么由于物理特性上的差异,一般应>用都会体会到性能的下降。那么怎么更明智的使用PMEM就是一个亟待解决的问题。 吴峰光的一组patch,PMEM NUMA node and hotness accounting/migration,来尝试解决这个问题。这组patch主要提供了下面几个功能:1. 隔离DRAM和PMEM。为PMEM单独构造了一个zonelist,这样一般的内存分配是不会分配到PMEM上的。2. 跟踪内存的冷热。利用内核中已经有的idle page tracking功能(目前主线内核只支持系统全局的tracking),在per process的粒度上跟踪内存的冷热。3. 利用现有的page reclaim,在reclaim时将冷内存迁移到PMEM上(只能迁移匿名页)。4. 利用一个userspace的daemon和idle page tracking,来将热内存(在PMEM上的)迁移到DRAM中。这组patch发到LKML以后,引来了很激烈的讨论。 主要集中在两个方面: 1. 为什么要单独构造一个zonelist把PMEM和DRAM分开?其实在这块,我们也遇到了相似的问题。我们在某些项目要求做到控制每个进程使用的DRAM和PMEM的比例(比如8:2),但是目前的NUMA API做不到。目前的NUMA API只能控制从哪个node分配,但是不能控制比例,>比如mbind(),只能告诉进程这段VMA可以用哪些node,但是不能控制具体多少memory从哪个node来。要想做到更细粒度的控制,需要改造目前的NUMA API。而且目前memory hierarchy越来越复杂,比如device memory,这都是目前的NUMA API所不能很好解决的。 2. 能不能把冷热内存迁移通用化?冷热内存迁移这个方向是没有问题的,问题在于目前patch中的处理太过于PMEM specific了。内核中的NUMA balancing是把“热”内存迁移到最近的NUMA node来提高性能。但是却没有对“冷”内存的处理。所以能不能实现一种更通用的NUMA rebalancing?比如,在reclaim时候,不是直接reclaim内存,而是把内存迁移到一个远端的,或者空闲的,或者低速的NUMA node,类似于NUMA balancing所做的,只不过是往相反的方向。笔者的一组patch,Another Approach to Use PMEM as NUMA Node,就体现了这种思路。利用Kernel中>已经很成熟的memory reclaim路径把“冷”内存迁移到PMEM node中,NUMA Balancing访问到这个page的时候可以选择是否把这个页迁移回DRAM,相当于是一种比较粗粒度的“热”内存识别。 社区中还有一种更加激进的想法就是不区分PMEM和DRAM,在memory reclaim时候只管把“冷”内存迁移到最近的remote node,如果target node也有内存压力,那就在target node上做同样的迁移。但是这种方法有可能引入一个内存迁移“环”,导致内存在NUMA node中间不停地迁移,有可能引入unbounded time问题。而且一旦node增多,可能会迅速恶化问题。 在笔者看来,在内存回收方面还有一个更可能立竿见影的方案就是把PMEM用作swap设备或者swap文件。目前swap的最大问题就是传统磁盘的延迟问题,很容易造成系统无响应,这也是为什么有zswap这样的技术出现。PMEM的低延迟特性完全可以消除swap的延迟问题。在这个方面,我们也正在做一些探索和实验。 PMEM用作RAM(DRAM作为Cache) 这个标题看起来有点歧义,上面已经说了PMEM可以作为NUMA node使用,这不已经是作为RAM了吗?怎么这里还要说用作RAM?这就涉及到AEP的另一个用法了,那就是所谓的“memory mode”。当在memory mode时,DRAM>并不是和PMEM并列的,而是变成了PMEM透明的Cache,PMEM就成了DRAM。这时候PMEM和DRAM的关系就变成了DRAM和Cache的关系。而且,DRAM是一个direct mapped的Cache(这点很重要)。这时疑问就来了,这样不是更没有什么可做的?既不需要管理NUMA,也没有冷热内存的问题了,热的自然就被Cache了。是的,但是这会引入另外一个问题,就是Cache冲突的问题。上面已经提到,在这种情况下,DRAM是一个direct mapped的Cache,就是在同样索引下只有一个cache line命中,这样会带来比较严重的Cache冲突问题,从而降低Cache的命中率,带来性能问题。对于这个问题的详细解释,请参见这篇文章为了解决这个Cache冲突的问题,Dan Williams提出了这组patch,mm: Randomize free memory。这组patch的想法很简单,就是通过randomize free area的方式来降低Cache>冲突。目前这组patch已经合并入-mm tree,不出意外应该会在5.1时合并入内核主线。但是这种配置的问题就是不够灵活,需要在BIOS中配置,一旦配置不可在运行时更改。 NVDIMM专用文件系统 前面提到PMEM可以作为一个块设备部署文件系统,但是现在支持的文件系统,比如ext4,xfs等,在设计时更多的考虑了怎样针对磁盘优化。但是PMEM是性质完全不同的存储介质,虽然经过一些改造,这些传统的文件系统可以比较好的工作在PMEM上,但是还是会有很多不适合PMEM的地方,比如metadata还要经过page cache等。所以,NVDIMM专用文件系统就应用而生了。 NOVA NOVA Filesystem就是专门为PMEM设计的文件系统。笔者对文件系统研究不深,而且对NOVA也没有很深入的研究,所以就不在这里班门弄斧了。感兴趣的读者可以参考NOVA的github link之前,NOVA曾发到LKML上,但是好像社区里的maintainer们没有时间仔细review一个新的文件系统,所以合入社区的努力暂时停止了,但是还在github上继续开发中。 ZUFS ZUFS是来自于NetApp的一个项目,ZUFS的意思是Zero-copy User Filesystem。声称是实现了完全的zero-copy,甚至文件系统的metadata都是zero-copy的。ZUFS主要是为了PMEM设计,但是也可以支持传统的磁盘设备,相当于是FUSE的zero-copy版本,是对FUSE的性能的提升。目前作者正在尝试将ZUFS的kernel部分upstream,据他说RHEL已经同意将ZUFS作为一个module加入RHEL 8。

    时间:2019-05-30 关键词: 内核 Linux aep

  • Linux内核驱动的platform机制

    Linux内核驱动的platform机制

    从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。 Linux platform driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备的流程是申请platform_device,注册platform_device,注册platform_driver platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下: struct platform_device { const char * name; //定义平台设备的名称 int id; struct device dev; u32 num_resources; struct resource * resource; //定义平台设备的资源。 }; 下面来看一下platform_device结构体中最重要的一个成员struct resource * resource。struct resource被定义在include/linux/ioport.h中,定义原型如下: struct resource { resource_size_t start; //定义资源的起始地址 resource_size_t end; //定义资源的结束地址 const char *name; //定义资源的名称 unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型 struct resource *parent, *sibling, *child; //资源链表指针 }; 通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用platform_device_register( )进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。 接下来来看platform_driver结构体的原型定义,在include/linux/platform_device.h中,代码如下: struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); struct device_driver driver; }; 内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下: int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; if (drv->suspend) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; return driver_register(&drv->driver); } 下面举个例子来说明一下: 在kernel/arch/arm/mach-pxa/pxa27x.c定义了 tatic struct resource pxa27x_ohci_resources[] = { [0] = { .start = 0x4C000000, .end = 0x4C00ff6f, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_USBH1, .end = IRQ_USBH1, .flags = IORESOURCE_IRQ, }, }; 这里定义了两组resource,它描述了一个usb host设备的资源,第1组描述了这个usb host设备所占用的 总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个usb host设备 的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。 有了resource信息,就可以定义platform_device了: static struct platform_device ohci_device = { .name = "pxa27x-ohci", .id = -1, .dev = { .dma_mask = &pxa27x_dmamask, .coherent_dma_mask = 0xffffffff, }, .num_resources = ARRAY_SIZE(pxa27x_ohci_resources), .resource = pxa27x_ohci_resources, }; 有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了,这里的实现是 static int __init pxa27x_init(void) { return platform_add_devices(devices, ARRAY_SIZE(devices)); } 这里的pxa27x_init必须在设备驱动加载之前被调用,可以把它放到 subsys_initcall(pxa27x_init); 驱动程序需要实现结构体struct platform_driver,参考kernel/driver/usb/host/ohci-pxa27.c, static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_drv_probe, .remove = ohci_hcd_pxa27x_drv_remove, #ifdef CONFIG_PM .suspend = ohci_hcd_pxa27x_drv_suspend, .resume = ohci_hcd_pxa27x_drv_resume, #endif .driver = { .name = "pxa27x-ohci", }, }; 在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是 ohci_device结构中name元素和ohci_hcd_pxa27x_driver结构中driver.name必须是相同的,这样 在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注 册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册 成功,当注册成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_pxa27x_drv_probe。 当进入probe函数后,需要获取设备的资源信息,获取资源的函数有: struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num); 根据参数type所指定类型,例如IORESOURCE_MEM,来获取指定的资源。 struct int platform_get_irq(struct platform_device *dev, unsigned int num); 获取资源中的中断号。 struct resource * platform_get_resource_byname(struct platform_device *dev, unsigned int type, char *name); 根据参数name所指定的名称,来获取指定的资源。 int platform_get_irq_byname(struct platform_device *dev, char *name); 根据参数name所指定的名称,来获取资源中的中断号。   总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。

    时间:2019-05-26 关键词: 内核 Linux

  • 手机CPU“核战”结束了吗?

    手机CPU“核战”结束了吗?

    曾几何时,咱们在买手机和电脑的时候,一定会注意这样一个数据:处理器是几核的。 由于当时电视广告也在铺天盖地宣传双核处理器、四核处理器、八核处理器,导致那时候即使是远离科技圈的大爷大妈,遛弯时候也会这ô打招呼:换手机了啊?几核的啊? 不知不觉间,手机搭载了几核CPU这个概念似乎已经离我们远去了。大概是对于一般用户来说,手机已经变成了彻底的日常用品,而因为同一价λ能买到的手机,从配置上看已经大差不差,好像也û什ô需要仔细对比的了? 那ô,关于手机CPU的“核”问题,就这样彻底结束了吗? 或许并û有。原因在于核心数这个概念或许已经家喻户晓,但大核和小核的区别,对于大多数消费者来说还是十分陌生的。手机存量战争时代的一个新机会,就隐藏在这个既熟悉又陌生的话题里。 手机谈“核”,已经过时了吗? 从智能手机刚刚兴起不久,厂商就已经开始教育用户关于CPU核心数的问题。一般意义上来说,确实CPU的核心数越多,处理能力就越强,手机的性能也就越好。 而且毕竟CPU核心数再多也不会到三λ数,这个概念非常容易理解,于是让大部分消费者都开始以此为参考来选购手机。 然而可能大部分消费者û有搞清楚的概念是,手机CPU核心,其实还有大核和小核的区别。 手机CPU的大小核概念,是英国著名半导体厂商Arm在2011年提出的。在此之前CPU确实只有核心数的差别。 在Arm的设计中,手机CPU可以不仅仅是用几个同样性能的核心并列,而是走向大小搭配的新玩法。 一般来说,大核的流水线为三发射甚至四发射,而小核心的流水线一般为双发射。相比小核,大核具备更快的指令处理速度核总线吞吐能力,其性能可以比小核心高2到3倍左右。 为什ô要分大小核呢? 原因在于,手机CPU的多核设计,原本就是为了能够负载大型计算任务。但是在手机的实际使用场景里,有些时候需要的不是长时间负载大型计算,而是要在短时间内快速获得计算爆发力。比如快速加载应用,或者手机触屏需要流畅体验的时候。 显然,这一类的“爆发力”场景非常多,但是调用多核处理能力却会带来效率降低。所以更实用的方式是用一个大核来提供短暂时刻的高性能反应,其他时候用小核来确保续航。 大小核设计,不仅是更加实际地解决了手机实用场景中的性能需求,更重要的是为手机体验提供了新的可能:比如,一大核三小核的四核设计处理器,可能在实际应用中体验并不输给八小核的八核处理器,而消费者付出的成本却可能大幅降低。 换言之,芯片厂商对于核心架构的理解与创新,正在让“手机核战”这个老话题,焕发出新的想象空间。 存量战争时代, 大核CPU意ζ着什ô? 到底大核小核应该用什ô标准判定呢? 其实也很简单,用户可以通过 GeekBench等性能跑分工具,查一下手机CPU的单核跑分成绩。通常情况下,同样是采用了2.0GHz频率,大核的单核跑分至少在1600以上,小核最多达到1200。 而大小核处理器带给整个手机产业的变化机遇在于,这个创新点可以在基本不增加用户成本的前提下,极大提高用户的手机使用体验。 其原因在于,手机日常实用场景中,大部分时间是只有单核在工作的。那ô把这个单核的性能提升上去,也就意ζ着对于大部分用户来说最常见的交互和启动APP等体验得到了大幅提升。而且由于芯片û有增加核心数,用户成本并不会大幅提高。 这样的低成本+性能优化,与如今的手机市场需求不谋而合。到今天,中国手机市场的出货量已经大幅下降,用户增量红利基本宣告结束,产业开始整体进入存量战争时代。 而与旗舰机用户可以基本只认品牌价值、核心技术创新,以及软件生态体验来买货不同,国内手机85%以上的出货量都是由中低端机型来完成的。 而这些机型的特点,是产品型号相当复杂,同质化竞争激烈。而用户很难判断究竟该如何选取产品。这种情况下,大核CPU芯片显然是在同价的前提下,带给用户以一个新的抉择砝码。 而另一方面,手机产品的增量市场,今天已经转移到亚非拉美等海外市场当中。这些市场正处在发育初期,高端机出货量有限。如何从芯片端开始保证手机产品的质优价廉,是行业共同谋求的主旋律。那ô大核CPU,又恰好刺入了这样一个需求点。 因此上,如何保证大核CPU手机的性价比足够强劲,很可能是“手机核战”接下来的主要命题——而这也是移动芯片厂商新的战略机遇。 “核战”继续,所打开的产业机遇 事实上,大核CPU早在2014年就开始出现在华为和三星的旗舰产品中,至今仍是旗舰机的主流选择。但是由于成本高昂,几年过去了,依旧û有在中低端市场中铺开。如今我们看到的手机芯片产品,更多还是采用四小核的模式。即使一些主打性价比的八核处理器,也û有推出大小核设计的产品。 然而大核CPU带来的市场变化,很可能在2019年奏响。今天我们已经看到,紫光展锐在4月初发布了新一代LTE平台虎贲T310芯片。这款新品就是采用1颗大核心2.0 GHz Cortex-A75处理器核心 和3颗小核心1.8 GHz Cortex-A55 处理器核心组合。主打“四核配置、八核性能”的卖点。 为了配合大小核搭配的特性,虎贲T310上采用了中高端平台才会用的Arm DynamIQ架构。能够可对ÿ个核配置不同的性能,进行独立的频率和电压控制,这样的架构特性可使CPU在获得更高性能的同时,又能精细化的控制功耗。这样的技术创新,让4核处理器在某些场景里甚至达成了高过8核处理器的性能。最终即是让四核的低功耗核低成本保留了下来,而用户收获了高端机才有的性能体验。 有理由相信,虎贲T310是一个产业机遇的开始。通过更具优势的架构创新和精细化调用,让低成本配置发挥出高水准性能,将是手机芯片市场接下来的主要航道。 在今天是,手机产品的创新能力已经普遍放缓。厂商只能在材质、外观设计等层面强调创新。然而基础算力的创新其实并δ终止。换言之,核心体验的创新,才是供应链与手机厂商,最终收获用户口碑的根本。而“大核CPU”的创新解决能力,在这个节点上也就显的弥足珍贵。 或许在某种意义上来说,手机“核战”才刚刚开始。

    时间:2019-05-07 关键词: CPU 内核 处理器 厂商动态

  • 坚果内核变量居然使用拼音命名!还拼错了!如何规范C语言编程?

    坚果内核变量居然使用拼音命名!还拼错了!如何规范C语言编程?

    作者:付斌不久前,锤子科技天猫官方旗舰店商品全线下架,锤子科技商城的多款手机也显示为“到货通知”。锤子可以的前途渺茫。小米9发布后,其开源了小米9稳定版内核代码,同时,开发者发现,锤子科技的多款手机内核代码也已经在 Github 上开源,包括 Smartian T1、T2、M1、M1L,以及坚果系列。小米9内核地址:https://github.com/MiCode/Xiaomi_Kernel_OpenSource/tree/cepheus-p-oss坚果系列内核地址:https://github.com/SmartisanTech/SmartisanOS_Kernel_Source代码公开后,锤子科技贴吧名为jocover的网友吐槽说,代码中变量居然用拼音命名!该网友表示,谁知道fenbianlu是啥玩意?而且代码里一堆日期标注,难道锤子连git代码管理都不会用了?这是背光的驱动,为ODM公司所写,不过这个fenbianlu是锤科的天才lishaokai改的,看注释,还有个变量temp也无力吐槽,加了俩变量还加成这样,我佛了。或许是一时疏忽将fenbianlv打错了,也或许是拼音+英文创造出fenbian+luminance。有人表示上市公司其实拼音英语双标的情况屡见不鲜,也有人表示这样做未免有点不够国际化,还有人表示,拼音怎么了,能用不就就行了。当然,这也给黑粉趁虚而入的机会:“居然没能写成日文,罗老师一定很不开心”。接下来代码又被扒出CPU作弊。如下图,代码写了一大堆,其实就是把boost时间加个5毫秒的样子,号称smartisan cpu boost。据猜测,为动画效果提供时钟延迟,为了界面看起来漂亮。另外,还有用坚果R1电池,另外有个670的方案,估计和T2的810版本一样难产不见。据悉,阿里巴巴、华为等公司严禁拼音与英文混合的方式命名,另外,还强制要求避免采用纯拼音方式命名。虽然这些的确可能不会影响到程序的运行,但作为严谨严肃的程序员,优质的程序需要精心的雕琢,应该尽量避免这种情况。网友表示,这种三流程序员就不要多说了,可能连阿里一面都过不了。那么该如何规范C语言编程?此前嵌入式ARM曾多次发布此类文章:嵌入式软件开发编程规范你了解吗?小心吃大亏严于律己!写给自己的C++编程规范养成良好的嵌入式C代码编码习惯要遵循哪些规则?今天,让我们再来学习一下华为的C语言编程规范(来源网络):1 排版1-1:程序块要采用缩进风格编写,缩进的空格数为4个。说明:对于由开发工具自动生成的代码可以有不一致。1-2:相对独立的程序块之间、变量说明之后必须加空行。如下例子不符合规范:if (!valid_ni(ni)){... // program code}repssn_ind = ssn_data[index].repssn_index;repssn_ni = ssn_data[index].ni;应如下书写:if (!valid_ni(ni)){... // program code}repssn_ind = ssn_data[index].repssn_index;repssn_ni = ssn_data[index].ni;1-3:较长的语句(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。示例:perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN+STAT_SIZE_PER_FRAM * sizeof( _UL );act_task_table[frame_id * STAT_TASK_CHECK_NUMBER +index].occupied= stat_poi[index].occupied;act_task_table[taskno].duration_true_or_false=SYS_get_sccp_statistic_state( stat_item );report_or_not_flag = ((taskno 1-4:不允许把多个短语句写在一行中,即一行只写一条语句。如下例子不符合规范:rect.length= 0; rect.width = 0;应如下书写:rect.length= 0;rect.width =0;1-5:if、for、do、while、case、switch、default等语句自占一行,且if、for、do、while等语句的执行语句部分无论多少都要加括号{}。如下例子不符合规范:if (pUserCR== NULL) return;应如下书写:if (pUserCR== NULL){return;}1-6:对齐只使用空格键,不使用TAB键。说明:以免用不同的编辑器阅读程序时,因TAB键所设置的空格数目不同而造成程序布局不整齐,不要使用BC作为编辑器合版本,因为BC会自动将8个空格变为一个TAB键,因此使用BC合入的版本大多会将缩进变乱。1-7:函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case语句下的情况处理语句也要遵从语句缩进要求。1-8:程序块的分界符(如C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以及if、for、do、while、switch、case 语句中的程序都要采用如上的缩进方式。如下例子不符合规范:for (...) {... //program code}if (...){... //program code}voidexample_fun( void ){... //program code}应如下书写:for (...){... //program code}if (...){... //program code}voidexample_fun( void ){... //program code}1-9:一行程序以小于80字符为宜,不要写得过长。2 注释2-1:一般情况下,源程序有效注释量必须在20%以上。说明:注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。2-2:文件头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、修改日志等。示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。/*****************************************************************************Copyright: 1988-1999, Huawei Tech. Co., Ltd.File name: 文件名Description: 用于详细说明此程序文件完成的主要功能,与其他模块或函数的接口,输出值、取值范围、含义及参数间的控制、顺序、独立或依赖等关系Author: 作者Version: 版本Date: 完成日期History: 修改历史记录列表,每条修改记录应包括修改日期、修改者及修改内容简述。*****************************************************************************/2-3:函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等。示例:下面这段函数的注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。/*************************************************Function: // 函数名称Description: // 函数功能、性能等的描述Calls: // 被本函数调用的函数清单Called By: // 调用本函数的函数清单Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序)Table Updated: // 被修改的表(此项仅对于牵扯到数据库操作的程序)Input: // 输入参数说明,包括每个参数的作// 用、取值说明及参数间关系。Output: // 对输出参数的说明。Return: // 函数返回值的说明Others: // 其它说明*************************************************/2-4:边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。2-5:注释的内容要清楚、明了,含义准确,防止注释二义性。说明:错误的注释不但无益反而有害。2-6:注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。如下例子不符合规范:例1:/* getreplicate sub system index and net indicator */repssn_ind =ssn_data[index].repssn_index;repssn_ni =ssn_data[index].ni;例2:repssn_ind =ssn_data[index].repssn_index;repssn_ni =ssn_data[index].ni;/* getreplicate sub system index and net indicator */应如下书写:/* getreplicate sub system index and net indicator */repssn_ind =ssn_data[index].repssn_index;repssn_ni =ssn_data[index].ni;2-7:对于所有有物理含义的变量、常量,如果其命名不是充分自注释的,在声明时都必须加以注释,说明其物理含义。变量、常量、宏的注释应放在其上方相邻位置或右方。示例:/* activestatistic task number */#defineMAX_ACT_TASK_NUMBER 1000#defineMAX_ACT_TASK_NUMBER 1000 /* active statistic task number */2-8:数据结构声明(包括数组、结构、类、枚举等),如果其命名不是充分自注释的,必须加以注释。对数据结构的注释应放在其上方相邻位置,不可放在下面;对结构中的每个域的注释放在此域的右方。示例:可按如下形式说明枚举/数据/联合结构。/* sccpinterface with sccp user primitive message name */enumSCCP_USER_PRIMITIVE{N_UNITDATA_IND, /* sccp notify sccp user unit data come*/N_NOTICE_IND, /* sccp notify user the No.7 network cannot *//* transmission this message */N_UNITDATA_REQ, /* sccp user's unit data transmissionrequest*/};2-9:全局变量要有较详细的注释,包括对其功能、取值范围、哪些函数或过程存取它以及存取时注意事项等的说明。示例:/* TheErrorCode when SCCP translate *//* GlobalTitle failure, as follows */ // 变量作用、含义/* 0 - SUCCESS 1 - GT Table error *//* 2 - GT error Others - no use */ // 变量取值范围/* onlyfunction SCCPTranslate() in *//* thismodual can modify it, and other *//* modulecan visit it through call *//* thefunction GetGTTransErrorCode() */ // 使用方法BYTEg_GTTranErrorCode;2-10:注释与所描述内容进行同样的缩排。说明:可使程序排版整齐,并方便注释的阅读与理解。示例:如下例子,排版不整齐,阅读稍感不方便。voidexample_fun( void ){/* code one comments */CodeBlock One/* code two comments */CodeBlock Two}应改为如下布局:voidexample_fun( void ){/* code one comments */CodeBlock One/* code two comments */CodeBlock Two}2-11:避免在一行代码或表达式的中间插入注释。说明:除非必要,不应在代码或表达中间插入注释,否则容易使代码可理解性变差。2-12:通过对函数或过程、变量、结构等正确的命名以及合理地组织代码的结构,使代码成为自注释的。说明:清晰准确的函数、变量等的命名,可增加代码可读性,并减少不必要的注释。2-13:在代码的功能、意图层次上进行注释,提供有用、额外的信息。说明:注释的目的是解释代码的目的、功能和采用的方法,提供代码以外的信息,帮助读者理解代码,防止没必要的重复注释信息。示例:如下注释意义不大。/* ifreceive_flag is TRUE */if(receive_flag)而如下的注释则给出了额外有用的信息。/* if mtpreceive a message from links */if(receive_flag)2-14:在程序块的结束行右方加注释标记,以表明某程序块的结束。说明:当代码段较长,特别是多重嵌套时,这样做可以使代码更清晰,更便于阅读。示例:参见如下例子。if (...){// programcodewhile (index< MAX_INDEX){// programcode} /* end ofwhile (index < MAX_INDEX) */ // 指明该条while 语句结束} /* end ofif (...)*/ // 指明是哪条if 语句结束2-15:注释格式尽量统一,建议使用“/* …… */”。2-16:注释应考虑程序易读及外观排版的因素,使用的语言若是中、英兼有的,建议多使用中文,除非能用非常流利准确的英文表达。说明:注释语言不统一,影响程序易读性和外观排版,出于对维护人员的考虑,建议使用中文。3 标识符命名3-1:标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。说明:较短的单词可通过去掉“元音”形成缩写;较长的单词可取单词的头几个字母形成缩写;一些单词有大家公认的缩写。示例:如下单词的缩写能够被大家基本认可。temp 可缩写为 tmp ;flag 可缩写为 flg ;statistic 可缩写为 stat ;increment 可缩写为 inc ;message 可缩写为 msg ;3-2:命名中若使用特殊约定或缩写,则要有注释说明。说明:应该在源文件的开始之处,对文件中所使用的缩写或约定,特别是特殊的缩写,进行必要的注释说明。3-3:自己特有的命名风格,要自始至终保持一致,不可来回变化。说明:个人的命名风格,在符合所在项目组或产品组的命名规则的前提下,才可使用。(即命名规则中没有规定到的地方才可有个人命名风格)。3-4:对于变量命名,禁止取单个字符(如i、j、k...),建议除了要有具体含义外,还能表明其变量类型、数据类型等,但i、j、k 作局部循环变量是允许的。说明:变量,尤其是局部变量,如果用单个字符表示,很容易敲错(如i 写成j),而编译时又检查不出来,有可能为了这个小小的错误而花费大量的查错时间。示例:下面所示的局部变量名的定义方法可以借鉴。intliv_Width其变量名解释如下:l 局部变量(Local)(其它:g 全局变量(Global)...)i 数据类型(Interger)v 变量(Variable)(其它:c 常量(Const)...)Width 变量含义这样可以防止局部变量与全局变量重名。3-5:命名规范必须与所使用的系统风格保持一致,并在同一项目中统一,比如采用UNIX的全小写加下划线的风格或大小写混排的方式,不要使用大小写与下划线混排的方式,用作特殊标识如标识成员变量或全局变量的m_和g_,其后加上大小写混排的方式是允许的。示例: Add_User 不允许,add_user、AddUser、m_AddUser 允许。3-6:除非必要,不要用数字或较奇怪的字符来定义标识符。示例:如下命名,使人产生疑惑。#define_EXAMPLE_0_TEST_#define_EXAMPLE_1_TEST_voidset_sls00( BYTE sls );应改为有意义的单词命名#define_EXAMPLE_UNIT_TEST_#define _EXAMPLE_ASSERT_TEST_voidset_udt_msg_sls( BYTE sls );3-7:在同一软件产品内,应规划好接口部分标识符(变量、结构、函数及常量)的命名,防止编译、链接时产生冲突。说明:对接口部分的标识符应该有更严格限制,防止冲突。如可规定接口部分的变量与常量之前加上“模块”标识等。3-8:用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。说明:下面是一些在软件中常用的反义词组。add / removebegin / end create / destroyinsert /delete first / last get / releaseincrement /decrement put / getadd / deletelock / unlock open / closemin / maxold / new start / stopnext /previous source / target show / hidesend /receive source / destinationcut / pasteup / down示例:int min_sum;int max_sum;intadd_user( BYTE *user_name );intdelete_user( BYTE *user_name );3-9:除了编译开关/头文件等特殊应用,应避免使用_EXAMPLE_TEST_之类以下划线开始和结尾的定义。4 可读性4-1:注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级。说明:防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。示例:下列语句中的表达式word = (high<< 8) | low (1)if ((a | b)&& (a & c)) (2)if ((a | b)< (c & d)) (3)如果书写为:high<< 8 | lowa | b&& a & ca | b < c& d由于:high<< 8 | low = ( high << 8) | low,a | b&& a & c = (a | b) && (a & c),(1)(2)不会出错,但语句不易理解;a | b < c & d = a | (b < c) & d,(3)造成了判断条件出错。4-2:避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。示例:如下的程序可读性差。if(Trunk[index].trunk_state == 0){Trunk[index].trunk_state= 1;... //program code}应改为如下形式:#defineTRUNK_IDLE 0#defineTRUNK_BUSY 1if(Trunk[index].trunk_state == TRUNK_IDLE){Trunk[index].trunk_state= TRUNK_BUSY;//program code}4-3:源程序中关系较为紧密的代码应尽可能相邻。说明:便于程序阅读和查找。示例:以下代码布局不太合理。rect.length= 10;char_poi =str;rect.width =5;若按如下形式书写,可能更清晰一些。rect.length= 10;rect.width =5; // 矩形的长与宽关系较密切,放在一起。char_poi =str;4-4:不要使用难懂的技巧性很高的语句,除非很有必要时。说明:高技巧语句不等于高效率的程序,实际上程序的效率关键在于算法。示例:如下表达式,考虑不周就可能出问题,也较难理解。* stat_poi++ += 1;* ++stat_poi += 1;应分别改为如下。*stat_poi +=1;stat_poi++;// 此二语句功能相当于“ * stat_poi ++ += 1; ”++ stat_poi;*stat_poi +=1; // 此二语句功能相当于“ * ++ stat_poi += 1; ”5 变量、结构5-1:去掉没必要的公共变量。说明:公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。5-2:仔细定义并明确公共变量的含义、作用、取值范围及公共变量间的关系。说明:在对变量声明的同时,应对其含义、作用及取值范围进行注释说明,同时若有必要还应说明与其它变量的关系。5-3:明确公共变量与操作此公共变量的函数或过程的关系,如访问、修改及创建等。说明:明确过程操作变量的关系后,将有利于程序的进一步优化、单元测试、系统联调以及代码维护等。这种关系的说明可在注释或文档中描述。示例:在源文件中,可按如下注释形式说明。RELATIONSystem_Init Input_Rec Print_Rec Stat_Score Student Create Modify Access AccessScore Create Modify Access Access, Modify注:RELATION 为操作关系;System_Init、Input_Rec、Print_Rec、Stat_Score 为四个不同的函数;Student、Score 为两个全局变量;Create 表示创建,Modify 表示修改,Access 表示访问。其中,函数Input_Rec、Stat_Score 都可修改变量Score,故此变量将引起函数间较大的耦合,并可能增加代码测试、维护的难度。5-4:当向公共变量传递数据时,要十分小心,防止赋与不合理的值或越界等现象发生。说明:对公共变量赋值时,若有必要应进行合法性检查,以提高代码的可靠性、稳定性。5-5:防止局部变量与公共变量同名。说明:若使用了较好的命名规则,那么此问题可自动消除。5-6:严禁使用未经初始化的变量作为右值。说明:特别是在C/C++中引用未经赋值的指针,经常会引起系统崩溃。5-7:结构的功能要单一,是针对一种事务的抽象。说明:设计结构时应力争使结构代表一种现实事务的抽象,而不是同时代表多种。结构中的各元素应代表同一事务的不同侧面,而不应把描述没有关系或关系很弱的不同事务的元素放到同一结构中。示例:如下结构不太清晰、合理。typedefstruct STUDENT_STRU{unsignedchar name[8]; /* student's name */unsignedchar age; /* student's age */unsignedchar sex; /* student's sex, as follows *//* 0 -FEMALE; 1 - MALE */unsignedcharteacher_name[8];/* the student teacher's name */unisgnedcharteacher_sex;/* his teacher sex */} STUDENT;若改为如下,可能更合理些:typedefstruct TEACHER_STRU{unsignedchar name[8]; /* teacher name */unisgnedchar sex; /* teacher sex, as follows *//* 0 -FEMALE; 1 - MALE */} TEACHER;typedefstruct STUDENT_STRU{unsignedchar name[8]; /* student's name */unsignedchar age; /* student's age */unsignedchar sex; /* student's sex, as follows *//* 0 -FEMALE; 1 - MALE */unsigned intteacher_ind; /* his teacher index */} STUDENT;5-8:不要设计面面俱到、非常灵活的数据结构。说明:面面俱到、灵活的数据结构反而容易引起误解和操作困难。5-9:不同结构间的关系不要过于复杂。说明:若两个结构间关系较复杂、密切,那么应合为一个结构。示例:如下两个结构的构造不合理。typedefstruct PERSON_ONE_STRU{unsignedchar name[8];unsignedchar addr[40];unsignedchar sex;unsignedchar city[15];}PERSON_ONE;typedefstruct PERSON_TWO_STRU{unsignedchar name[8];unsignedchar age;unsignedchar tel;}PERSON_TWO;由于两个结构都是描述同一事物的,那么不如合成一个结构。typedefstruct PERSON_STRU{unsignedchar name[8];unsignedchar age;unsignedchar sex;unsignedchar addr[40];unsignedchar city[15];unsignedchar tel;} PERSON;5-10:结构中元素的个数应适中。若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数。说明:增加结构的可理解性、可操作性和可维护性。示例:假如认为如上的_PERSON 结构元素过多,那么可如下对之划分。typedefstruct PERSON_BASE_INFO_STRU{unsignedchar name[8];unsignedchar age;unsignedchar sex;}PERSON_BASE_INFO;typedefstruct PERSON_ADDRESS_STRU{unsignedchar addr[40];unsignedchar city[15];unsignedchar tel;}PERSON_ADDRESS;typedefstruct PERSON_STRU{PERSON_BASE_INFOperson_base;PERSON_ADDRESSperson_addr;} PERSON;5-11:仔细设计结构中元素的布局与排列顺序,使结构容易理解、节省占用空间,并减少引起误用现象。说明:合理排列结构中元素顺序,可节省空间并增加可理解性。示例:如下结构中的位域排列,将占较大空间,可读性也稍差。typedefstruct EXAMPLE_STRU{unsigned intvalid: 1;PERSONperson;unsigned intset_flg: 1;} EXAMPLE;若改成如下形式,不仅可节省1字节空间,可读性也变好了。typedefstruct EXAMPLE_STRU{unsigned intvalid: 1;unsigned intset_flg: 1;PERSONperson ;} EXAMPLE;5-12:编程时,要注意数据类型的强制转换。说明:当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。5-13:对编译系统默认的数据类型转换,也要有充分的认识。示例:如下赋值,多数编译器不产生告警,但值的含义还是稍有变化。char chr;unsignedshort int exam;chr = -1;exam = chr;// 编译器不产生告警,此时exam 为0xFFFF。5-14:尽量减少没有必要的数据类型默认转换与强制转换。5-15:合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换。5-16:对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意其命名方式在同一产品中的统一。说明:使用自定义类型,可以弥补编程语言提供类型少、信息量不足的缺点,并能使程序清晰、简洁。示例:可参考如下方式声明自定义数据类型。下面的声明可使数据类型的使用简洁、明了。typedefunsigned char BYTE;typedefunsigned short WORD;typedefunsigned int DWORD;下面的声明可使数据类型具有更丰富的含义。typedeffloat DISTANCE;typedeffloat SCORE;6 函数、过程6-1:对所调用函数的错误返回码要仔细、全面地处理。6-2:明确函数功能,精确(而不是近似)地实现函数设计。6-3:编写可重入函数时,应注意局部变量的使用(如编写C/C++语言的可重入函数时,应使用auto 即缺省态局部变量或寄存器变量)。说明:编写C/C++语言的可重入函数时,不应使用static 局部变量,否则必须经过特殊处理,才能使函数具有可重入性。6-4:编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V 操作)等手段对其加以保护。说明:若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。示例:假设Exam 是int 型全局变量,函数Squre_Exam 返回Exam 平方值。那么如下函数不具有可重入性。unsigned intexample( int para ){unsigned inttemp;Exam = para;// (**)temp =Square_Exam( );return temp;}此函数若被多个进程调用的话,其结果可能是未知的,因为当(**)语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使Exam赋与另一个不同的para 值,所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改进。unsigned intexample( int para ){unsigned inttemp;[申请信号量操作] // 若申请不到“信号量”,说明另外的进程正处于Exam = para; // 给Exam 赋值并计算其平方过程中(即正在使用此temp = Square_Exam( ); // 信号),本进程必须等待其释放信号后,才可继[释放信号量操作]// 续执行。若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。return temp;}6-5:在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责,缺省是由函数调用者负责。说明:对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。6-6:函数的规模尽量限制在200行以内。说明:不包括注释和空格行。6-7:一个函数仅完成一件功能,不要设计多用途面面俱到的函数。说明:多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。6-8:函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出。说明:带有内部“存储器”的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维护。在C/C++语言中,函数的static 局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是STATIC的局部变量的地址作为返回值,若为AUTO类,则返回为错针。示例:如下函数,其返回值(即功能)是不可预测的。unsigned intinteger_sum( unsigned int base ){unsigned intindex;staticunsigned int sum = 0; // 注意,是static 类型的。// 若改为auto 类型,则函数即变为可预测。for (index =1; index <= base; index++){sum +=index;}return sum;}6-9:尽量不要编写依赖于其他函数内部实现的函数。说明:此条为函数独立性的基本要求。由于目前大部分高级语言都是结构化的,所以通过具体语言的语法要求与编译器功能,基本就可以防止这种情况发生。但在汇编语言中,由于其灵活性,很可能使函数出现这种情况。示例:如下是在DOS下TASM的汇编程序例子。过程Print_Msg的实现依赖于Input_Msg的具体实现,这种程序是非结构化的,难以维护、修改。... // 程序代码procPrint_Msg // 过程(函数)Print_Msg... // 程序代码jmp LABEL... // 程序代码endpprocInput_Msg // 过程(函数)Input_Msg... // 程序代码LABEL:... // 程序代码endp6-10:检查函数所有参数输入的有效性。6-11:检查函数所有非参数输入的有效性,如数据文件、公共变量等。说明:函数的输入主要有两种:一种是参数输入;另一种是全局变量、数据文件的输入,即非参数输入。函数在使用输入之前,应进行必要的检查。6-12:函数名应准确描述函数的功能。6-13:使用动宾词组为执行某操作的函数命名。如果是OOP方法,可以只有动词(名词是对象本身)。示例:参照如下方式命名函数。voidprint_record( unsigned int rec_ind ) ;intinput_record( void ) ;unsignedchar get_current_color( void ) ;6-14:避免使用无意义或含义不清的动词为函数命名。说明:避免用含义不清的动词如process、handle 等为函数命名,因为这些动词并没有说明要具体做什么。6-15:函数的返回值要清楚、明了,让使用者不容易忽视错误情况。说明:函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。6-16:除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。6-17:让函数在调用点显得易懂、容易理解。6-18:在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换。说明:因为数据类型转换或多或少存在危险。6-19:避免函数中不必要语句,防止程序中的垃圾代码。说明:程序中的垃圾代码不仅占用额外的空间,而且还常常影响程序的功能与性能,很可能给程序的测试、维护等造成不必要的麻烦。6-20:防止把没有关联的语句放到一个函数中。说明:防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。示例:如下函数就是一种随机内聚。voidInit_Var( void ){Rect.length = 0;Rect.width = 0; /* 初始化矩形的长与宽 */Point.x = 10;Point.y = 10; /* 初始化“点”的坐标 */}矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。应如下分为两个函数:voidInit_Rect( void ){Rect.length = 0;Rect.width = 0; /* 初始化矩形的长与宽 */}voidInit_Point( void ){Point.x = 10;Point.y = 10; /* 初始化“点”的坐标 */}6-21:如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题。说明:若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。6-22:功能不明确较小的函数,特别是仅有一个上级函数调用它时,应考虑把它合并到上级函数中,而不必单独存在。说明:模块中函数划分的过多,一般会使函数间的接口变得复杂。所以过小的函数,特别是扇入很低的或功能不明确的函数,不值得单独存在。6-23:设计高扇入、合理扇出(小于7)的函数。说明:扇出是指一个函数直接调用(控制)其它函数的数目,而扇入是指有多少上级函数调用它。扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数;而扇出过小,如总是1,表明函数的调用层次可能过多,这样不利程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造成压力。函数较合理的扇出(调度函数除外)通常是3-5。扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进一步分解多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。较良好的软件结构通常是顶层函数的扇出较高,中层函数的扇出较少,而底层函数则扇入到公共模块中。6-24:减少函数本身或函数间的递归调用。说明:递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用。6-26:改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代码可读性、效率和可维护性。优化函数结构时,要遵守以下原则:(1)不能影响模块功能的实现。(2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。(3)通过分解或合并函数来改进软件结构。(4)考查函数的规模,过大的要进行分解。(5)降低函数间接口的复杂度。(6)不同层次的函数调用要有较合理的扇入、扇出。(7)函数功能应可预测。(8)提高函数内聚。(单一功能的函数内聚最高)说明:对初步划分后的函数结构应进行改进、优化,使之更为合理。6-27:在多任务操作系统的环境下编程,要注意函数可重入性的构造。说明:可重入性是指函数可以被多个任务进程调用。在多任务操作系统中,函数是否具有可重入性是非常重要的,因为这是多个进程可以共用此函数的必要条件。另外,编译器是否提供可重入函数库,与它所服务的操作系统有关,只有操作系统是多任务时,编译器才有可能提供可重入函数库。如DOS 下BC 和MSC 等就不具备可重入函数库,因为DOS 是单用户单任务操作系统。6-28:避免使用BOOL参数。说明:原因有二,其一是BOOL参数值无意义,TURE/FALSE的含义是非常模糊的,在调用时很难知道该参数到底传达的是什么意思;其二是BOOL参数值不利于扩充。还有NULL也是一个无意义的单词。6-29:对于提供了返回值的函数,在引用时最好使用其返回值。6-30:当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替。说明:这样可以增加编程效率和程序的可读性。示例:在某过程中较多引用TheReceiveBuffer[FirstSocket].byDataPtr,则可以通过以下宏定义来代替:# definepSOCKDATA TheReceiveBuffer[FirstScoket].byDataPtr7 程序效率7-1:编程时要经常注意代码的效率。说明:代码效率分为全局效率、局部效率、时间效率及空间效率。全局效率是站在整个系统的角度上的系统效率;局部效率是站在模块或函数角度上的效率;时间效率是程序处理输入任务所需的时间长短;空间效率是程序所需内存空间,如机器代码空间大小、数据空间大小、栈空间大小等。7-2:在保证软件系统的正确性、稳定性、可读性及可测性的前提下,提高代码效率。说明:不能一味地追求代码效率,而对软件的正确性、稳定性、可读性及可测性造成影响。7-3:局部效率应为全局效率服务,不能因为提高局部效率而对全局效率造成影响。7-4:通过对系统数据结构的划分与组织的改进,以及对程序算法的优化来提高空间效率。说明:这种方式是解决软件空间效率的根本办法。示例:如下记录学生学习成绩的结构不合理。typedefunsigned char BYTE;typedefunsigned short WORD;typedefstruct STUDENT_SCORE_STRUBYTEname[8];BYTE age;BYTE sex;BYTE class;BYTEsubject;float score;}STUDENT_SCORE;因为每位学生都有多科学习成绩,故如上结构将占用较大空间。应如下改进(分为两个结构),总的存贮空间将变小,操作也变得更方便。typedefstruct STUDENT_STRU{BYTEname[8];BYTE age;BYTE sex;BYTE class;} STUDENT;typedefstruct STUDENT_SCORE_STRU{WORDstudent_index;BYTEsubject;float score;}STUDENT_SCORE;7-5:循环体内工作量最小化。说明:应仔细考虑循环体内的语句是否可以放在循环体之外,使循环体内工作量最小,从而提高程序的时间效率。示例:如下代码效率不高。for (ind =0; ind < MAX_ADD_NUMBER; ind++){sum += ind;back_sum = sum; /* backup sum */}语句“back_sum = sum;”完全可以放在for 语句之后,如下。for (ind =0; ind < MAX_ADD_NUMBER; ind++){sum+= ind;}back_sum =sum; /* backup sum */7-6:仔细分析有关算法,并进行优化。仔细考查、分析系统及模块处理输入(如事务、消息等)的方式,并加以改进。7-7:对模块中函数的划分及组织方式进行分析、优化,改进模块中函数的组织结构,提高程序效率。说明:软件系统的效率主要与算法、处理任务方式、系统功能及函数结构有很大关系,仅在代码上下功夫一般不能解决根本问题。7-8:编程时,要随时留心代码效率;优化代码时,要考虑周全。7-9:不应花过多的时间拼命地提高调用不很频繁的函数代码效率。说明:对代码优化可提高效率,但若考虑不周很有可能引起严重后果。7-10:要仔细地构造或直接用汇编编写调用频繁或性能要求极高的函数。说明:只有对编译系统产生机器码的方式以及硬件系统较为熟悉时,才可使用汇编嵌入方式。嵌入汇编可提高时间及空间效率,但也存在一定风险。7-11:在保证程序质量的前提下,通过压缩代码量、去掉不必要代码以及减少不必要的局部和全局变量,来提高空间效率。说明:这种方式对提高空间效率可起到一定作用,但往往不能解决根本问题。7-12:在多重循环中,应将最忙的循环放在最内层。说明:减少CPU切入循环层的次数。示例:如下代码效率不高。for (row =0; row < 100; row++){for (col = 0; col < 5; col++){sum += a[row][col];}}可以改为如下方式,以提高效率。for (col =0; col < 5; col++){for (row = 0; row < 100; row++){sum += a[row][col];}}7-13:尽量减少循环嵌套层次。7-14:避免循环体内含判断语句,应将循环语句置于判断语句的代码块之中。说明:目的是减少判断次数。循环体中的判断语句是否可以移到循环体外,要视程序的具体情况而言,一般情况,与循环变量无关的判断语句可以移到循环体外,而有关的则不可以。示例:如下代码效率稍低。for (ind =0; ind < MAX_RECT_NUMBER; ind++){if (data_type == RECT_AREA){area_sum += rect_area[ind];}}else{rect_length_sum += rect[ind].length;rect_width_sum += rect[ind].width;}因为判断语句与循环变量无关,故可如下改进,以减少判断次数。if(data_type == RECT_AREA){for (ind = 0; ind < MAX_RECT_NUMBER; ind++){area_sum += rect_area[ind];}}else{for (ind = 0; ind < MAX_RECT_NUMBER; ind++){rect_length_sum += rect[ind].length;rect_width_sum += rect[ind].width;}}7-15:尽量用乘法或其它方法代替除法,特别是浮点运算中的除法。说明:浮点运算除法要占用较多CPU资源。示例:如下表达式运算可能要占较多CPU 资源。#define PAI3.1416radius =circle_length / (2 * PAI);应如下把浮点除法改为浮点乘法。#definePAI_RECIPROCAL (1 / 3.1416 ) // 编译器编译时,将生成具体浮点数radius =circle_length * PAI_RECIPROCAL / 2;7-16:不要一味追求紧凑的代码。说明:因为紧凑的代码并不代表高效的机器码。8 质量保证8-1:在软件设计过程中构筑软件质量。8-2:代码质量保证优先原则(1)正确性,指程序要实现设计要求的功能。(2)稳定性、安全性,指程序稳定、可靠、安全。(3)可测试性,指程序要具有良好的可测试性。(4)规范/可读性,指程序书写风格、命名规则等要符合规范。(5)全局效率,指软件系统的整体效率。(6)局部效率,指某个模块/子模块/函数的本身效率。(7)个人表达方式/个人方便性,指个人编程习惯。8-3:只引用属于自己的存贮空间。说明:若模块封装的较好,那么一般不会发生非法引用他人的空间。8-4:防止引用已经释放的内存空间。说明:在实际编程过程中,稍不留心就会出现在一个模块中释放了某个内存块(如C语言指针),而另一模块在随后的某个时刻又使用了它。要防止这种情况发生。8-5:过程/函数中分配的内存,在过程/函数退出之前要释放。8-6:过程/函数中申请的(为打开文件而使用的)文件句柄,在过程/函数退出之前要关闭。说明:分配的内存不释放以及文件句柄不关闭,是较常见的错误,而且稍不注意就有可能发生。这类错误往往会引起很严重后果,且难以定位。示例:下函数在退出之前,没有把分配的内存释放。typedefunsigned char BYTE;intexample_fun( BYTE gt_len, BYTE *gt_code ){BYTE *gt_buf;gt_buf = (BYTE *) malloc (MAX_GT_LENGTH);... //program code, include check gt_buf if or not NULL./* global title length error */if (gt_len > MAX_GT_LENGTH){return GT_LENGTH_ERROR; // 忘了释放gt_buf}... // other program code}应改为如下。intexample_fun( BYTE gt_len, BYTE *gt_code ){BYTE *gt_buf;gt_buf = (BYTE * ) malloc ( MAX_GT_LENGTH );... // program code, include check gt_buf if or not NULL./* global title length error */if (gt_len > MAX_GT_LENGTH){free( gt_buf ); // 退出之前释放gt_bufreturn GT_LENGTH_ERROR;}... // other program code}8-7:防止内存操作越界。说明:内存操作主要是指对数组、指针、内存地址等的操作。内存操作越界是软件系统主要错误之一,后果往往非常严重,所以当我们进行这些操作时一定要仔细小心。示例:假设某软件系统最多可由10个用户同时使用,用户号为1-10,那么如下程序存在问题。#defineMAX_USR_NUM 10unsignedchar usr_login_flg[MAX_USR_NUM]= "";voidset_usr_login_flg( unsigned char usr_no ){if (!usr_login_flg[usr_no]){usr_login_flg[usr_no]= TRUE;}}当usr_no 为10 时,将使用usr_login_flg 越界。可采用如下方式解决。voidset_usr_login_flg( unsigned char usr_no ){if (!usr_login_flg[usr_no - 1]){usr_login_flg[usr_no - 1]= TRUE;}}8-8:认真处理程序所能遇到的各种出错情况。8-9:系统运行之初,要初始化有关变量及运行环境,防止未经初始化的变量被引用。8-10:系统运行之初,要对加载到系统中的数据进行一致性检查。说明:使用不一致的数据,容易使系统进入混乱状态和不可知状态。8-11:严禁随意更改其它模块或系统的有关设置和配置。说明:编程时,不能随心所欲地更改不属于自己模块的有关设置如常量、数组的大小等。8-12:不能随意改变与其它模块的接口。8-13:充分了解系统的接口之后,再使用系统提供的功能。示例:在B型机的各模块与操作系统的接口函数中,有一个要由各模块负责编写的初始化过程,此过程在软件系统加载完成后,由操作系统发送的初始化消息来调度。因此就涉及到初始化消息的类型与消息发送的顺序问题,特别是消息顺序,若没搞清楚就开始编程,很容易引起严重后果。以下示例引自B 型曾出现过的实际代码,其中使用了FID_FETCH_DATA与FID_INITIAL 初始化消息类型,注意B 型机的系统是在FID_FETCH_DATA 之前发送FID_INITIAL 的。MIDalarm_module_list[MAX_ALARM_MID];int FARSYS_ALARM_proc( FID function_id, int handle ){_UI i, j;switch ( function_id ){... // program codecase FID_INITAIL:for (i = 0; i < MAX_ALARM_MID; i++){if (alarm_module_list[i]== BAM_MODULE // **)|| (alarm_module_list[i]== LOCAL_MODULE){for (j = 0; j < ALARM_CLASS_SUM; j++){FAR_MALLOC( ... );}}}... // program codebreak;case FID_FETCH_DATA:... // program codeGet_Alarm_Module( ); // 初始化alarm_module_listbreak;... // program code}}由于FID_INITIAL 是在FID_FETCH_DATA 之前执行的,而初始化alarm_module_list 是在FID_FETCH_DATA 中进行的,故在FID_INITIAL 中(**)处引用alarm_module_list 变量时,它还没有被初始化。这是个严重错误。应如下改正:要么把Get_Alarm_Module 函数放在FID_INITIAL 中(**)之前;要么就必须考虑(**)处的判断语句是否可以用(不使用alarm_module_list 变量的)其它方式替代,或者是否可以取消此判断语句。8-14:编程时,要防止差1错误。说明:此类错误一般是由于把“<=”误写成“<”或“>=”误写成“>”等造成的,由此引起的后果,很多情况下是很严重的,所以编程时,一定要在这些地方小心。当编完程序后,应对这些操作符进行彻底检查。8-15:要时刻注意易混淆的操作符。当编完程序后,应从头至尾检查一遍这些操作符,以防止拼写错误。说明:形式相近的操作符最容易引起误用,如C/C++中的“=”与“==”、“|”与“||”、“&”与“&&”等,若拼写错了,编译器不一定能够检查出来。示例:如把“&”写成“&&”,或反之。ret_flg =(pmsg->ret_flg & RETURN_MASK);被写为:ret_flg =(pmsg->ret_flg && RETURN_MASK);rpt_flg =(VALID_TASK_NO( taskno ) && DATA_NOT_ZERO( stat_data ));被写为:rpt_flg =(VALID_TASK_NO( taskno ) & DATA_NOT_ZERO( stat_data ));8-16:有可能的话,if语句尽量加上else分支,对没有else分支的语句要小心对待;switch语句必须有default分支。8-17:Unix下,多线程的中的子线程退出必需采用主动退出方式,即子线程应return出口。8-18:不要滥用goto语句。说明:goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。8-19:精心地构造、划分子模块,并按“接口”部分及“内核”部分合理地组织子模块,以提高“内核”部分的可移植性和可重用性。说明:对不同产品中的某个功能相同的模块,若能做到其内核部分完全或基本一致,那么无论对产品的测试、维护,还是对以后产品的升级都会有很大帮助。8-20:精心构造算法,并对其性能、效率进行测试。8-21:对较关键的算法最好使用其它算法来确认。8-22:时刻注意表达式是否会上溢、下溢。示例:如下程序将造成变量下溢。unsignedchar size ;while(size-- >= 0) // 将出现下溢{...// program code}当size 等于0时,再减1不会小于0,而是0xFF,故程序是一个死循环。应如下修改。char size;// 从unsigned char 改为charwhile(size-- >= 0){...// program code}8-23:使用变量时要注意其边界值的情况。示例:如C 语言中字符型变量,有效值范围为-128到127。故以下表达式的计算存在一定风险。char chr =127;int sum =200;chr += 1; //127 为chr 的边界值,再加1 将使chr 上溢到-128,而不是128。sum += chr;// 故sum 的结果不是328,而是72。若chr 与sum 为同一种类型,或表达式按如下方式书写,可能会好些。sum = sum +chr + 1;8-24:留心程序机器码大小(如指令空间大小、数据空间大小、堆栈空间大小等)是否超出系统有关限制。8-25:为用户提供良好的接口界面,使用户能较充分地了解系统内部运行状态及有关系统出错情况。8-26:系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救。8-27:对一些具有危险性的操作代码(如写硬盘、删数据等)要仔细考虑,防止对数据、硬件等的安全构成危害,以提高系统的安全性。8-28:使用第三方提供的软件开发工具包或控件时,要注意以下几点:(1)充分了解应用接口、使用环境及使用时注意事项。(2)不能过分相信其正确性。(3)除非必要,不要使用不熟悉的第三方工具包与控件。说明:使用工具包与控件,可加快程序开发速度,节省时间,但使用之前一定对它有较充分的了解,同时第三方工具包与控件也有可能存在问题。8-29:资源文件(多语言版本支持),如果资源是对语言敏感的,应让该资源与源代码文件脱离,具体方法有下面几种:使用单独的资源文件、DLL 文件或其它单独的描述文件(如数据库格式)9 代码编辑、编译、审查9-1:打开编译器的所有告警开关对程序进行编译。9-2:在产品软件(项目组)中,要统一编译开关选项。9-3:通过代码走读及审查方式对代码进行检查。说明:代码走读主要是对程序的编程风格如注释、命名等以及编程时易出错的内容进行检查,可由开发人员自己或开发人员交叉的方式进行;代码审查主要是对程序实现的功能及程序的稳定性、安全性、可靠性等进行检查及评审,可通过自审、交叉审核或指定部门抽查等方式进行。9-4:测试部测试产品之前,应对代码进行抽查及评审。9-5:编写代码时要注意随时保存,并定期备份,防止由于断电、硬盘损坏等原因造成代码丢失。9-6:同产品软件(项目组)内,最好使用相同的编辑器,并使用相同的设置选项。说明:同一项目组最好采用相同的智能语言编辑器,如Muiti Editor,Visual Editor 等,并设计、使用一套缩进宏及注释宏等,将缩进等问题交由编辑器处理。9-7:合理地设计软件系统目录,方便开发人员使用。说明:方便、合理的软件系统目录,可提高工作效率。目录构造的原则是方便有关源程序的存储、查询、编译、链接等工作,同时目录中还应具有工作目录----所有的编译、链接等工作应在此目录中进行,工具目录----有关文件编辑器、文件查找等工具可存放在此目录中。9-8:某些语句经编译后产生告警,但如果你认为它是正确的,那么应通过某种手段去掉告警信息。说明:在Borland C/C++中,可用“#pragma warn”来关掉或打开某些告警。示例:#pragma warn-rvl // 关闭告警int examples_fun(void ){//程序,但无return 语句。}#pragma warn+rvl // 打开告警编译函数examples_fun 时本应产生“函数应有返回值”告警,但由于关掉了此告警信息显示,所以编译时将不会产生此告警提示。9-9:使用代码检查工具(如C 语言用PC-Lint)对源程序检查。10 代码测试、维护10-1:单元测试要求至少达到语句覆盖。10-2:单元测试开始要跟踪每一条语句,并观察数据流及变量的变化。10-3:清理、整理或优化后的代码要经过审查及测试。10-4:代码版本升级要经过严格测试。10-5:使用工具软件对代码版本进行维护。10-6:正式版本上软件的任何修改都应有详细的文档记录。10-7:发现错误立即修改,并且要记录下来。10-8:关键的代码在汇编级跟踪。10-9:仔细设计并分析测试用例,使测试用例覆盖尽可能多的情况,以提高测试用例的效率。10-11:尽可能模拟出程序的各种出错情况,对出错处理代码进行充分的测试。10-12:仔细测试代码处理数据、变量的边界情况。10-13:保留测试信息,以便分析、总结经验及进行更充分的测试。10-14:不应通过“试”来解决问题,应寻找问题的根本原因。10-15:对自动消失的错误进行分析,搞清楚错误是如何消失的。10-16:修改错误不仅要治表,更要治本。10-17:测试时应设法使很少发生的事件经常发生。10-18:明确模块或函数处理哪些事件,并使它们经常发生。10-19:坚持在编码阶段就对代码进行彻底的单元测试,不要等以后的测试工作来发现问题。10-20:去除代码运行的随机性(如去掉无用的数据、代码及尽可能防止并注意函数中的“内部寄存器”等),让函数运行的结果可预测,并使出现的错误可再现。11 宏11-1:用宏定义表达式时,要使用完备的括号。示例:如下定义的宏都存在一定的风险。#defineRECTANGLE_AREA( a, b ) a * b#defineRECTANGLE_AREA( a, b ) (a * b)#defineRECTANGLE_AREA( a, b ) (a) * (b)正确的定义应为:#defineRECTANGLE_AREA( a, b ) ((a) * (b))11-2:将宏所定义的多条表达式放在大括号中。示例:下面的语句只有宏的第一条表达式被执行。为了说明问题,for语句的书写稍不符规范。#defineINTI_RECT_VALUE( a, b )a = 0;b = 0;for (index =0; index < RECT_TOTAL_NUM; index++)INTI_RECT_VALUE(rect.a, rect.b );正确的用法应为:#defineINTI_RECT_VALUE( a, b ){a = 0;b = 0;}for (index =0; index < RECT_TOTAL_NUM; index++){INTI_RECT_VALUE(rect[index].a, rect[index].b );}11-3:使用宏时,不允许参数发生变化。示例:如下用法可能导致错误。#defineSQUARE( a ) ((a) * (a))int a = 5;int b;b = SQUARE(a++ ); // 结果:a = 7,即执行了两次增1。正确的用法是:b = SQUARE(a );a++; // 结果:a = 6,即只执行了一次增1。

    时间:2019-04-29 关键词: 内核 代码 坚果

  • MIPS 多线程内核显著提升LTE和LTE Advanced基带处理性能

    Imagination Technologies (IMG.L) 宣布,该公司的多线程 ®* 处理器内核能比单线程处理器在手机和平板电脑等用户设备上为LTE基带处理带来显著的性能效益。其新开发的 LTE 和 LTE 参考平台已实现优异结果,Imagination 在 2 月 25~28 日西班牙巴塞罗那举行的全球移动通信大会 (MWC) 上展示了这项成果。 采用拥有优异性能效率的 多线程内核,Imagination 和 SAI 在 SMP 上执行 SAI 的LTE 的软件栈时,达到了比单线程平台高 45% 的性能**。LTE 是 LTE 的演进版本,它可大幅提升网络容量与数据传输能力,为家庭和企业用户带来高带宽与丰富的多媒体服务。微微基站 (picocell)、微基站 (femtocell) 和先进手机等下一代系统的设计人员能通过-Based™ 的多线程,支持新的 LTE Advanced 第十版,包括载波聚合、、EMBMS 和 Voice-over-LTE (VoLTE)等,用来开发高性能、低功耗产品。 Imagination 同时与 SAI 和 Mentor 合作,为 4G LTE 提供集成的的多线程。此方案包括多线程 MIPS 处理器内核、广受欢迎的 Mentor Nucleus® 实时操作系统 以及 SAI的 LTE 的软件堆栈,可获得优化的多线程性能。在数据卡、智能手机、平板电脑和物联网等大量应用LTE 的系统中,MIPS-Based 多线程能帮助设计人员实现先进的实时性、低功耗和高性能。 这些LTE 和 LTE Advanced 参考平台充分显示了此解决方案在4G 系统开发中的效益与可行性。Imagination 经验证的多线程 MIPS 处理器包括广受欢迎的 MIPS® 34K™ 内核和最新一代的 interAptiv™ 多处理器,即日起已可提供授权。设计人员能通过运用多线程 MIPS 内核、SAI 经验证的 LTE 和 LTE Advanced 系统软件堆栈,以及业界标准 SMP 与 Mentor 的 Nucleus ,着手开发各种具备优异性能效率的 LTE 系统。业界也可从此解决方案的可扩展性中获益,能以通用的工具和架构来进行大跨度的移动和无线系统产品开发。 支持性引言 “我们一直了解硬件多线程技术可为移动和无线应用带来的独特效益。的确,这也是 MIPS 吸引Imagination 的原因之一。由于我们与 SAI 和 Mentor Graphics 的合作,我们现在可以展示此技术如何为LTE 系统带来竞争优势。这些解决方案是MIPS架构性能效率的充分展现。” —Imagination Technologies 营销执行副总裁 Tony King-Smith “嵌入式系统开发人员能通过结合 Imagination 的多线程 MIPS 处理器、SAI 的 LTE 协议栈、以及Mentor Graphics 的 Nucleus ,以更低成本显著提升系统性能。随着联网应用扩展到各种各样的日常设备,以及 LTE 成为许多嵌入式 IoT 设备的标准,我们看到了这一整合性解决方案的绝佳商机。运用 Nucleus,开发人员将能充分发挥业界最广泛部署的实时操作系统所提供的先进电源管理功能。” —Mentor Graphics 嵌入式解决方案总经理 Morrison “随着 厂商不断寻求能使产品差异化和快速上市的方法,我们很高兴能与 Imagination 和 Mentor Graphics 合作,为业界展示如何通过多线程技术的性能和效率效益为产品带来差异化特性,并同时提供经验证的参考平台,以协助客户快速开发下一代 LTE 系统。” —SAI 首席执行官 Venkat Rayapati 博士 关于 Mentor Nucleus Mentor Graphics 的嵌入式 Nucleus 产品已广泛获得超过 23 亿部移动电话的采用,是经验证、高效率和可靠的操作系统。Nucleus RTOS 可提供高性能,并最优化单操作系统或多操作系统平台的资源运用。采用高效率和电源管理架构设计,Nucleus RTOS 是内存资源有限的设备以及需要最大化每瓦周期以节省功耗的系统的理想选择。 关于 SAI 的 LTE 和 LTE Advanced 协议栈 SAI 通过小型基站验证与第三方 IOT 测试的 LTE UE 和 eNB 协定堆叠遵循最新的 LTE 和 LTE Advanced规范,并能根据 厂商的特定需求进行定制化设计。3GPP 支持堆叠可为第二层、第三层和应用层优化性能,以实现 至 Cat-4 和更高等级的上传与下载资料传输率。SAI LTE 协定堆叠架构的 CPU 利用率低且内存占用空间小,是专为模组、可扩展性和可移植到不同硬件平台、以及多种操作系统 (、Nucleus、Android和Vx Works) 所设计。SAI 可为客户提供开发 LTE 产品所需的移植、整合、IOT 和定制化设计服务。SAI 可提供 LTE UE 和 eNB 小型基站物理层 (PHY)、协议栈与无线电,以实现完整的系统产品开发。

    时间:2019-04-24 关键词: 内核 基带 性能 多线程 嵌入式开发

  • 从内核驱动到android app

    该文不得作为商业用途,仅为学习积累所用,转载请注明出处:http://blog.csdn.net/callon_h/article/details/51909169了解android驱动框架:1.方法1——jni调用底层驱动在android框架中写入c/c++直接调用底层linux驱动,并向上提供jni接口给应用程序:优点:简单易行;缺点:主要在于驱动程序,由于在linux中需要遵循GPL协议,需要开源,而许多厂商的一些代码不希望开源。2.方法2——增加硬件抽象层将驱动程序一分为二,一部分开源在内核中,一部分不开源在android框架中:led android驱动:从这里我们将看到整个应用框架层到底层驱动的走向。首先,无论是哪种方法,我们都需要实现一个linux驱动以供上层访问led资源。1.linux驱动:linux的字符设备驱动编写:#include#include#include#include#include#include#include#includestatic struct cdev dev; static dev_t dev_num; #define GPM4CON 0x110002E0 #define GPM4DAT 0X110002E4 #define LED_ON _IOW('G',0,int) #define LED_OFF _IOW('G',1,int) static unsigned int *led_con = NULL; static unsigned int *led_dat = NULL; static struct class *led_class = NULL; static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { case LED_ON: { writel(readl(led_dat)& ~(0x1<<arg), led_dat); break; } case LED_OFF: { writel(readl(led_dat)| (0x1<<arg), led_dat); break; } default: { return -EINVAL; break; } } return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .unlocked_ioctl = led_ioctl, }; static void hw_init()//GPM4_0-3 { //1.2.1 映射地址 led_con = ioremap(GPM4CON,4); led_dat = ioremap(GPM4DAT,4); //1.2.2 设置为输出状态 writel((readl(led_con)& ~0xffff) | 0x1111, led_con); //1.2.3 设置为高电平 writel(readl(led_dat)|0xf, led_dat); } static int led_init() {   //1.1 cdev字符设备初始化 //1.1.1 分配cdev结构(静态分配)  //1.1.2 初始化cdev结构 alloc_chrdev_region(&dev_num,0,1,"callon_led"); cdev_init(&dev, &led_fops); dev.owner = THIS_MODULE; //1.1.3 注册cdev结构  cdev_add(&dev,dev_num,1); //1.2 硬件初始化 hw_init(); //1.3 创建设备文件 //1.3.1 创建类 led_class = class_create(THIS_MODULE,"callon_led"); //1.3.2 创建设备 device_create(led_class,NULL,dev_num,NULL,"%s","callon_led"); printk("init led device is OK!n"); return 0; } static void led_exit() { device_destroy(led_class,dev_num); class_destroy(led_class); iounmap(led_dat); iounmap(led_con); cdev_del(&dev); unregister_chrdev_region(dev_num,1); } module_init(led_init); module_exit(led_exit);2 实现第一种方法:首先,说明一下为什么使用jni:1.基于对代码的保护,java相对容易被反编译,而c/c++库反汇编难度较大;2.可以方便地使用现存的开源库;3.提高执行效率;4.java在某些文件操作方面,找不到相关的API,如此处的ioctl.其次,为什么使用ndk?ndk提供了一系列的工具,能够帮助开发者快速开发c/c++的动态库,并能自动将.so动态库和java一起打包成apk.第一种方法的设计思路如下所示:在第一步中想要使用javah命令自动产生头文件需要先编写app程序,但是此时的app程序并不需要很完善,只需要提出一个你想要的native接口就好(但是你的android程序必须要Rebuild正确才会正确产生头文件):package com.led.ndk.example.callon.ndk_led; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; public class MainActivity extends Activity {     private CheckBox[] Led = new CheckBox[4];     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         Led[0] = (CheckBox)findViewById(R.id.checkbox_led1);         Led[1] = (CheckBox)findViewById(R.id.checkbox_led2);         Led[2] = (CheckBox)findViewById(R.id.checkbox_led3);         Led[3] = (CheckBox)findViewById(R.id.checkbox_led4);     }     private void SendCmd(View view)     {         for(int i =1; i<5; i++)         {             if(Led[i].isChecked())             {                 cmdLeds(1, i);             }             else             {                 cmdLeds(0, i);             }         }     }     public native void cmdLeds(int cmd, int arg);      }根据如上简单的app,使用命令:javah -d jni -classpath 你的sdk目录/platforms/你的平台名/android.jar:你的应用程序/app/build/intermediates/classes/debug/ 你的包名.主类名如:javah -d jni -classpath /opt/AndroidSDK/platforms/android-23/android.jar:/home/callon/Downloads/callon_ndk_led/ndk_led/app/build/intermediates/classes/debug/ com.led.ndk.example.callon.ndk_led.MainActivity此时自动产生出jni文件夹,其中包含头文件com_led_ndk_example_callon_ndk_led_MainActivity.h,头文件比较重要的:JNIEXPORT void JNICALL Java_com_led_ndk_example_callon_ndk_1led_MainActivity_cmdLeds   (JNIEnv *, jobject, jint, jint);这也就是我们编写源文件需要实现的方法名,下面直接编写源文件:#include "com_led_ndk_example_callon_ndk_led_MainActivity.h" #include#include#include#include#include#include#include#define LED_ON _IOW('G',0,int) #define LED_OFF _IOW('G',1,int) JNIEXPORT void JNICALL Java_com_led_ndk_example_callon_ndk_1led_MainActivity_cmdLeds   (JNIEnv * env, jobject thiz, jint cmd, jint arg) { int fd; int temp_cmd; fd = open("/dev/callon_led",O_WRONLY); if(cmd == 1) temp_cmd = LED_ON; else temp_cmd = LED_OFF; ioctl(fd, temp_cmd, arg); close(fd);    }就是一些对内核驱动文件的操作,然后编写Android.mk即makefile:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := callon_ndk_led LOCAL_SRC_FILES := ndk_led.c include $(BUILD_SHARED_LIBRARY)此时,在你的jni文件夹应该有这三个文件了,退到jni文件夹之外,使用命令ndk-build即可:callon@ubuntu:~/Downloads/callon_ndk_led/jni$ ls Android.mk  com_led_ndk_example_callon_ndk_led_MainActivity.h  ndk_led.c callon@ubuntu:~/Downloads/callon_ndk_led/jni$ cd .. callon@ubuntu:~/Downloads/callon_ndk_led$ ndk-build  [armeabi] Compile thumb  : callon_ndk_led  libs/armeabi/libcallon_ndk_led.so callon@ubuntu:~/Downloads/callon_ndk_led$最后一步,完善app:首先,用Project形式来看我们的app,并在app->src->main下创建一个目录"jniLibs";然后,将我们.so所在的armeabi目录拷贝到jniLibs目录下;最后在我们的app代码的最后加上:static     {         System.loadLibrary("callon_ndk_led");     }然后Rebuild Project即可!3 实现第二种方法:1.HAL程序编写:首先在  安卓源代码根目录/hardware/libhardware/modules/下创建自己的hal代码存放路径,如led。最终编写的文件为:安卓源代码根目录/hardware/libhardware/modules/led/led_hal.c和Android.mk,安卓源代码根目录/hardware/libhardware/include/hardware/led.h。#include#include#include#include#include#include#include#include#include#include#define LOG_TAG "callon_led" static int fd; static int led_close(struct hw_device_t *device) { struct led_device_t* led = (struct led_device_t*)device; free(led); close(fd); return 0; } int led_on(struct led_device_t* dev,int arg) { ioctl(fd,LED_ON,arg); return 0; } int led_off(struct led_device_t* dev,int arg) { ioctl(fd,LED_OFF,arg); return 0; } static struct led_device_t led_dev = { .led_device = { .tag = HARDWARE_DEVICE_TAG, .close = led_close, }, .set_on = led_on, .set_off = led_off, }; static int open_led(const struct hw_module_t* module, char const* name,         struct hw_device_t** device) { *device = &led_dev; fd = open("/dev/callon_led",O_RDWR); if(fd < 0) { ALOGD(LOG_TAG, "open device fail!"); return -1; } return 0; } static struct hw_module_methods_t led_methods = {     .open =  open_led, }; struct hw_module_t HAL_MODULE_INFO_SYM = {     .tag = HARDWARE_MODULE_TAG,     .id = "led",     .methods = &led_methods, };led.h#ifndef _HARDWARE_LED_H #define _HARDWARE_LED_H #include#define LED_ON _IOW('G',0,int) #define LED_OFF _IOW('G',1,int) struct led_device_t { struct hw_device_t led_device; int (*set_on)(struct led_device_t* dev,int arg);//means led_number int (*set_off)(struct led_device_t* dev,int arg); }; #endif  // _HARDWARE_LED_HAndroid.mkLOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := led.default # HAL module implementation stored in # hw/.default.so LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_C_INCLUDES := hardware/libhardware LOCAL_SRC_FILES := led_hal.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := eng include $(BUILD_SHARED_LIBRARY)编译则在  安卓源代码根目录下使用$ . setenv $ lunch $ mmm hardware/libhardware/modules/led/看到target SharedLib: led.default (out/target/product/tiny4412/obj/SHARED_LIBRARIES/led.default_intermediates/LINKED/led.default.so) target Symbolic: led.default (out/target/product/tiny4412/symbols/system/lib/hw/led.default.so) Export includes file: hardware/libhardware/modules/led/Android.mk -- out/target/product/tiny4412/obj/SHARED_LIBRARIES/led.default_intermediates/export_includes target Strip: led.default (out/target/product/tiny4412/obj/lib/led.default.so) Install: out/target/product/tiny4412/system/lib/hw/led.default.so make: Leaving directory `/opt/Tiny4412/Android/android-5.0.2' #### make completed successfully (1 seconds) ####即可,最后产生的就在out/target/product/tiny4412/system/lib/hw/led.default.so了。我们将system.img重做,这里通过一个脚本./gen-img.sh完成。2.硬件服务编写:首先,Service Manager为了解决访问冲突而存在的,在有Service Manager的基础之上,我们的底层需要先编写硬件服务,注册到Service Manager,我们的app才能通过Service Manager获取到服务,操作底层。由于涉及到许多目录操作,细化操作后为:1.创建 ILedService.aidl仿照 frameworks/base/core/java/android/os/IVibratorService.aidl package android.os; /** {@hide} */ interface ILedService {     int LedOpen();     int LedOn(int arg);     int LedOff(int arg); }2.修改frameworks/base/Android.mk 增加core/java/android/os/ILedService.aidl 3.自动生成ILedService.javammm frameworks/base/编译自动生成out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java4.创建 LedService.java 实现接口函数仿照frameworks/base/services/core/java/com/android/server/VibratorService.java package com.android.server; import android.util.Slog; import android.os.ILedService; public class LedService extends ILedService.Stub{     private static final String TAG = "LedService";      public LedService()  { Slog.d(TAG,"LedService"); }      public int LedOpen() throws android.os.RemoteException { return native_LedOpen(); }      public int LedOn(int arg) throws android.os.RemoteException { return native_LedOn(arg); } public int LedOff(int arg) throws android.os.RemoteException { return native_LedOff(arg); }                public static native int native_LedOpen(); public static native int native_LedOn(int arg); public static native int native_LedOff(int arg); }5.将服务注册到Service Manager当中 修改frameworks/base/services/java/com/android/server/SystemServer.java参考vibrator修改的地方LedService led = null;Slog.i(TAG, "Led Service");             led = new LedService();             ServiceManager.addService("led", led);6.实现com_android_server_LedService.cpp为什么需要它?因为我们的hal代码并没有提供jni的接口(这里提供了一种新的方法来提供native方法,之前是自动产生头文件然后来实现的)。根据frameworks/base/services/core/jni/com_android_server_VibratorService.cpp/*  * Copyright (C) 2009 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ #define LOG_TAG "LedService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include#include#include#includestruct led_device_t *led_dev; namespace android { static jint LedOpen(JNIEnv *env, jobject clazz) { hw_module_t *module; hw_device_t *device; hw_get_module("led",(hw_module_t const **)&module); module->methods->open(module, NULL, &device); led_dev = (struct led_device_t*)device; return 0; } static jint LedOn(JNIEnv *env, jobject clazz, int arg) { led_dev->set_on(led_dev,arg); return 0; } static jint LedOff(JNIEnv *env, jobject clazz, int arg) {      led_dev->set_off(led_dev,arg); return 0; } static JNINativeMethod method_table[] = {     { "native_LedOpen", "()I", (void*)LedOpen },     { "native_LedOn", "(I)I", (void*)LedOn },     { "native_LedOff", "(I)I", (void*)LedOff} }; int register_android_server_LedService(JNIEnv *env) {     return jniRegisterNativeMethods(env, "com/android/server/LedService",             method_table, NELEM(method_table)); } };7.注册native接口在frameworks/base/services/core/jni/onload.cpp中添加int register_android_server_LedService(JNIEnv* env);register_android_server_LedService(env);8.修改Android.mk在frameworks/base/services/core/jni/Android.mk中加入自己写的com_android_server_LedService.cpp$(LOCAL_REL_DIR)/com_android_server_LedService.cpp 9.mmm frameworks/base/services/编译Copying: out/target/common/obj/JAVA_LIBRARIES/services_intermediates/classes.dex target Jar: services (out/target/common/obj/JAVA_LIBRARIES/services_intermediates/javalib.jar) Install: out/target/product/tiny4412/system/framework/services.jar make: Leaving directory `/opt/Tiny4412/Android/android-5.0.2' #### make completed successfully (50 seconds) ####其中碰到一阵错误,非常无语:make: Entering directory `/opt/Tiny4412/Android/android-5.0.2' make: *** No rule to make target `frameworks/base/services/core/.java', needed by `out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes-full-debug.jar'.  Stop. make: Leaving directory `/opt/Tiny4412/Android/android-5.0.2' #### make failed to build some targets (1 seconds) ####直接不往下编译,直接报错,非常难找错误,最后发现由于LedService.java的文件名多了个空格。出现这种错误一定要耐心,一定是某个小地方出错的。最后继续使用./gen-img.sh完成system.img的编译,烧写进开发板,为写应用程序做准备。3.应用程序设计:package com.led.hal.example.callon.callonhalled; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; import android.os.ILedService; import android.os.ServiceManager; public class MainActivity extends AppCompatActivity {     private CheckBox[] Led = new CheckBox[4];     private ILedService iLedService = null;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         Led[0] = (CheckBox)findViewById(R.id.checkbox_led1);         Led[1] = (CheckBox)findViewById(R.id.checkbox_led2);         Led[2] = (CheckBox)findViewById(R.id.checkbox_led3);         Led[3] = (CheckBox)findViewById(R.id.checkbox_led4);         iLedService = ILedService.Stub.asInterface(ServiceManager.getService("led"));         try {             iLedService.LedOpen();         }catch(RemoteException e){             e.printStackTrace();         }     }     private void SendCmd(View view)     {         for(int i =1; i<5; i++)         {             if(Led[i].isChecked()) {                 try {                     iLedService.LedOn(i);                 }catch (RemoteException e){                     e.printStackTrace();                 }             }             else {                 try {                     iLedService.LedOff(i);                 }catch (RemoteException e){                     e.printStackTrace();                 }             }         }     } }程序其实很简单,但是涉及到android 中隐藏类的使用,我们的ILedService和ServiceManager其实都是隐藏类,你编译不过去的,添加相应的jar包才可以,参考http://blog.csdn.net/wukunting/article/details/5788196首先拷贝out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar到工程下然后AndroidStudio中File->Project Structure点击其中的'+'->选择Import .JAR/.AAR Package->选择classes.jar->Finish继续在Project Structure下选择app->Dependencies->选择'+'->Module Dependency->选择'classes'即可。这时候整个源码都好了,但是下载速度会很慢,改善基于frameworks设计的app下载和运行速度的方案:1.修改build.gradle(Module:app)apply plugin: 'com.android.application' android {     compileSdkVersion 23     buildToolsVersion "23.0.2"     defaultConfig {         applicationId "com.led.hal.example.callon.callonhalled"         minSdkVersion 21         targetSdkVersion 23         versionCode 1         versionName "1.0"         multiDexEnabled true     }     dexOptions {         javaMaxHeapSize "4g"     }     buildTypes {         release {             minifyEnabled false             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'         }     } } dependencies {     compile fileTree(dir: 'libs', include: ['*.jar'])     testCompile 'junit:junit:4.12'     compile 'com.android.support:appcompat-v7:23.2.0'     compile project(':classes')     compile 'com.android.support:multidex:1.0.0' }2.修改AndroidManifest.xml在application下增加android:name="android.support.multidex.MultiDexApplication"最后下载运行吧!

    时间:2019-04-17 关键词: 内核 Android android驱动

首页  上一页  1 2 3 4 5 6 7 8 9 10 下一页 尾页
发布文章

技术子站

更多

项目外包