ThinkNotes

Simple is not easy | 化繁为简,知易行难

0%

套接字(socket)基础

套接字是网络编程中,应用层和传输层之间的数据结构,其作用如下:
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口,以区分不同应用程序进程间的网络通信和连接。

套接字地址结构

通用套接字地址的结构体sockaddr定义如下:
1
在以太网中,不直接使用sockaddr结构体,而使用sockaddr_in,其定义如下:

3

通用结构体sockaddr和以太网的sockaddr_in结构体,有点像C++中的父类和子类的关系,sockaddr_in是对sockaddr的细化,其存储结构大小相同,分布如下:
4
由于大小相同,在设置socket地址时,一般先设置sockaddr_in结构体,然后强转为sockaddr类型

套接字地址结构在用户层和内核层的交互

sockaddr的使用,以socket流程中的bind()函数为例:
5
bind函数需要传入sockaddr结构体的指针,和sockaddr结构体的长度

向内核传入数据

向内核传入数据的socket函数有:bind,send
传入过程如下:

  • sockaddr结构体的长度,以传值方式传入内核
  • 内核通过sockaddr结构体的指针以及结构体长度,以内存复制的方式,从用户层拷贝sockaddr结构体到内核层。
阅读全文 »

背景

项目文件中有如下类型文件:

Makefile, .sh, .bat, .cfg, .exe

源码用git管理,客户端用cygwin实现windows内的linux环境

问题:如何解决git多人协作下的linux、windows换行符差异问题?

(1)什么是换行符
LF:”\n”,Linux的换行符, 只包含“换行”;
CRLF:”\r\n”,Windows的换行符,包含“回车+换行”;

(2)不同换行符带来什么问题
用git管理代码,必定有远程端和本地端两个仓库,两端的操作系统不同,换行符可能有差异;

多人协作时,本地端可能有linux环境和windows, 如果所有人都是linux就不存在换行符差异的问题;如果有windows和linux就有该问题;
例如A上传了Linux换行符LF的代码到远程,B 本地环境是windows, B git pull下来,其git-config设置了自动转换成本地换行,将代码换行全成了CRLF,B上传后,远程仓库变成CRLF换行。此时A git diff查看,所有代码都有换行差异,扰乱真正的代码diff。

不仅是影响git diff, 换行差异还影响脚本执行

  • 例如LF换行的.sh,git pull到windows环境后换行转换成CRLF, 导致sh无法正常执行;
  • .bat调用.exe读取.cfg内的一行,.exe是windows程序,其readline方法只能识别CRLF换行,无法读取LF换行的.cfg文件内容
阅读全文 »

背景

存储设备产品(如SSD/eMMC)的读写速度和稳定性测试,是量产前必不可少的步骤。除了常规的两个设备来回拷贝读写,存储设备做系统盘的情况下,反复的启动,睡眠,休眠,也是重要的测试项。本文介绍这几种测试的原理,工具实现,和调试过程

系统电源状态

ACPI(高级配置与电源接口)是电源配置和接口的规范,供操作系统和应用程序管理所有电源,其定义了几种状态(State):S1~S6
image-20221205155311145

操作系统在ACPI基础上实现各自的电源状态划分
Linux电源状态划分为如下:
image-20221205155406635

其中,常见的几种状态有别称,如S3通常也称Suspend,S4也称Hibernation, S5即shutdown。下面描述这几种状态的区别

S3:
1、将系统当前的运行状态等数据保存在内存中,此时仍需要向RAM供电,以保证后续快速恢复至工作状态
2、冻结用户态的进程和内核态的任务(进入内核态的进程或内核自己的task)
3、关闭外围设备,如显示屏、鼠标等,中断唤醒外设不会关闭,如电源键
4、CPU停止工作

S4:
挂起到硬盘,俗称休眠(Hibernation)将系统当前的运行状态等数据保存到硬盘上,并自动关机。下次开机时便从硬盘上读取之前保存的数据,恢复到休眠关机之前的状态。
譬如在休眠关机时,桌面打开了一个应用,那么下一次开机启动时,该应用也处于打开状态。而正常的关机-开机流程,该应用是不会打开的

