总结来说,这篇文档放入tools吧。
最近手上有一块nvidia jetson的板子,在调程序时需要不断的开关机,但是呢总忘记风扇的开启, 对我来说,板子稍微发热我就难受,但是每次开机后手动enable风扇太low,总搜集整理这个文档。
systemd也是大名鼎鼎,目前据说在最新的Ubuntu中已经使用它代替了initd. initd我之前用的比较多了, 但是具体原理没有太多的涉及,这篇文档也是这样的思路,在原理上、技术上不做太多的纠结,直接拿来使用。
可以先看一下这个目录下有很多service后缀的文件,而且大部分是系统文件。例如syslog.service
.
此时,我们可以在目录下新建一个文件: rc-local.service,其实这里自定义的名字是无所谓的。其内容可以比着葫芦
画瓢:
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
# Script was located in that
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
[Install]
WantedBy=multi-user.target
上面的字面意思也不用太多解释(之所以能够开机自启动在那个 forking)。当然,要记得修改文件权限:
sudo chmod 755 /etc/systemd/system/rc-local.service
这个文件就是开机后具体执行的脚本,我的如下:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
echo "测试脚本执行成功"
echo 100 >> /sys/devices/pwm-fan/target_pwm
exit 0
当然, 需要同样赋予权限:sudo chmod 755 /etc/rc.local
.
下面的命令,就是基于rc-local.service文件。
dclab@dclab-desktop:~$ sudo systemctl enable rc-local
[sudo] password for dclab:
启动服务并检查状态
sudo systemctl start rc-local.service
dclab@dclab-desktop:~$ sudo systemctl status rc-local.service
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: e
Drop-In: /lib/systemd/system/rc-local.service.d
└─debian.conf
Active: active (exited) since Tue 2021-01-19 19:59:36 CST; 57min ago
Tasks: 0 (limit: 4915)
CGroup: /system.slice/rc-local.service
Jan 19 19:59:36 dclab-desktop systemd[1]: Starting /etc/rc.local Compatibility...
Jan 19 19:59:36 dclab-desktop rc.local[6314]: 测试脚本执行成功
Jan 19 19:59:36 dclab-desktop systemd[1]: Started /etc/rc.local Compatibility.
Jan 19 20:55:18 dclab-desktop systemd[1]: /etc/systemd/system/rc-local.service:11:
在使用这个脚本一段时间过后,我才发现这里有一个问题就是:不建议使用forking作为启动脚本的hook,很容易引起因为系统资源的问题导致的host hang问题。
原来,自己在很多年前就总结了这块知识
今日,再一次进行更松散的总结。
linker在文件编译时,解决所有的外部符号;当执行时,loader(属于OS)将会重置所有的符号,并且放入合适的内存中。
linux 支持两类库: 静态库和动态库。根据程序编译阶段的不同,又可以分为动态链接库和动态加载库。
共享库没啥特别的地方,也是一种ELF格式文件,和普通的C语言文件没有区别(编译之前),除了没有main()
函数入口。当然,要告诉gcc-fPIC
选项。
vimer@host:~/test/lib$ cat libfunction.c
int double_me(int a){
return a + a;
}
// gcc -c -fPIC libfunction.c
// gcc -shared -o libmylib.so libfunction.o
vimer@host:~/test/lib$ ls
libfunction.c libfunction.o libmylib.so
当然,也可以综合起来:
gcc -shared -fPIC -o libmylib.so libfunction.c
那么, 在其他文件中,调用这个函数的方法是:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int double_me(int); // 最好放在 .h 文件内
int main(){
int i;
for(i = 0; i < 10; i++)
printf("%d'' s duoble is %d\t", i, double_me(i));
}
我们的main函数在编译阶段会link这个lib。
gcc -o main main.c -L. -lmylib
# -L: path
# -l : 自动加上lib及后缀so
但是,如果你执行试试:
vimer@host:~/test/lib$ ./main
./main: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory
link是没问题的,但是load是无法的。也就是说,尽管我们我们在link阶段指明了so的位置,但是load并不会得知。以上,基本上就是前面文件的内容。
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
# 必须在一条命令下才可以
So in order to make the loader find our library, we have to tell it where to look, either by adding the correct directory to the LD_LIBRARY_PATH environment variable, or by adding it to the ldconfig paths in either /etc/ld.so.conf inside the /etc/ld.so.conf.d/ directory. Let’s try it with the environment variable for now
如果只想在加载 load 时使用共享库的函数,还是有办法的。
// dynload.c
#include <stdio.h>
#include <dlfcn.h>
int main(void) {
// handle for dynamic loading functions
void *handle;
// function pointer for the library's double_me() function
int (*double_me)(int);
// just a counter
int i;
// open our library ..hopefully
if ( (handle = dlopen("./libmylib.so", RTLD_LAZY)) == NULL) {
return 1;
}
// try to extract "double_me" symbol from the library
double_me = dlsym(handle, "double_me");
if (dlerror() != NULL) {
dlclose(handle);
return 2;
}
// use double_me() just like with a regularly linked library
for (i = 1; i <= 10; i++) {
printf("%d doubled is %d\n", i, double_me(i));
}
dlclose(handle);
return 0;
}
这段代码需要借助libdl
动态库, 其实不好的一点就是得猜。
gcc -o dynload dynload.c -ldl
linux中有两种lib,一种是静态的,一种是动态的(Dynamic Shared Object–DSO),由操作系统借助
ld.so
或者ld-linux.so
在运行时动态load.
/etc/ld.so.cache : File containing an ordered list of libraries found in the directories specified in /*etc/ld.so.conf. This file is not in human readable format, and is not intended to be edited. This file is created by ldconfig command.
在上面的 main 例子中, ldconfig
作用在libmylib.so 的目录中并不起作用,不知道为什么。
但是,可以使用下面的命令让共享库生效:
sudo vim /etc/ld.so.conf.d/mylib.conf
vimer@host:~/test/lib$ cat /etc/ld.so.conf.d/mylib.conf
/home/vimer/test/lib # 把libmylib.so所在目录放入这个自己创建的文件,ldconfig一下就可以
vimer@host:~/test/lib$ sudo ldconfig
vimer@host:~/test/lib$ ./main
0'' s duoble is 0 1'' s duoble is 2 2'' s duoble is 4 3'' s duoble is 6 4'' s duoble is 8 5'' s duoble is 10 6'' s duoble is 12 7'' s duoble is 14 8'' s duoble is 16 9'' s duoble is 18
查看ldconfig的内容使用-v
选项。
sudo ldconfig -v | grep -i mylib
libmylib.so -> libmylib.so
ldd可以查看一个可执行文件所依赖的动态库
The ld.so or / ld-linux.so used as follows by Linux:
# cd /lib64
vimer@host:/lib64$ ./ld-linux-x86-64.so.2 --list /home/vimer/test/lib/main
linux-vdso.so.1 (0x00007fff48dfa000)
libmylib.so => /home/vimer/test/lib/libmylib.so (0x00007f9654e92000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9654aa1000)
与ldd的作用一样
The LD_LIBRARY_PATH can be used to set a library path for finding dynamic libraries using LD_LIBRARY_PATH, in the standard colon seperated format:
$ export LD_LIBRARY_PATH=/opt/simulator/lib:/usr/local/lib
The LD_PRELOAD allow an extra library not specified in the executable to be loaded:
$ export LD_PRELOAD=/home/vivek/dirhard/libdiehard.so
Please note that these variables are ignored when executing setuid/setgid programs.
pkg-config的作用与ldconfig有些类似,但是pkg-config更侧重在编译、链接阶段;而ldconfig侧重链接、运行时阶段。
一定要限定在普通用户下,或者一定确保你自己执行ps
命令不会引出别的进程,不然会引出大麻烦,杀掉系统服务进程或者杀掉别人的进程。
#########################################################################
# File Name: kill_python.sh
# Author: Bo Yu
# mail: [email protected]
# Created Time: 2020年12月25日 星期五 18时57分02秒
##########################################################################
#!/bin/bash
function grep_python(){
tmp_line="ps_txt"
if [ -e $tmp_line ];then
`rm $tmp_line`
else
`touch $tmp_line`
fi
`ps > $tmp_line`
#cat $tmp_line
cat $tmp_line | \
awk '{
for(i=1; i<=NF;i++){
if($i ~ /python/){
cmd="echo "$1" "$4
system(cmd)
cmd="kill -9 "$1
system(cmd)
}
}
}'
}
grep_python
Dockerfile就是指令docker镜像如何建立的一个文件,基本上只有动作,过程的东西基本不要放在里面。
下面的这个Dockerfile就是快速搭建一个Ubuntu18.04的环境,当然,这个没什么可以值得记录的东西,关键是快速更换了国内的镜像源,包括python.
vimer@host:~/test/docker$ cat Dockerfile
FROM ubuntu:18.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV SOURCES_LIST=/etc/apt/sources.list
# It will fail without cert
#RUN apt update -y && apt install ca-certificates -y
# 更换国内源 @method 1: 但是没有替换全面
# RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
# @method 2: Fill source.list with echo
RUN mv /etc/apt/sources.list /etc/apt/sources-bak.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse" >> $SOURCES_LIST
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN apt update
RUN mkdir .pip && echo "[global]" >> .pip/pip.conf && echo "https://pypi.tuna.tsinghua.edu.cn/simple/" >> .pip/pip.conf
如果可以的话, 我打算把echo中的内容放在一个文件内,这样就可以快速更新source了。
最后执行 :
docker build -t="demo-video" ./
# 或者
docker build -t "demo-video" -f Dockerfile ./
就可以创建镜像了,使用类似docker run -it --rm image-name
就可以run它了。
不过需要注意的是,标签一定设置的正确,不然,你的应用可能用不上。
别的也不说了,还是具体看代码的例子吧。
```bash vimer@host:~/git/videobench$ cat Dockerfile FROM ubuntu:18.04
ENV SOURCES_LIST=/etc/apt/sources.list
RUN mv /etc/apt/sources.list /etc/apt/sources-bak.list
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse” » $SOURCES_LIST
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN apt update
RUN mkdir .pip && echo “[global]” » .pip/pip.conf && echo “https://pypi.tuna.tsinghua.edu.cn/simple/” » .pip/pip.conf
RUN
apt-get install -y build-essential git make
ninja-build
nasm
python3
python3-dev
python3-pip
python3-setuptools
python3-tk
&& pip3 install –upgrade pip
&& pip install numpy scipy matplotlib notebook pandas sympy nose scikit-learn scikit-image h5py sureal meson cython
&& mkdir /tmp/vmaf
&& cd /tmp/vmaf
&& git clone https://github.com/Netflix/vmaf.git .
&& make
&& make install
&& mv model /usr/local/share
&& rm -r /tmp/vmaf
RUN
apt-get install -y yasm pkg-config
&& mkdir /tmp/ffmpeg
&& cd /tmp/ffmpeg
&& git clone https://git.ffmpeg.org/ffmpeg.git .
&& ./configure –enable-libvmaf –enable-version3 –pkg-config-flags=”–static”
&& make -j 8 install
&& rm -r /tmp/ffmpeg
算法学习,是一个持久的斗争,除了思维上的锻炼,更考验意志品质,说白了,对我而言,这不是简单的一个任务,必须持之以恒。
今天关注一下分治算法。个人感觉这个算法比回溯算法要难一些。 当然,形式上看着是非常简单的,但是代码写起来,未必容易理解。
分治法,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
有没有看到,分成子问题的这个过程,肯定借用了递归,可以说二者是形影不离的。
思想: 把大问题分解成规模较小的子问题, 各个击破,分而治之。
策略: 将子问题的解进行合并。
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)合并排序
(6)快速排序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔
以上参考资料
先看一下leetcode 95. Unique Binary Search Trees II,题目的意思是给定一个整型数字n,打印出具有n个节点的二叉搜索树。
二叉搜索树的定义很简单,就是根节点左子树的值都小于根节点,右子树都大于根节点。
怎么思考呢?其实我是没有思路的,这个时候我们应该怎么办,正面突破不容易,得想想办法。我们首先从n等于1开始尝试.
1
/ \
NULL NULL
如果n等于2,则有:
1
/ \
null 2
# and
2
/ \
1 NULL
其实,如果把这两种情况的例子写出来,也许就容易多了,但是,对我来说,这并不容易,只能靠程序量去解决这个问题。 先上一个优秀的代码,看一下这个代码是怎么写的。
class Solution {
private:
vector<TreeNode*> helper(int start, int end){ // 这两个很好
vector<TreeNode*> res;
if(start > end){
res.push_back(NULL);
return res;
}
for(int i = start; i <= end; i++){
vector<TreeNode*> lefts = helper(start, i-1);
vector<TreeNode*> rights = helper(i+1, end);
for(int j = 0; j < (int)lefts.size(); j++)
for(int k = 0; k < (int)rights.size(); k++){
TreeNode *root = new TreeNode(i);
root->left = lefts[j];
root->right = rights[k];
res.push_back(root);
}
}
return res;
}
public:
vector<TreeNode*> generateTrees(int n) {
if(n == 0) return vector<TreeNode*>(0);
return helper(1,n);
}
};
先来看一下n=1的情况,当helper(1,1)
传入时,
vector<TreeNode*> lefts = helper(start, i-1);
vector<TreeNode*> rights = helper(i+1, end);
都会返回一个NULL,并且加入进res中(也就是res.size()为1),然后在双重循环中,new TreeNode(1)
,一定使用new去实例化。这个时候,左右子树都为NULL,最后将root压入res中。
当n=2是,helper(1,2)传入,首先i=1, lefts = helper(start, i-1)
将会返回一个NULL,而rights
则有些变化了。helper(2,2)
,并不会返回一个NULL,而是返回一个只有值为2的节点。那么,到了二重for语句那里,会实例化出(i=1)作为root,然后左孩子为NULL,右孩子为节点2.这里还需要注意一个语句,root->left = lefts[j]
,这个为啥需要[j],字面上的意思肯定是深度。这样,就完成了一个。
然后i=2时,重复上面的过程,lefts = helper(1,1)
,所以lefts是只有一个节点值为1的节点。而此时的rights是一个NULL,
那么,在二重for,会实例化(i=2),并把1节点lefts、null节点rights赋给root,最后加入到res中。
n=1,2都好理解,当n=3时就麻烦一些了,但是,我们以其中一个例子说明。当helper(1,3)传入时,首先得到的是lefts = helper(1,0)和rights = helper(2,3),(i=1)这是第一层的。显而易见,helper(1,0)只会返回一个NULL, 还是要注意rights = helper(2,3)(i=2),那么rights会分裂为lefts = helper(2,1)(NULL), rights=helper(3,3),这是第二层;rights=heler(3,3)返回节点3,在第三层,可以不用管了。那么在第二层中i=2时,有了lefts=NULL和rights=3,在new TreeNode(2)
后,lefts和rights分别赋值给root(2).这里需要注意的是,这个
2
/ \
NULL 3
是在第一层的rights=helper(2,3)得来的,此时i=1,new TreeNode(1)
,左孩子为NULL, 右孩子为上图,符合题意。
当i=2时, 会分裂为lefts=helper(1,1)和rights=helper(3,3),这个就简单了。
2
/ \
1 3
我们都对c++的NULL不陌生,但是今天在看一个代码的时候突然愣住了,就是简单的如下面这样:
if(start > end) {
res.push_back(NULL);
return res;
}
你说这个条件成立的话,另一个意思就是res添加了一个NULL元素,那么,该res.size()的值会不会加1呢,答案是肯定的,只不过我之前没有遇到此类的问题,导致自己没想过来,下面使用具体的代码进行验证:
int main() {
vector<char*> s;
s.push_back("hello");
cout << s.size() << endl;
s.push_back(NULL);
cout << s.size() << endl;
}
// output
// 1
// 2
一看上述代码就是新手写的,为啥呢?会报警告的,c++编译器,详情可以参考这篇文章
s.push_back(const_cast<char*>("hello"));
这样的代码才算ok, 或者直接声明为vector<string> s
, 但是更需要特许处理一下。