前段时间有朋友想要了解一下多线程编程,正好有个项目上有这么个例子可以抽出来讲一讲。只要搞清楚这个例子,就一下子掌握了线程间同步、互斥量、条件变量、队列、内存池的概念和使用。
首先,线程间同步的概念。
比如,学过数字电路的人都知道,两个时钟域的信号如果没有经过同步直接接到一起的话,会引起亚稳态。原因是如果恰好输入信号在时钟边沿附近变化的话(不满足建立保持时间的情况下),信号可能处于一个中间电平,这样会导致触发器处于一个振荡状态,引起整块数字电路的不稳定。这就是数字电路中异步的概念,两个时钟都是各自free running,彼此没有关系。
再比如单片机程序中,各个不同的中断程序或者跟主程序间是异步的,因为主程序在执行的过程中随时可能被进来的中断打断,如果中断和主程序之间要通过一个共享的变量传递数据,你就要注意这个共享的变量的保护。假如主程序只读取了一半的数据而被中断打断,然后中断程序中又更新了整个变量,这样的回到主程序继续执行时读到的数据就有一半是上一次的,一半是更新过的。这样的结果显然不是我们想要的。这里只是举了一个很明显的例子。更多的情况可以搜索一下“原子操作”。
所以在多线程环境下,我们就要注意线程间共享变量的保护,这块敏感区域叫临界区(Critical area)。在单片机中,我们用中断开关来保护共享变量读写操作的完整性。在操作系统中,我们用的是互斥锁(mutex)来占有这个变量,防止它被多个线程同时访问。当一个线程访问当前已经被另一个线程占有的变量时,就会进入阻塞态,直到另一个线程完成解锁操作后,这个线程将得到继续执行。
互斥锁(mutex)是多线程编程时最重要的一个工具,用来解决多线程竞争同个资源的问题。其最底层的实现都是一个原子操作来界定lock or unlock。
接下来的例子创建了两个线程,一个是producer, 另一个是cusumer, 它们两个是异步的,中间通过一个队列来通讯。producer 向队列中发送数据,cusumer读取数据。模拟了一个场景:producer 以较快的速度向队列写数据,cusumer 处理数据较慢。这在图像帧处理时经常会碰到CPU处理和发送数据较慢,而外设采集速度较快的情况,这样多余的帧将被丢弃。队列节点使用自己写的一个内存池来分配,在malloc_node 从内存池(free_queue)里取出node; release_node 时把节点放回资源池。当对free_queue 进行操作的时候都要加锁,因为malloc_node 和release_node 可能被不同的线程调用,必须对free_queue 进行保护。这样的函数称之为是线程安全的。同理对enqueue,dequeue的操作也要对队列进行保护。
然后使用条件变量来通知consumer 队列有新数据到来。条件变量同样是被多个线程调用,也是需要带一个mutex 来进行保护的。当条件不满足时,线程会解锁mutex 进入block状态等待消息,这样才不会一直占有CPU。当条件满足或者超时时,才继续执行下面的程序。
例子中使用了pthread(POSIX thread) 的实现。其实各大操作系统都有自己的实现,FreeRTOS, Linux kernel等等,都可以拿代码过来看看学习。
请看这个多线程的例子,可以在online gdb 中运行调试:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/time.h>
#include <errno.h>
/\*\*\*\*\*\*\*\*\*\*\*\* queue manage \*\*\*\*\*\*\*\*\*\*\*/
typedef struct Node
{
void \*data;
struct Node \*next;
}queue\_node\_t;
#define BUFFER\_POOL\_SIZE (640 \* 480)
#define BUFFER\_POOL\_NUM 5
struct pbuf{
uint32\_t len;
uint8\_t payload\[BUFFER\_POOL\_SIZE\];
};
typedef struct QueueList
{
int sizeOfQueue;
uint16\_t memSize;
queue\_node\_t \*head;
queue\_node\_t \*tail;
}queue\_t;
/\* to inform consumer\_thread \*/
static pthread\_cond\_t cap\_cond;
static pthread\_mutex\_t cap\_mutex;
/\* stream queue for communicate between two threads \*/
static queue\_t strm\_queue;
static pthread\_mutex\_t strmq\_mutex;
int strm\_queue\_init(){
queue\_t \*q = &strm\_queue;
q->sizeOfQueue = 0;
q->memSize = 0;
q->head = q->tail = NULL;
if (pthread\_cond\_init(&cap\_cond, NULL) !
• Elements of a video Sequence
o Frames
o Slices
o MBs (macroblocks)
• Frame Types
o I-, P-, B-frames
o GOP (group of picture), specifies the order in which intra- and inter-frames are arranged.
o NAL (Network Abstraction Layer)
v SPS (Sequence parameter set)
v PPS (Picture parameter set)
v IDR (Instantaneous Decoder Refresh), every IDR frame is an I-frame, but not vice versa,
• Coding Tools
o Entropy Coding
中心服务方式选择 git 本身是一个分布式的版本管理系统,但如果要设置一个中心库方便很多开发者同步,或者像SVN 一样使用它,就需要搭建一个中心库。有几种方式可以选择:
gitosis :
这个是比较老的方式。不推荐
详情参考: https://git-scm.com/book/en/v1/Git-on-the-Server-Gitosis
GitLab: git 结合web 服务来管理,方便issue 和权限管理。比较推荐。收费版还可提供更多功能。参考:https://about.gitlab.com/install/
只用ssh git 用户管理
开一个git 用户,设定好权限,也比较方便。但是缺少管理issue 功能。
参考:https://git-scm.com/book/en/v2/Git-on-the-Server-Setting-Up-the-Server
ssh git用户设置 ssh key证书生成 $ ssh-keygen –t rsa –C “user@host”
将id_rsa.pub 发给git服务器管理员添加进/home/git/.ssh/authorized_keys 即可,或者直接用 ssh-copy-id 到服务器即可。
$ git clone git@IP:/srv/git/test.git
有几个注意点:
使用 ssh key 登陆 git 用户时,home目录只能是git 可写的,否则当git组包含多个用户时会出现不用能ssh key 登录的问题。具体debug ssh key 登录问题,可以查看 /var/log/auth 下的日志来解决。.ssh/ 的权限是700,.ssh/authorized_keys 权限是600。 最后用chsh 修改 git用户shell 为 git-shell,不让git 用户有其它多余的权限。 创建仓库可以用git 组的其它用户来创建。注意使用newgrp 将创建文件夹时用户的默认组改成git, 这样整个git 组的用户都有读写权限。 一些实用的git 命令记录 git checkout --patch BRANCH FILE git checkout --theirs PATH/FILE
git clean -n //演习
git ls-files
git ls-files | xargs -n 1 dirname | uniq
git diff master sync --name-only
git config --global core.
一、嵌入式系统的分层与专业的分类。 嵌入式系统分为4层,硬件层、驱动层、操作系统层和应用层。 1、硬件层,是整个嵌入式系统的根本,如果现在单片机及接口这块很熟悉,并且能用C和汇编语言来编程的话,从嵌入式系统的硬件层走起来相对容易,硬件 层也是驱动层的基础,一个优秀的驱动工程师是要能够看懂硬件的电路图和自行完成CPLD的逻辑设计的,同时还要对操作系统内核及其调度性相当的熟悉的。但 硬件平台是基础,增值还要靠软件。 硬件层比较适合于,电子、通信、自动化、机电一体、信息工程类专业的人来搞,需要掌握的专业基础知识有,单片机原理及接口技术、微机原理及接口技术、C语言。 2、 驱动层,这部分比较难,驱动工程师不仅要能看懂电路图还要能对操作系统内核十分的精通,以便其所写的驱动程序在系统调用时,不会独占操作系统时间片,而导 至其它任务不能动行,不懂操作系统内核架构和实时调度性,没有良好的驱动编写风格,按大多数书上所说添加的驱动的方式,很多人都能做到,但可能连个初级的 驱动工程师的水平都达不到,这样所写的驱动在应用调用时就如同windows下我们打开一个程序运行后,再打开一个程序时,要不就是中断以前的程序,要不 就是等上一会才能运行后来打开的程序。想做个好的驱动人员没有三、四年功底,操作系统内核不研究上几编,不是太容易成功的,但其工资在嵌入式系统四层中可 是最高的。 驱动层比较适合于电子、通信、自动化、机电一体、信息工程类专业尤其是计算机偏体系结构类专业的人来搞,除硬件层所具备的基础学科外,还要对数据结构与算法、操作系统原理、编译原理都要十分精通了解。 3、操作系统层,对于操作系统层目前可能只能说是简单的移植,而很少有人来自已写操作系统,或者写出缺胳膊少腿的操作系统来,这部分工作大都由驱动工 程师来完成。操作系统是负责系统任务的调试、磁盘和文件的管理,而嵌入式系统的实时性十分重要。据说,XP操作系统是微软投入300人用两年时间才搞定 的,总时工时是600人年,中科院软件所自己的女娲Hopen操作系统估计也得花遇几百人年才能搞定。因此这部分工作相对来讲没有太大意义。 4、应用层,相对来讲较为容易的,如果会在windows下如何进行编程接口函数调用,到操作系统下只是编译和开发环 境有相应的变化而已。如果涉及Jave方面的编程也是如此的。嵌入式系统中涉及算法的由专业算法的人来处理的,不必归结到嵌入式系统范畴内。但如果涉及嵌 入式系统下面嵌入式数据库、基于嵌入式系统的网络编程和基于某此应用层面的协议应用开发(比如基于SIP、H.323、Astrisk)方面又较为复杂, 并且有难度了。
从开始用freescale 的CodeWarrior起到这个ADS V1.2 ,一直认为这个开发环境做得不好。有几个问题。 1.当make完之后在xx.o文件报错有不明物体,双击这条error, 它没帮你定位到xx.c里。以至于你还得打开xx.c CTRL+F,找出这个不明物体。这很烦,有时候会觉得这个编译器很无语。 2.ADS V1.2的工程管理不能靠边?dock,至今没找到这个功能。。以至于窗口管理乱糟糟的。
这里使用的是TQ2440开发板,CPU 一般运行在400M的时钟频率。核心板载有两片Hynix(海力士,又叫现代内存)256Mb的SDRAM合64MB的内存,256MB SAMSUNG 的Nand flash,2MB 的Eon(宜扬) 的Nor flash。 首先,Nor flash 和 Nand flash 的区别? Nor flash的有自己的地址线和数据线,操作以字为单位,可以采用类似于memory的随机访问方式,在nor flash上可以直接运行程序,所以nor flash可以直接用来做boot,采用nor flash启动的时候会把地址映射到0x00上。类似于计算机内BIOS的存放位置。 Nand flash是IO设备,类似于计算机的硬盘,数据、地址、控制线都是共用的,需要软件区控制读取时序,更**无法挂在ARM的程序空间,**所以不能像nor flash、内存一样随机访问,不能EIP(片上运行),因此不能直接作为boot。一般Nand flash 用来存储系统程序,系统上电后由Nor flash中的BootLoader将系统加载进RAM中的可执行地址中运行,然后跳到主程序中运行。(这有点像冯.诺伊曼架构,程序和数据都在RAM中)。Nand flash在读取数据量较大的成块数据时,速度较快。