S5:
关机并断电,实际上,S5在不同场景下,可以指代关机和重启两种操作,关机是彻底power off的,而重启不是彻底断电,只是重新启动了系统,(当然基本所有设备都被reset),重启!=关机再启动。

测试工具设计

阅读全文 »

Github常用配置笔记

配置SSH登录(Github,Gitlab等各种git server通用)

配置SSH登录的目的是git操作免密码验证,方便拉取和上传代码。

参考:https://segmentfault.com/a/1190000043924833 (若要为个人和公司使用不同git账号,见此链接)

下面windows环境创建全局使用唯一的git账号和SSH key;

Linux环境创建SSH key,先查看~/.ssh有没有key,如果没有key或者有key但git clone有问题,可以删除key再重新创建id_rsa.pub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ git config --global user.name thomas.hu

cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ git config --global user.email thomas.hu@o2micro.com

cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/cursorhu/.ssh/id_rsa):
/c/Users/cursorhu/.ssh/id_rsa already exists.
Overwrite (y/n)?

cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ cat /c/Users/cursorhu/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
....
2hdYrBeOK+vu1LAAAACGN1cnNvcmh1AQI=
-----END OPENSSH PRIVATE KEY-----

cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ cat /c/Users/cursorhu/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDONvU2p10NVjRhu6UGlEMsRWqhbo16zK2Tnqg8....chI60tVZHozCK9PMKZd4dE9RoYMXpJWTo6uIRKEV41qHfaiipfsu1ibRCj1drz/3BTs= cursorhu

最后将id_rsa.pub公钥内容文本粘贴到github/gitlab的git账号设置页面中,之后无需登录密码即可git clone,git push。

1
2
3
4
5
6
7
8
9
cursorhu@DESKTOP-73G2O3N MINGW64 /c/gitlab-bht
$ git clone git@10.52.1.103:software/storport.git
Cloning into 'storport'...
remote: Enumerating objects: 12830, done.
remote: Counting objects: 100% (12830/12830), done.
remote: Compressing objects: 100% (3441/3441), done.
remote: Total 12830 (delta 9621), reused 12368 (delta 9251)
Receiving objects: 100% (12830/12830), 259.92 MiB | 52.21 MiB/s, done.
Resolving deltas: 100% (9621/9621), done.

初始化git和github仓库

阅读全文 »

背景

某软件有不同的配置参数,实现不同功能版本的编译
批量测试需要批量编译各种版本,实现方式为:
1.将编译参数组合,生成大量配置文件
2.编译过程遍历这些配置文件,依次编译对应版本
3.有参数加入,修改,删除,只需要更新这些配置文件
如何实现这些配置文件的更新?

实例

某芯片的Firmware批量编译实现:
Firmware代码为C, 配置参数用宏实现,后缀为.def
目录结构如下

|–project_folder
  |–config
   |–build.def
   |–defs
     |–1.def 2.def … n.def
  |–src
  |–Makefile
  |–build_All.sh
  |–update.sh

批量编译脚本

批量编译脚本如下
基本过程:
1.依次拷贝def文件夹中的每个def,替换默认的build.def
2.编译,接受所有编译参数
3.拷贝编译输出的image到包含git tag, def名,时间等信息的文件夹

#!/bin/bash

echo "Batch build support args:"
echo "1. functin version:"
echo "verargs=mp_fpga"
echo "verargs=mpw_asic"
echo "2. boot debug:"
echo "bootargs=debug"

OUTPUT=batch_build_$1$2

mkdir -p ${OUTPUT}
rm -rf ./batch_build_*

build_time=`date +%Y%m%d%H%M%S`

#commit_id=`git rev-parse HEAD`

tag_name=`git describe --exact-match --tags 2>/dev/null`

if [ -z "${tag_name}" ]; then
	tag_name="NO_TAG"
fi

mv ./config/build.def ./config/build.def.bak 

