git reset
reset既可以处理commit后的回退,还可以处理add后的返回
状态机设计
controller类负责对外的接口,worker类负责实际干活,这两个是没有什么疑问的。
重要的是stateMachine和各个子state的区别,statemachine正常情况下是只做命令透传给当前的子state,而不应该做状态切换,状态切换应该由各个子state去做。
但是,有的情况下,状态的切换不是由controller触发的,比如当installing进度到达100时,应该切换到installsuccess的状态,这个状态的切换就比较有意思了。应该是由installing状态返回getProcess方法进度值100,但是不做切换动作,然后由statemachine类判断进度值,statemachine也不直接切状态,而是由statemachine去触发一个installsucced的方法,交由当前的状态去响应这个方法,而installing对installsucceed方法的响应就是切状态。
只有这样,你才能保证,在installing状态能自发切installsuccess状态,且能不断多次调用getProgress,同时在installsuccess状态能响应getProgress指令,同时所有的状态切换都是由子state完成。同时保证只有切状态的指令如installsucceed会触发切换状态,而子state的getProgress本身不会切换状态。
openssl命令
P7B证书:
der转pem: openssl pkcs7 -inform der -in a.p7b -out a.cer
读取证书pem里的信息: openssl pkcs7 -in 03user.p7b -print_certs -print -text
把p7b的pem的的证书链单独搞出来到单独的cert的pem里: openssl pkcs7 -inform DER -print_certs -in <path of the file> | awk 'split_after==1{n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > "cert" n ".pem"}'
ED25519加密
参考https://blog.pinterjann.is/ed25519-certificates.html
生成秘钥:openssl genpkey -algorithm ED25519 > 03.pem
手动构造一个配置文件25519cnf:
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = DE
CN = www.example.com
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
生成申请证书用的requset: openssl req -new -out exa.csr -key 03.pem -config 25519.cnf
查看request:openssl req -in exa.csr -text -noout
根据requset查看公钥(后面可以在crt里看公钥更好):openssl req -in exa.csr -pubkey
根据request和签发用的私钥生成证书(此处使用自签名):openssl x509 -req -days 700 -in exa.csr -signkey 03.pem -out exa.crt
读取证书信息:openssl x509 -text -in exa.crt
快速启动一个文件服务器
SimpleHTTPServer是python自带的模块,可以快速启动一个能够在网页上访问的文件服务器:
linux: python -m SimpleHTTPServer 8081
,
Windows: python -m http.server 8081
,
查看共享内存的方式
看句柄:lsof -p $(pidof com.huawei.updateagent) |grep shmem
看内存信息:dumpsys meminfo com.huawei.ivi.hmi.launcher
看文件句柄: ls -al /proc/$(pidof com.huawei.updateagent)/fd |grep ashmem
看资源限制: cat /proc/$(pidof com.huawei.updateagent)/limits
java和CPP的对比
数据类型:
长度 | JAVA | CPP |
---|---|---|
不定 | boolean(java中的boolean不是1个bit,而是1个或者4个字节,根据JVM的实现而不同) | bool |
8 | byte | char / signed char / unsigned char(cpp中的char是个坑,char不像short、int、long那样默认就是signed,而是根据编译器不同而不同,最好是手动制定signed或者unsigned,而且这里的char是8位的。不过字符字面量倒是既可以转化为unsigned char,也可以转化为signed char,因为ascii码只有128个) |
16 | char(java中的char是16位,而且可以表达utf) | |
16 | short | short / unsigned char |
32 | int | int / unsigned int |
64 | long | long long/ long / unsigned long (cpp的long/unsigned long也是坑,是不可移植的,在不同系统可能不一样,可能是32,也可能是64, 现今一般把long long当做是64位) |
32 | float | float |
64 | double | double |
常用接口对比
String: (cpp的命名普遍简短一点)
JAVA | CPP |
---|---|
char a = s.charAt(0) | char a = s.at(0) / s[0] |
String sub = s.substring(6, 11) (6是左闭,11是右开) | std::string sub = s.substr(6, 5) (6是左闭,5是长度) |
boolean out = s.equals(b) | bool out = (s.compare(b) == 0) |
其他两者一样 |
迭代器:
JAVA | CPP |
---|---|
Iterator<String> it = l.iterator(); | std::vector< std::string >::iterator& it = l.begin(); (一般直接用auto方便, 迭代器可以是引用也可以不是引用) |
it.hasNext(); | it != l.end(); |
String s = it.next(); (JAVA的迭代器的起始是从-1位置开始,所以取值是直接取下一个的值,既拆解成为原子操作的话,是先+1再取值) | std::string s = *it; it++; (cpp的迭代器的起始是从0位置开始,所以先解引用,再+1) |
其他两者一样 |
数组:
JAVA | CPP |
---|---|
int[][] arr = new int[n][m]; | int arr[n][m]; / int **arr = new int[n][m]; |
arr[a][b]; | arr[a][b]; / arr[a*m + b] (cpp的数组内存是连续分配的,所以不允许相同层次有不同大小,但这样也允许了通过单层数组的指针来指向多维数组的值) |
枚举:此处使用的枚举是c++11后的强类型枚举,更加规范
JAVA | CPP |
---|---|
enum Color {RED, GREEN} | enum class Color {RED, GREEN} |
Color a = Color.RED; | Color a = Color::RED; |
栈
JAVA | CPP |
---|---|
Stack<Integer> s = new Stack<>(); | std::stack<int> s; |
Integer peek = s.peek(); | int& top = s.top(); / int top = s.top(); (注意,获取的正常返回是引用,但是我们可以用一个非引用来接,这样就可以避免对容器中数据的修改。后面同理。) |
int size = s.size();(JAVA里各种数据类型的size一般都是int) | auto size = s.size(); (注意,一般size的数据类型定义都是宏size_type,所以我们用auto比较稳) |
boolean empty = s.isEmpty(); | bool empty = s.empty(); |
Integer a = s.pop(); | int top = s.top(); s.pop(); (cpp的pop是没有返回值的,这是因为cpp里从异常处理的角度出发,防止在从容器中删除再拷贝时,如果中间出了错,会导致值丢失,所以让你在pop之前,要自己先用top或者front读一下值) |
队列: 尽量还是用双头链表吧
JAVA | CPP |
---|---|
Queue<String> q = new LinkedList<>(); | std::queue<std::string> q; |
q.add(“a”); | s.push(“a”) (cpp的队列和栈的操作英文都是pop和push,但是读值分别是top和front) |
boolean out = q.isEmpty(); | bool out = q.empty(); |
String a = q.poll(); | std::string a = q.front(); q.pop(); (注意,cpp里的pop没有返回值) |
String a = q.peek(); | std::string a = q.front(); |
动态数组
JAVA | CPP |
---|---|
ArrayList<String> arr = new ArrayList<>(); | std::vector<std::string> vec; |
arr.add(“a”); | vec.push_back(“a”); |
arr.add(1, “b”); | vec.insert(vec.begin() + 1, “b”); |
boolean out = arr.isEmpty(); | bool out = arr.empty(); |
String a = arr.get(1); | std::string a = vec.at(1); / std::string a = vec[1]; |
arr.addAll(new ArrayList<>()); | vec.insert(vec.end(), b.begin(), b.end()); |
arr.remove(“a”); / arr.remove(1); | vec.erase(vec.begin() + 1); (不能根据元素的内容进行删除。) |
arr.sort((String a2, String b2) -> { return b2.compareTo(a2); }); / arr.sort((a2, b2) -> { return b2.compareTo(a2); }); / arr.sort(Comparator.reverseOrder()); / Collections.sort(arr, Comparator.reverseOrder()); | bool myFun(int a, int b) {return a < b;}; std::sort(vec.begin(); vec.end(); myFun); (cpp中可以只排序一个容器中的部分元素,所以是传入迭代器) |
arr.set(0, “aa”); | vec[0] = “aa”; / vec.at(0) = “aa”; (因为at返回的是引用,也可以作为左值,但是要注意,要是没有add就直接设值,不会生效。) |
双头链表
JAVA | CPP |
---|---|
LinkedList<String> l = new LinkedList<String>(); | std::deque<std::string> d; |
l.addFirst(“a”); | d.push_front(“a”); |
l.addLast(“a”); | d.push_back(“a”); |
String s = l.getFirst(); | std::string s = d.front(); |
String s = l.getLast(); | std::string s =d.back(); |
String s = l.removeFirst(); | std::string s = d.front(); d.pop_front(“a”); |
String s = l.removeLast(); | std::string s =d.back(); d.pop_back(“a”); |
Set
JAVA | CPP |
---|---|
HashSet<String> s = new HashSet<>(); | std::set<std::string> s; |
s.add(“a”); | s.insert(“a”) |
boolean out = s.isEmpty(); | bool out = s.empty(); |
s.remove(“a”); | s.erase(s.find(“a”)) (cpp的erase只能处理迭代器) |
s.contains(“a”); | bool out = (s.count(“a”) == 1) |
Map(CPP中的hashmap是unordered_map,正常的map是有序的使用红黑树实现的)
JAVA | CPP |
---|---|
HashMap<String, String> m = new HashMap<>(); | std::map<std::string, std::string> m; |
m.put(“a”, “b”); | m.insert(std::pair<std::string, std::string>(“a”, “b”)); / m[“a”] = “b”; (注意:1、不可以用m.insert(“a”, “b”);2、用insert插入时,如果key已经被占用了,则不会覆盖原值,但是用[]会覆盖原值;3、虽然cpp的map是有序的,但是m[3]这种下标取值,[]中间的只能是key值类型,而不会是插入的顺序,插入顺序只能通过迭代器(map.begin() + n)这种相对方式来获取) |
m.get(“a”); | std::string& s = m[“a”]; / std::string s = m[“a”] (对于下标取值,可以是引用也可以拷贝,所有的容器都是这样,后面就不再复述了。) |
m.remove(“a”); | m.erase(m.find(“a”)); |
boolean out = m.containsKey(“a”); | bool out = (m.count(“a”) == 1) |
Set<String> k = m.keySet(); | |
Collections<String> v = m.values(); | |
Set<Map.Entry<String, String>> e = m.entrySet(); | 只能通过增强for循环,直接获取到std::pair<std::string, std::string> |
优先队列
JAVA | CPP |
---|---|
PriorityQueue<String> p = new PriorityQueue<>(); / PriorityQueue<String> p = new PriorityQueue<>(Comparator.reverseOrder()); (JAVA的堆默认是最小堆,cpp默认是最大堆) | std::priority_queue<std::string> p; |
p.add(“a”) | p.push(“a”) |
String s = p.peek(); | std::string s = p.top(); |
String s = p.poll(); | std::string s = p.top(); p.pop(); |
总结:
JAVA:判空之所以都是用isEmpty(),是因为这个是Collections容器的方法,同理还有:size、contains、add、remove;map因为不是继承collection接口,所以不用add而是用put来增加元素;栈、队列、优先队列是因为本身有各自特有的命名方式,所以没有用collection的通用名称。
CPP:cpp的容器类之间没有继承上的关系,主要还是靠设计语言的统一。
接口设计
接口设计时,没有一个固定结构的接口设计规范,但是,一定要考虑你的接口能返回所有的结果可能。
最常用的,你的接口要保证你能够返回正常情况值和异常情况值。
比如如果你当前层是做数据透传的,从底层获取一个byte数组,然后返回这个byte数组给上层,如果你跟上层定的接口就是直接返回这个byte的话,就会有问题,因为你处理不了你从底层获取不到数据时,怎么给上层返回。所以一个合理的设计,是你返回一个结构体,结构体有两个字段,一个状态码,另一个是你透传的byte数组。
再比如你是生产数据的,然后你返回给上层的本来就是一个结构体,这个时候我们看到很多设计上是没有返回状态码的。那是因为你当报错时,你可能直接就给上层抛出一个Exception来表达异常,或者直接返回一个null,如果没有这些设计的话,也得在结构体里设计一个状态码。
再举一个C语言的例子,mkdir函数,在正常的返回值外,还会通过修改errno这个全局变量来表述错误值。
综上,就是接口一定要能够表达所有的返回情况,一般最简单的,就是在返回值里加一个状态码。