脚本地址:点击访问
安装
yum - y install mysql-servse 启动mysql服务
service mysqld start 运行端口为3306
登陆
mysql -uroot 语法 :mysql -u账号 -p密码
默认是空密码
库和表
层次关系
库–>表
create database 库名;创建一个库
show database;查看有哪些库
use 库名;进入这个库
show tables;查看表
创建表
CREATE TABLE fruits( f_id CHAR(10) PRIMARY KEY, s_id INT NOT NULL, f_name char(255) NOT NULL, f_price DECIMAL(8,2) NOT NULL, ); 语法 CREATE TABLE 表名(
列名 类型,约束条件,
);
后面要用,隔开
f_id s_id f_name f_price
添加数据
INSERT INTO fruits(f_id,s_id,f_name,f_price) VALUES('a1',101,'apple',5.2); INSERT INTO fruits(f_id,s_id,f_name,f_price)VALUES('b1',101,'blackberry',10.
i++ 是先引用后增加 ,先在i所在的表达式中使用i的当前值,后让i加1++i 是先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值他们其实都是i=i+1的意思,但是在程序中运行的时候的执行的顺序不一样。 i–-和–-i 的使用方法和作用和 i++ / ++i同理
项目简介 SpringBoot2+MybatisPlus+SpringSecurity+jwt+redis+Vue 的前后端分离的商城系统, 包含商城、拼团、砍价、商户管理、 秒杀、优惠券、积分、分销、会员、充值、多门店等功能,更适合企业或个人二次开发。
商城功能 商品模块:商品添加、规格设置,商品上下架等
订单模块:下单、购物车、支付,发货、收货、评价、退款等
营销模块:积分、优惠券、分销、砍价、拼团、秒杀、多门店等
微信模块:自定义菜单、自动回复、微信授权、图文管理、模板消息推送
配置模块:各种配置
用户模块:登陆、注册、会员卡、充值等
其他等
项目结构 项目采用分模块开发方式
yshop-weixin 微信相关模块
yshop-common 公共模块
yshop-admin 后台模块
yshop-logging 日志模块
yshop-tools 第三方工具模块
yshop-generator 代码生成模块
yshop-shop 商城模块
yshop-mproot mybatisPlus
系统预览 技术选型 后端使用技术 1.1 SpringBoot2
1.2 mybatis、MyBatis-Plus
1.3 SpringSecurity
1.5 Druid
1.6 Slf4j
1.7 Fastjson
1.8 JWT
1.9 Redis
1.10 Quartz
1.11 Mysql
1.12 swagger
1.13 WxJava
1.14 Lombok
1.15 Hutool
1.16 Mapstruct
1.17 Redisson
1.18 Rocketmq
前端使用技术 2.
1、如何用缩进表示代码块
Python 使用缩进(空格)来表示代码块。通常,语句末尾的冒号表示代码块的开始。在 if、for、while、函数、类等定义中都会使用到代码块。例如:
if x > 100:
y = x *5 -1
else:
y = 0
# 2、代码怎样注释
Python 注释有单行注释和多行注释。
单行注释用“#”表示注释开始,“#”之后的内容不会被执行。单行注释可以单独占一行,也可放在语句末尾。例如:
x=5
if x > 100:
y = x *5 - 1 #单行注释:x>100 时执行该语句
else:
y = 0 #x<=100 时执行该语句
print(y) #输出 y
多行注释是用三个英文的单引号“'''”或双引号“"""”作为注释的开始和结束符号。例如:""" 多行注释开始
下面的代码根据变量 x 的值计算 y
注意代码中使用缩进表示代码块多行注释结束"""
# 3、语句续行有几种方式
通常,Python 中的一条语句占一行,没有类似于 Java 中的分号等语句结束符号。在遇到较长的语句时,可使用语句续行符号,将一条语句写在多行之中。
Python 有两种续行方式。
一种是使用“\”符号。
例如:if x < 100 \
and x>10:
y = x *5 - 1
MySQL是业界常用的关系型数据库,本文介绍了Go语言如何操作MySQL数据库。
Go操作MySQL 连接 Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的数据库驱动。使用database/sql包时必须注入(至少)一个数据库驱动。
我们常用的数据库基本上都有完整的第三方实现。例如:MySQL驱动
下载依赖 go get -u github.com/go-sql-driver/mysql 使用MySQL驱动 func Open(driverName, dataSourceName string) (*DB, error) Open打开一个dirverName指定的数据库,dataSourceName指定数据源,一般至少包括数据库文件名和其它连接必要的信息。
import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) func main() { // DSN:Data Source Name dsn := "user:password@tcp(127.0.0.1:3306)/dbname" db, err := sql.Open("mysql", dsn) if err != nil { panic(err) } defer db.Close() // 注意这行代码要写在上面err判断的下面 } 思考题: 为什么上面代码中的defer db.Close()语句不应该写在if err != nil的前面呢?
初始化连接 Open函数可能只是验证其参数格式是否正确,实际上并不创建与数据库的连接。如果要检查数据源的名称是否真实有效,应该调用Ping方法。
返回的DB对象可以安全地被多个goroutine并发使用,并且维护其自己的空闲连接池。因此,Open函数应该仅被调用一次,很少需要关闭这个DB对象。
// 定义一个全局对象db var db *sql.DB // 定义一个初始化数据库的函数 func initDB() (err error) { // DSN:Data Source Name dsn := "
注意: 必要时需要离散化
#include<bits/stdc++.h> using namespace std; const int N=1e5+10000; int c[N]; int n,a[N]; int f[N]; int lowbit(int x) { return x&(-x); } void add(int i,int val) { while(i<N) { c[i]=max(c[i],val); i+=lowbit(i); } } int sum(int i) { int res=0; while(i) { res=max(res,c[i]); i-=lowbit(i); } return res; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int mx=0; for(int i=1;i<=n;i++) { f[i]=sum(a[i])+1; add(a[i],f[i]); } for(int i=1;i<=n;i++) cout<<f[i]<<endl; }
升级jdk11后,IDEA本地构建报错如下
解决办法:
增加编译参数:--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED
保存后本地代码不再编译报错。但是上环境发布仍然会报错。修改方式为:
对应模块pom.xml增加maven插件,增加
<compilerArgs> <arg>--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED</arg> </compilerArgs> 注意:
java.base/sun.security.pkcs是我这个项目需要修改的模块。修改时替换成你自己的模块名称。
ubuntu16.04
挂起,唤醒后 ,电源指示灯正常,风扇正常。就是屏幕不亮。
原因:系统内核自动升级,导致系统内核版本改变 。
解决:修改系统内核版本,禁止系统内核自动升级。
本周的聊技术话题和大家说说如何在Android中实现自适应文本大小显示。
想象一下,在布局中,通常显示文本的区域大小是固定的,但是文本长度并不总是固定的。比如列表中的文章标题、界面下方的按钮文本等等。
为了尽可能让这些文字可见,传统的做法是通过文本长度设置文本大小,或者通过android:ellipsize属性设置文本省略方式等等。
实际上,从Android O开始,Android API引入了自适应文本大小的简易实现。它不仅可以用于TextView,对于Button等具有文本显示的控件同样适用。通过androidX,还可以适配到低版本的Android中。
下面,我们以TextView为例,来看看如何使用它们吧。
开启自适应显示模式 我们以Empty Activity方式创建一个新的工程,其中默认包含了只显示Hello World!文本的TextView控件,我们为其加上id,并修改尺寸和默认文本。为了兼容低版本的Android设备,使用androidX中提供的的AppCompatTextView替代TextView。
完整的布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/activity_main_tv" android:layout_width="100dp" android:layout_height="20dp" android:text="abcdefghijklmno" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> 然后,将程序运行起来,可以看到,文字的显示仅到字母“n”处就停止了,”o”并没有显示。显而易见,这是因为文本框的大小限制了文字的显示。
下面,我们来到MainActivity代码,调用setAutoSizeTextTypeWithDefaults();方法,开启自适应缩放。
private AppCompatTextView demoTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); demoTv = findViewById(R.id.activity_main_tv); demoTv.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM); } 重新运行后,可以看到文本被完整地显示出来了:
对于setAutoSizeTextTypeWithDefaults();方法,我们可以传入的参数值通常有两个,分别是TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM和TextView.AUTO_SIZE_TEXT_TYPE_NONE。对应开启自适应文本大小与否。
自定义文本缩放方式 下面,我们修改TextView的文本,继续追加:
android:text="abcdefghijklmnopqrstuvwxyz" 再次运行App,我们发现:无论自适应模式开关与否,都无法完整地显示所有文本。这时候,我们就要对文本大小的自动缩放进行自定义了。
设定缩放配置 首先要介绍一种通过设定缩放配置实现自定义缩放的方法。我们先来看代码:
demoTv.setAutoSizeTextTypeUniformWithConfiguration(4,15,2, TypedValue.COMPLEX_UNIT_SP); 如上代码所示,setAutoSizeTextTypeUniformWithConfiguration();是实现此种缩放方式的关键,它由4个参数构成。
4:表示文本缩放的最小值;20:表示文本缩放的最大值;2:表示文本缩放的尝试步长;TypedValue.COMPLEX_UNIT_SP:指定前三个参数使用SP作为单位。 可以这样理解,当文本无法显示完全时,系统将以步长为单位递减文本大小设定值,直到文本完全显示,或者尝试值小于给定的文本缩放的最小值。
重新运行App,可以看到文本已经被完整地显示出来了。
定义预设值 自定义缩放的第二种方式是通过定义预设值实现。我们还是直接看代码:
demoTv.setAutoSizeTextTypeUniformWithPresetSizes(new int[]{9, 11, 13, 15}, TypedValue.
String a= “1,2,3,4,5,6,”;
//删除最后一个字符,
a= a.substring(0, a.length() -1);
1、将逗号分隔的字符串转换为List
String str = “a,b,c”;
List result = Arrays.asList(str.split(","));
2、将List转换为逗号分隔的字符串
(1) 利用Guava的Joiner
List list = new ArrayList();
list.add(“a”);
list.add(“b”);
list.add(“c”);
String str = Joiner.on(",").join(list);
(2)利用Apache Commons的StringUtils
List list = new ArrayList();
list.add(“a”);
list.add(“b”);
list.add(“c”);
String str = StringUtils.join(list.toArray(), “,”);
Android APP如何简单快速实现控制硬件设备 driver(C)层硬件控制节点的创建 APP(java)层native(C/C++)层接下来上干货,重点说说实现流程Android studio 中APP 实现部分驱动实现部分安装APP调试APP签名 本文叙述的是方便快速的实现APP控制硬件,而不是Android的整体架构。Android APP控制硬件设备主要是通过JNI工具,实现Java层与C层的链接。driver层实现对硬件的直接控制,并通过创建文件或设备节点提供给native层访问。在native层代码中实现Java调用接口,一般包括对文件节点的open、read、write等操作,并利用ndk工具将native层代码编译成.so库,提供给Java层调用。 driver©层 在驱动层包括两方面的任务。第一是实现硬件控制部分,第二是实现节点创建。 硬件控制 主要通过对硬件模块寄存器的读写操作,或者对GPIO的置位和复位。 例如,你想控制开发板的某个led,那么只需要对那个led对应的寄存器的某一位置位(设置1)或复位(设置0)即可。 如果想要控制某个模块设备的led,就需要通过通信协议实现,通信协议一般包括II2,SPI,UART等等。 节点的创建 在Android/Linux驱动中创建节点的方法有三种。 第一,通过fileoperation结构体创建 第二,通过sys文件系统创建 第三,通过proc文件系统创建 APP(java)层 下面展示一些 内联代码片,主要实现Java层的调用接口,文件名:
myjni.java
package com.example.awdetectapplication; public class awjni { //本方法主要实现了驱动节点的读写操作 public static native int IICWriteRead(byte[] WriteBuffer, int WriteLength,byte[] ReadBuffer, int ReadLength); //本方法主要实现了驱动节点的打开和关闭 public static native int OpenDeviceNode(); public static native int CloseDeviceNode(); static { //jni为生成.so库的名字 System.loadLibrary("jni"); } } native(C/C++)层 在本层通过C语言实现对节点的读写。 下面展示一些 内联代码片,文件名:
myjni.c
/* * Class: com_example_awdetectapplication_awjni * Method: IICWriteRead * Signature: ([BI[BI)I */ #include <errno.
当时是find -exec 缺少参数 然后研究了研究 发现是参数没给对
-exec
参数后面跟的是command命令,它的终止是以;为结束标志的,所以这句命令后面的分号是不可缺少的,考虑到各个系统中分号会有不同的意义,所以前面加反斜杠。
{} 代表的是find 查找处理的结果
exec选项后面跟随着所要执行的命令或脚本,{},空格 和 \,最后是一个分号;
示例:
find ./ -name "*.py" -newermt "2020-12-07" -exec cp '{}' /home/app/**/**/**/ \;
Android studio Activity中找不到layout的id问题 cause firstcause secondcause thirdcause fourth cause first 先搞清楚有几个xml文件,如果是水平方向和垂直方向的两个xml, 那么就要分析,是否在两个xml文件中都实现了你要寻找的id。 cause second ①setContentView(R.layout.activity_canvas);必须写在findByid()之前; ②btn_get = findViewById(R.id.btn_get); ③ btn_get.setOnClickListener(this); ④实现onClick()方法。 cause third 清除工程文件,重新编译: build——>clean project cause fourth 通过view查找id: View view = getLayoutInflater().inflate(R.layout.activity_menu, findViewById(R.id.menu_id)); btn_getbtn_get =view.findViewById(R.id.btn_get);
参考 https://blog.csdn.net/humanking7/article/details/78559692 简化
1. 先新建一个 Qt Widgets Application
2. 进入ui设计界面,把 Scroll Area 拖进来
3. 在主窗口上右键,选择布局,水平布局或者垂直布局
4. 在scrollAreaWidgetContents上右键,选择大小限定,设置最小大小
5. 然后运行,缩小下窗口大小,即可看到滚动条
Quorum机制介绍 在分布式系统中有个CAP理论,对于P(分区容忍性)而言,是实际存在 从而无法避免的。因为,分布系统中的处理不是在本机,而是网络中的许多机器相互通信,故网络分区、网络通信故障问题无法避免。因此,只能尽量地在C 和 A 之间寻求平衡。对于数据存储而言,为了提高可用性(Availability),采用了副本备份 。 但是,问题来了,当需要修改数据时,就需要更新所有的副本数据,这样才能保证数据的一致性(Consistency)。因此,就需要在 C(Consistency) 和 A(Availability) 之间权衡 。
在介绍Quorum之前,先看一个极端的情况:WARO机制
WARO(Write All Read one)是一种简单的副本控制协议,当Client请求向某副本写数据时(更新数据),只有当所有的副本都更新成功之后,这次写操作才算成功,否则视为失败。 这里可以看出两点:①写操作很脆弱,因为只要有一个副本更新失败,此次写操作就视为失败了。②读操作很简单,因为,所有的副本更新成功,才视为更新成功,从而保证所有的副本一致。这样,只需要读任何一个副本上的数据即可。假设有N个副本,N-1个都宕机了,剩下的那个副本仍能提供读服务;但是只要有一个副本宕机了,写服务就不会成功
WARO牺牲了更新服务的可用性,最大程度地增强了读服务的可用性。而Quorum就是更新服务和读服务之间进行一个折衷。
Quorum机制是“抽屉原理”的一个应用。定义如下:假设有N个副本,更新操作wi 在W个副本中更新成功之后,才认为此次更新操作wi 成功。称成功提交的更新操作对应的数据为:“成功提交的数据”。对于读操作而言,至少需要读R个副本才能读到此次更新的数据。**其中,W+R>N ,即W和R有重叠。**一般,W+R=N+1
假设系统中有5个副本,W=3,R=3。初始时数据为(V1,V1,V1,V1,V1)–成功提交的版本号为1
当某次更新操作在3个副本上成功后,就认为此次更新操作成功。数据变成:(V2,V2,V2,V1,V1)–成功提交后,版本号变成2
因此,最多只需要读3个副本,一定能够读到V2(此次更新成功的数据)。而在后台,可对剩余的V1 同步到V2,而不需要让Client知道。
Quorum机制分析
①Quorum机制无法保证强一致性
所谓强一致性就是:任何时刻任何用户或节点都可以读到最近一次成功提交的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。
因为,仅仅通过Quorum机制无法确定最新已经成功提交的版本号。
比如,上面的V2 成功提交后(已经写入W=3份),尽管读取3个副本时一定能读到V2,如果刚好读到的是(V2,V2,V2),则此次读取的数据是最新成功提交的数据,因为W=3,而此时刚好读到了3份V2。如果读到的是(V2,V1,V1),则无法确定是一个成功提交的版本,还需要继续再读,直到读到V2的达到3份为止,这时才能确定V2 就是已经成功提交的最新的数据。
1)如何读取最新的数据?—在已经知道最近成功提交的数据版本号的前提下,最多读R个副本就可以读到最新的数据了。
2)如何确定 最高版本号 的数据是一个成功提交的数据?—继续读其他的副本,直到读到的 最高版本号副本 出现了W次。
②基于Quorum机制选择 primary
中心节点(服务器)读取R个副本,选择R个副本中版本号最高的副本作为新的primary。
新选出的primary不能立即提供服务,还需要与至少与W个副本完成同步后,才能提供服务—为了保证Quorum机制的规则:W+R>N
至于如何处理同步过程中冲突的数据,则需要视情况而定。
比如,(V2,V2,V1,V1,V1),R=3,如果读取的3个副本是:(V1,V1,V1)则高版本的 V2需要丢弃。
如果读取的3个副本是(V2,V1,V1),则低版本的V1需要同步到V2
2.2 Quorum机制应用实例 HDFS高可用性实现
HDFS的运行依赖于NameNode,如果NameNode挂了,那么整个HDFS就用不了了,因此就存在单点故障(single point of failure);其次,如果需要升级或者维护停止NameNode,整个HDFS也用不了。为了解决这个问题,采用了QJM机制(Quorum Journal Manager)实现HDFS的HA(High Availability)。注意,一开始采用的“共享存储”机制,关于共享存储机制的不足,可参考:(还提到了QJM的优点)
In a typical HA cluster, two separate machines are configured as NameNodes.
今日内容:
1.hashlib模块:加密
2.hmac模块:加密
3.configparser模块:操作配置文件
4.subprocess模块:操作shell命令
5.xlrd模块:excel
6.xlwt模块:excel写
7.xml模块
hashlib模块:加密
import hashlib
# 基本使用
cipher = hashlib.md5('需要加密的数据的二进制形式'.encode('utf-8'))
print(cipher.hexdigest()) # 加密结果码
# 加盐
cipher = hashlib.md5()
cipher.update('前盐'.encode('utf-8'))
cipher.update('需要加密的数据'.encode('utf-8'))
cipher.update('后盐'.encode('utf-8'))
print(cipher.hexdigest()) # 加密结果码
# 其他算法
cipher = hashlib.sha3_256(b'')
print(cipher.hexdigest())
cipher = hashlib.sha3_512(b'')
print(cipher.hexdigest())
hmac模块:加密
# 必须加盐
cipher = hmac.new('盐'.encode('utf-8'))
cipher.update('数据'.encode('utf-8'))
print(cipher.hexdigest())
configparser模块:操作配置文件
# my.ini
# 注释:该配置文件中,值直接书写,但有四种类型
# -- int float boolean str
# section
[server]
# name:option | value:mysql
name = mysql
排序算法是最基础的算法之一,下面简单讲解下常用的七大算法的解题思路和相关的代码解法,包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、计数排序。
1.冒泡排序 从第一个元素开始比较相邻元素,若第一个元素比第二个大,则交换他们的顺序重复操作直到最后一个元素,一组操作下来最后一个元素是这组元素的最大值重复上述操作,除了最后一个元素直到没有元素可以比较,结束排序 //已知将要比较的数组arr[], for(int i=1;i<arr.length;i++){ for(int j=0;j<arr.length-i;j++){ //比较相邻元素的大小 if(a[j]>a[j+1]){ int tmp=a[j]; a[j]=a[j+1]; a[j+1]=tmp; } } } 冒泡排序内外嵌套了两层循环,时间复杂度O(n²);
2.选择排序 在未排序序列中选择最小的元素,将其放到最前面重复第一步的操作直到所有元素完成排序 //待排序的序列array for(int i=0;i<array.length-1;i++){ //初始化最小值 int min=i; //开始比较 for(int j=i;j<array.length-1;j++){ if(array[j]<array[min]){ min=j; } } if(min!=i){ int tmp=array[j]; array[j]=array[min]; array[min]=tmp; } } 仍然是双层循环,时间复杂度与冒泡排序一样
3.插入排序 从未排序的元素中选中第一个元素与已排序的元素从后往前一一比大小,选择合适位置插入重复上述操作直到所有元素排序完成 //待排序的数组a; for(int i=1;i<a.length;i++){ int tmp=a[i]; int j=i; //不断比较找到合适插入的地方 while(tmp<a[j-1]){ a[j]=a[j-1]; j--; } if(i!=j){ a[j]=tmp; } } 类比打扑克,一手乱序的牌一张一张的插入他们合适的位置。
最好情况的时间复杂度是 O(n),最坏情况的时间复杂度是 O(n2),然而时间复杂度这个指标看的是最坏的情况,而不是最好的情况,所以插入排序的时间复杂度是 O(n2)。
4 希尔排序 确定增量gap值(如数组长度的二分之一)相邻gap值的元素进行插入排序将gap值减小(如二分之一gap值),继续进行插入排序,直至gap值为1,完成所有排序 //待排序数组a; for(int gap=a.
大家在一些节目中应该经常会看到抽奖环节,在海量的手机号码中随便抽出一个来。今天,小汪老师就来用Excel仿制一个简易的抽奖器,制作起来非常简单。大家今后如果做抽奖活动的话,也可以用上一用。
效果演示 按住F9键不松,即可开始抽奖,松开后会随机显示一个号码。
前期准备 在制作抽奖器之前,大家要先录入好抽奖名单。如下图所示,这里我选择录入的是手机号码。当然,你也可以选择录入人员姓名或其他号码。然后在边合并几个单元格,当作是显示中间号码的位置。制作这些没什么技巧而言,我就不多演示了。
开始制作 1、在“中奖号码”下面单元格中输入公式「=INDEX(A:A,RANDBETWEEN(2,17))」。 说明:
INDEX函数:返回区域中的值或对值的引用。
INDEX(单元格区域,数组中某行的行序号)
RANDBETWEEN函数:是随机数函数。RANDBETWEEN(2,17)是包含2-17之间所有随机数。(这里录入的数据是从第2行开始到17行)
INDEX(A:A,RANDBETWEEN(2,17))INDEX(A列,随机显示包含2-17之间所有数)
2、选中所有手机号码,然后进入「开始」-「样式」-「条件格式」-「突出显示单元格规则」-「等于」,选中一下位置,再在右侧设置一下突出的样式确定即可完成。按住F9就能开始刷新,随机数字也会跟着变化。
相关阅读 / WORD联盟这种高端表格模板你会做吗?Excel制作带照片的员工信息查询表 / WORD联盟Excel录入信息,你有我快吗! ↓↓↓点击「
openvas干啥用的还有该扫描器是怎样的体系架构不是本文的重点,如需了解请查阅官网文档或openvas - 搜索结果
本文着重介绍如何为树莓派搭建交叉编译环境以及编译openvas软件的最优流程
1.安装编译主机操作系统,因为目标平台是raspbian,编译主机操作系统应尽量一致,故安装Debian 9.7,虚拟机的话尽量分配足够多的核心数,有助于提高编译速度。
2.安装cmake、git等编译工具
3.获取toolchain
git clone https://github.com/raspberrypi/tools.git 4.将toolchain加入到path环境变量中(本人安装的64位debian,使用root登录系统,tools被克隆到/root中,请根据你自己的路径设置)
echo '$PATH:/root/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin' >> ~/.profile source ~/.profile #输出gcc版本信息验证 arm-linux-gnueabihf-gcc --version 5.通过nfs将目标系统的根目录挂载到编译主机上
6.将greenbone openvas的几个关键组件克隆到编译主机上
#扫描器 git clone https://github.com/greenbone/openvas-scanner #管理器 git clone https://github.com/greenbone/gvmd #WEB控制端 git clone https://github.com/greenbone/gsa #库 git clone https://github.com/greenbone/gvm-libs ---------------------------以上进行交叉编译的准备基本完成------------------------------
编译需要优先编译gvm-libs,其他组件顺序随意。
1.准备一份cmake toolchain file
SET(CMAKE_SYSTEM_NAME Linux) #SET(CMAKE_SYSTEM_VERSION 1) SET(CMAKE_SYSTEM_PROCESSOR arm) SET(ARCH armhf) SET(rootfs "/root/rootfs") SET(CMAKE_SYSROOT "${rootfs}") SET(tools "/root/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64") SET(CMAKE_C_COMPILER "${tools}/bin/arm-linux-gnueabihf-gcc") SET(CMAKE_CXX_COMPILER "${tools}/bin/arm-linux-gnueabihf-g++") SET(CMAKE_Fortran_COMPILER "${tools}/bin/arm-linux-gnueabihf-gfortran") set(PKG_CONFIG_EXECUTABLE "arm-linux-gnueabihf-pkg-config" CACHE PATH "