for file in `ls ./config/defs/*.def`;
do
	file_name=${file##*/}
	config_name=${file_name%.def}
	
	cp -rf ${file} ./config/build.def
	make clean
	make -j4 $@
	#mv ./build/image ./batch_build/${tag_name}_${config_name}_time_${build_time}_cid_${commit_id}
	mkdir -p ./${OUTPUT}/${tag_name}_${config_name}_time_${build_time}
	mv ./build/image/* ./${OUTPUT}/${tag_name}_${config_name}_time_${build_time}
done

mv ./config/build.def.bak ./config/build.def

def文件内的.def文件即各种参数配置文件,.def文件名即参数功能的别名组合
例如:

CQ_emmc_two_card_enhance_hs400_always_l0_MSIx_dllphase14_tuning_on.def
阅读全文 »

gcc编译选项

gcc提供了大量的警告选项,对代码中可能存在的问题提出警告,通常可以使用-Wall来开启以下警告:

   -Waddress -Warray-bounds (only with -O2) -Wc++0x-compat
   -Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration
   -Wcomment -Wformat -Wmain (only for C/ObjC and unless
   -ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses
   -Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point
   -Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1
   -Wswitch -Wtrigraphs -Wuninitialized (only with -O1 and above)
   -Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value
   -Wunused-variable

unused-function:警告声明但是没有定义的static函数;
unused- label:声明但是未使用的标签;
unused-parameter:警告未使用的函数参数;
unused-variable:声明但是未使用的本地变量;
unused-value:计算了但是未使用的值;
format:printf和scanf这样的函数中的格式字符串的使用不当;
implicit-int:未指定类型;
implicit-function:函数在声明前使用;
char- subscripts:使用char类作为数组下标(因为char可能是有符号数);
missingbraces:大括号不匹配;
parentheses: 圆括号不匹配;
return-type:函数有无返回值以及返回值类型不匹配;
sequence-point:违反顺序点的代码,比如 a[i] = c[i++];
switch:switch语句缺少default或者switch使用枚举变量为索引时缺少某个变量的case;
strict- aliasing=n:使用n设置对指针变量指向的对象类型产生警告的限制程度,默认n=3;只有在-fstrict-aliasing设置的情况下有效;
unknow-pragmas:使用未知的#pragma指令;
uninitialized:使用的变量为初始化,只在-O2时有效;

以下是在-Wall中不会激活的警告选项:

cast-align:当指针进行类型转换后有内存对齐要求更严格时发出警告;
sign- compare:当使用signed和unsigned类型比较时;
missing-prototypes:当函数在使用前没有函数原型时;
packed:packed 是gcc的一个扩展,是使结构体各成员之间不留内存对齐所需的空间,有时候会造成内存对齐的问题;
padded:也是gcc的扩展,使结构体成员之间进行内存对齐的填充,会造成结构体体积增大.
unreachable-code:有不会执行的代码时.
inline:当inline函数不再保持inline时 (比如对inline函数取地址);
disable-optimization:当不能执行指定的优化时.(需要太多时间或系统资源).
可以使用 -Werror时所有的警告都变成错误,使出现警告时也停止编译.需要和指定警告的参数一起使用.

编译的优化级别:
gcc默认提供了5级优化选项的集合:

-O0:无优化(默认)
-O和-O1:使用能减少目标文件大小以及执行时间并且不会使编译时间明显增加的优化.在编译大型程序的时候会显著增加编译时内存的使用.
-O2: 包含-O1的优化并增加了不需要在目标文件大小和执行速度上进行折衷的优化.编译器不执行循环展开以及函数内联.此选项将增加编译时间和目标文件的执行性能.
-Os:专门优化目标文件大小,执行所有的不增加目标文件大小的-O2优化选项.并且执行专门减小目标文件大小的优化选项.
-O3: 打开所有-O2的优化选项并且增加 -finline-functions, -funswitch-loops,-fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize优化选项.

-O1包含的选项-O1通常可以安全的和调试的选项一起使用:

   -fauto-inc-dec -fcprop-registers -fdce -fdefer-pop -fdelayed-branch
   -fdse -fguess-branch-probability -fif-conversion2 -fif-conversion
   -finline-small-functions -fipa-pure-const -fipa-reference
   -fmerge-constants -fsplit-wide-types -ftree-ccp -ftree-ch
   -ftree-copyrename -ftree-dce -ftree-dominator-opts -ftree-dse
   -ftree-fre -ftree-sra -ftree-ter -funit-at-a-time

以下所有的优化选项需要在名字前加上-f,如果不需要此选项可以使用-fno-前缀

阅读全文 »

文件名替换

1.wildcard
展开多个文件为使用空格分开的、匹配此模式的列表参数
格式
$(wildcard PATTERN...)

示例:

SRC=$(wildcard *.c)

2.patsubst
替换通配符
格式

$(patsubst %.c,%.o,$(dir))

示例:

obj := $(patsubst %.c,%.o,$(wildcard *.c))

3.替换引用
patsubst的示例等价于:

obj=$(dir:%.c=%.o)
阅读全文 »

shell增删改查概述

Linux Shell环境对文本增删改查,可以通过sed,awk和grep命令完成。

  • sed: 支持特定模式匹配的增加、插入、删除、替换、提取等操作
  • awk: 支持特定模式匹配的过滤查找,如提取某行列的字符串
  • grep: 支持包含特定字符串的结果提取,可配合find和awk实现过滤查找

这几种命令都支持流输入和文本输入,支持管道传递参数,也可以命令行传参。对于多个步骤的处理,可以几个命令串行使用。

shell的输入参数概述

Shell的命令,如cat, echo, sed, awk, grep, 管道命令|等,都要有输入参数,即待处理的数据。
输入参数有两种类型:

  • 标准输入:本质是stdin文件,即读取该文件的内容作为命令的输入参数,该文件内容可由其他命令的输出,或者用户输入来产生
  • 命令行输入:直接接受用户在命令行输入的字符串,一次性使用,不存储在stdin

支持标准输入作为参数的命令:cat, sed, awk, grep, |
只支持命令行输入字符串的命令:echo, ls
标准输入示例:

cat /etc/passwd | grep root

上面的代码使用了管道命令|,管道命令的作用是将左侧命令cat /etc/passwd的标准输出转换为标准输入,提供给右侧命令grep root作为参数。
以上命令也可以写成命令行输入形式:

阅读全文 »

背景

nodejs服务可以用nohup node xxx.js &后台启动,但是实际使用发现不太稳定,使用专门的node后台服务管理工具:pm2解决此问题

pm2特性

1、内建负载均衡(使用Node cluster 集群模块)
2、后台运行
3、0秒停机重载
4、具有Ubuntu和CentOS 的启动脚本
5、停止不稳定的进程(避免无限循环)
6、控制台检测
7、提供 HTTP API
8、远程控制和实时的接口API ( Nodejs 模块,允许和PM2进程管理器交互 )

pm2安装

npm install -g pm2

pm2用法

pm2 start app.js        //启动进程
pm2 start app.js -i 4   // 后台运行pm2,启动4个app.js
pm2 start app.js -i max //启动,使用所有CPU核心
pm2 start app.js --name my-api // 命名进程
pm2 list               // 显示所有进程状态
pm2 monit              // 监视所有进程
pm2 logs               //  显示所有进程日志
pm2 stop all           // 停止所有进程
pm2 restart all        // 重启所有进程
pm2 reload all         // 0秒停机重载进程 (用于 NETWORKED 进程)
pm2 stop 0             // 停止指定的进程
pm2 restart 0          // 重启指定的进程
pm2 startup            // 产生 init 脚本 保持进程活着
pm2 web                // 运行健壮的 computer API endpoint 
pm2 delete 0           // 杀死指定的进程
pm2 delete all         // 杀死全部进程

使用示例

部署Github项目NodeMail,每天给指定邮箱发邮件。

阅读全文 »

概述

shell中if-then-else-fi判断语句如下:

a="abc"

if [ $a = "abc" ]
then
   echo "$a = $b"
else
   echo "$a != $b"
fi

注意以下几点:

  • shell中的等号:=可用于赋值,也可以用于判断;==只用于判断,更规范
  • shell中的if语句各符号间都要空格分隔:if[ ]之间要空格;[ ]“ ”之间要空格; "=之间要空格。否则if语句中的符号会解析失败。
  • shell变量没有数据类型的区分,把任何存储在变量中的值,皆视为“字符串”
  • 对于变量可能为空的情况,需要用双括号[[ $a = "abc" ]]
  • if-then可以写在同一行,用;分隔两个语句:if [ $a = "abc" ];then

不同类型的判断语句

关系运算符判断

-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。

-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。

-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。

阅读全文 »