OpenWrt的学习和总结

2023-11-08

OpenWrt的学习和总结

内容目录




















  1. 1OpenWrt背景知识

Linksys成立于1988年,2003年被思科收购,最著名的产品为WRT54G,为降低成本Linksys决定使用基于Linux操作系统的固件,由于版权原因,释放WRT54G/GS的源码,网上出现了很多不同版本的Firmware。流行的第三方路由固件,主要DDWrt,Tomato,OpenWrt三个。

DDWRT:界面美观,设置简单直观,易用性比较好,官方固件自带的功能是三大固件中最为丰富的,对新路由的支持也十分迅速,可支持迅速的同时,由于新代码的加入,导致DDWRT经常出现一些Bug,一些可能之前已经解决的Bug,往往又会在之后的某次更新中原地满状态复活,着实让人喷血,最让人呕病的,是孱弱的QOS功能,DDWRTQOS功能只能说聊胜于无吧。

Tomato:界面尚可,设置也算简单,易用性还行,流量的统计查看等功能是三个固件中做的最好的.官版Tomato是所有第三方路由固件中最为稳定的,它的稳定源自于它的保守,官方版本的Tomato好几个版本之前就已经没有再加入什么新功能,基本是对原有软件的升级和除Bug,让本来就稳定无比的Tomato更加稳定,可惜成也萧何败也萧何,Tomato的声誉来自于它的稳定,源自它的保守,它的缺点也来自于它的保守,由于这三大固件都源于思科当初开放的路由源码,所以对思科以及和思科一样采用BCM54M芯片的机子的支持程度是最好的

Openwrt:从零开始,一点一点的把各软件加入进去,使其接近LinksysFirmware的功能。是三大固件中扩展性最好的固件,通过扩展,可以实现很多超过路由本职工作的事情,比如脱机下载,网站,论坛,QOS功能也十分强大,不逊色于TomatoQOS,设置得当后网络表现会让人偷着乐,而且OpenWrt对新路由的支持也是十分迅速,被认为是最有前途的固件,因为可以在新路由强劲CPU的支持下获得很好的性能表现,各种功能的软件扩展包比比皆是。

OpenWrt可以被描述为一个嵌入式的Linux发行版,而不是试图建立一个单一的、静态的系统。OpenWrt的包管理提供了一个完全可写的文件系统。对于开发人员,OpenWrt是使用框架来构建应用程序,而无需建立一个完整的固件来支持;对于用户来说,这意味着其拥有完全定制的能力,可以用前所未有的方式使用该设备。

  1. 2OpenWrt 基础知识

关于Openwrt的源代码下载,途径有二,一是通过svn,一是通过git,建议使用svn,因为Openwrt主要以svn来维护Openwrt系统的版本。另外,请注意Openwrt中不同的分支版本,一个是用得较多的开发快照,俗称trunk,二是稳定版,俗称backfire

  1. 2.1目录结构


有几个重要目录:package,target, build_dir, staging_dir, bin, dl

上图是openwrt的基本的目录结构,其中白色部分是直接下载源码就有的源目录,蓝色部分则是通过make之后产生的目录。


tooltoolchain:是编译固件image,获取内核头文件,二进制编译器和调试器,c库文件,需要用到的通用工具。

include:存放*.mk文件,这里的文件是在Makefile里被include

scriptsopenwrt的包进行管理的perl脚本,存放各类脚本的目录。比如:feeds脚本

target:里面是各个平台(arch)的相关代码

package:里面包含了我们在配置文件里设定的所有编译好的软件包。默认情况下,会有默认选择的软件包。

bin:用来存放生成的ipk包,固件。

build_dir:目录编译时的临时目录,进行包的解压,编译和打补丁等。

staging_dir:是工具链的安装位置

dl:是'download'的缩写,编译前期,需要从网络下载的数据包都会放在这个目录下,这些软件包的一个特点就是,会自动安装在所编译的固件中,也就是我们makemenuconfig的时候,为固件配置的一些软件包。如果我们需要更改这些源码包,只需要将更改好的源码包打包成相同的名字放在这个目录下,然后开始编译即可。编译时,会将软件包解压到build_dir目录下。

feeds是通过命令./scripts/feedsupdate -a; ./scripts/feeds install-a之后安装的扩展包目录,这个目录将所有的文件链接到package/feeds/中去了,里面存放的就是按feeds.conf.default文件中列举的要处理的文件,所展开得到的目录。目录中存放的东西,和package目录中大致相似,指导如何下载和编译对应模块的。


    1. 2.2扩展软件包feeds


如果缺少一些应用程序可以是用feeds管理工具来安装,feeds即为包含到你的OpenWrt环境中的额外软件包,有一个用perl写的脚本(feeds)放在scripts目录下,会根据feeds.config.default文件来进行相关的操作,feeds命令有:



./scripts/feedsupdate

./scripts/feedslist

./scripts/feedsinstall

./scripts/feedsuninstall

./scripts/feedssearch

./scripts/feessclean


feed.config.default文件中有:

src-gitpackages https://github.com/openwrt/packages.git;for-15.05

src-gitluci https://github.com/openwrt/luci.git;for-15.05

src-linkluci ../qca/feeds/luci

src-svnxwrt http://x-wrt.googlecode.com/svn/trunk/package


src-svnphone svn://svn.openwrt.org/openwrt/feeds/phone

.....

.....


一般情况,你至少需要含packagesfeeds,其他可根据需求下载、安装feeds

*packages 提供众多库、工具等基本功能;也是其他feed所依赖的软件源,因此在安装其他feed前一定要先安装packages

*luci OpenWrt默认的GUIWEB管理界面)


比如先安装luci先要安装packages包:

$./scripts/feeds install -p packages -a

$./scripts/feeds install -p luci -a


举例:

下载、更新完feeds

$./scripts/feeds update -a


要安装feeds.conf中定义的全部feed

$./scripts/feeds install -a


安装某个feed的全部,

$./scripts/feeds install -p luci -a


搜索安装所需的软件包,这时候可以搜索安装相关的软件包,例如安装和蓝牙有关的软件包:

$./scripts/feeds search bluetooth

Searchresults in feed 'packages':

anyremote A bluetooth remote control app

bemusedlinuxserver Bemused linux server

bluez-hcidump Bluetooth packet analyzer

bluez-libs Bluetooth library

bluez-utils Bluetooth utilities

miax A console iax (asterisk) client

python-bluez Python wrapper for the BlueZ Bluetooth stack


比如需要安装bluez-libsbluez-utils这两个包,可以直接安装他们:

$./scripts/feeds install bluez-libs bluez-utils

Collectingpackage info: done

Collectingtarget info: done

Installingpackage 'bluez-libs'

Installingpackage 'gettext'

Installingpackage 'libiconv'

Installingpackage 'bluez-utils'

Installingpackage 'dbus'

Installingpackage 'expat'

Installingpackage 'gettext-full'

Installingpackage 'libiconv-full'

feeds已经相当智能了,能够根据软件包间的依赖关系,自动把所依赖的软件包也一同安装了


    1. 2.3OpenWrt SDK

如果要编译应用程序可以在OpenWrt源码目录下,也可以使用OpenWrtSDK。在makemenuconfig 是选择 [*]Build the OpenWrtSDK,在bin目录下生成,OpenWrt-SDK-ar71xx-xxxxx-0.9.33.2.tar.bz2包,目录结构与OpenWrt结构基本相同,应用程序在SDK下编译就可以,里面有需要的所有依赖库和交叉编译工具。


新建一个helloworldproject举例:

OpenWrt-SDK/package$mkdir helloworld

OpenWrt-SDK/package/helloworld$ls

Makefile src

OpenWrt-SDK/package/helloworld$ls src

helloworld.c Makefile

橙色的MakefileOpenWrtMakefile,下面是模板:

##############################################


#OpenWrt Makefile for helloworld program


#


#


#Most of the variables used here are defined in


#the include directives below. We just need to


#specify a basic description of the package,


#where to build our program, where to find


#the source files, and where to install the


#compiled program on the router.


#


#Be very careful of spacing in this file.


#Indents should be tabs, not spaces, and


#there should be no trailing whitespace in


#lines that are not commented.


#


##############################################


include$(TOPDIR)/rules.mk


#Name and release number of this package


PKG_NAME:=helloworld


PKG_RELEASE:=1






#This specifies the directory where we're going to build the program.


#The root build directory, $(BUILD_DIR), is by default thebuild_mipsel


#directory in your OpenWrt SDK directory


PKG_BUILD_DIR:= $(BUILD_DIR)/$(PKG_NAME)






include$(INCLUDE_DIR)/package.mk



#Specify package information for this program.


#The variables defined here should be self explanatory.


#If you are running Kamikaze, delete the DESCRIPTION


#variable below and uncomment the Kamikaze define


#directive for the description below


definePackage/helloworld


SECTION:=utils


CATEGORY:=Utilities


TITLE:=Helloworld-- prints a snarky message


endef




#Uncomment portion below for Kamikaze and delete DESCRIPTION variableabove


definePackage/helloworld/description


Ifyou can't figure out what this program does, you're probably


brain-deadand need immediate medical attention.


endef



#Specify what needs to be done to prepare for building the package.


#In our case, we need to copy the source files to the build directory.


#This is NOT the default. The default uses the PKG_SOURCE_URL and the


#PKG_SOURCE which is not defined here to download the source from theweb.


#In order to just build a simple program that we have just written, itis


#much easier to do it this way.


defineBuild/Prepare


mkdir-p $(PKG_BUILD_DIR)


$(CP)./src/* $(PKG_BUILD_DIR)/


endef






#We do not need to define Build/Configure or Build/Compile directives


#The defaults are appropriate for compiling a simple program such asthis one






#Specify where and how to install the program. Since we only have onefile,


#the helloworld executable, install it by copying it to the /bindirectory on


#the router. The $(1) variable represents the root directory on therouter running


#OpenWrt. The $(INSTALL_DIR) variable contains a command to preparethe install


#directory if it does not already exist. Likewise $(INSTALL_BIN)contains the


#command to copy the binary file from its current location (in ourcase the build


#directory) to the install directory.


definePackage/helloworld/install


$(INSTALL_DIR)$(1)/bin


$(INSTALL_BIN)$(PKG_BUILD_DIR)/helloworld $(1)/bin/


endef






#This line executes the necessary commands to compile our program.


#The above define directives specify all the information needed, butthis


#line calls BuildPackage which in turn actually uses this informationto


#build a package.


$(eval$(call BuildPackage,helloworld))




src目录下的Makefile为:


#build helloworld executable when user executes "make"


helloworld:helloworld.o


$(CC)$(LDFLAGS) helloworld.o -o helloworld


helloworld.o:helloworld.c


$(CC)$(CFLAGS) -c helloworld.c


#remove object files and executable when user executes "makeclean"


clean:


rm*.o helloworld



编译命令为:

OpenWrt-SDK$makeV=99 package/helloworld/compile

成功后会在bin目录下生成helloworld_1_ar71xx.ipk,在板子上是用opkg命令安装。

opkginstall helloworld_1_ar71xx.ipk 就可以安装到板子上。


Opkg是一个轻量快速的套件管理系统,目前已成为Opensource界嵌入式系统标准。常用于路由、交换机等嵌入式设备中,用来管理软件包的安装升级与下载。


常用命令

opkgupdate 更新可以获取的软件包列表

opkgupgrade 对已经安装的软件包升级

opkglist 获取软件列表

opkginstall 安装指定的软件包

opkgremove 卸载已经安装的指定的软件包


    1. 2.4固件升级

Thegeneric Flash layout is:


Layer0

rawflash

Layer1

bootloader
partition(s)

optional
SoC
specific
partition(s)

OpenWrtfirmware partition

optional
SoC
specific
partition(s)

Layer2

LinuxKernel

rootfs
mounted:"
/",OverlayFSwith/overlay

Layer3

/dev/root
mounted: "
/rom",SquashFS
sizedepends on selected packages

rootfs_data
mounted:"
/overlay",JFFS2
"free"space



/overlay/rom共同构成统一的/根目录,rootfs_data使用overlayFS,可读写。

源码编译后会在bin目录下生成固件包,对应上图Layer1中的OpenWrtfirmware partition“

openwrt-ar71xx-generic-ap152-16M-squashfs-sysupgrade.bin

三种方式:网页升级,uboot进入系统使用sysupgrade命令。

  1. 3OpenWrt内部机制

    1. 3.1UCIunifiedconfiguration Interface

Linux下的各种软件包有不同的配置脚本,每个脚本的语法格式和操作方式不同,这样的设计虽然体现了各个软件自身的优势,但同时也增加了学习难度,所以OpenWrt引入了一套配置参数管理系统,就是UCIunifiedconfiguration Interface),意在OpenWrt整个系统的配置集中化。

UCI是一个用C语言编写的小应用程序(也可用作一个shell脚本封装器),其开发目的是集中管理一个OpenWrt设备内的所有配置选项。UCI是对OpenWrt历史版本WhiteRussianNVRAM基础配置管理系统的一个继承,是一个对众多应用程序自带的标准配置文件的包装,比如像:/etc/network/interfaces,/etc/exports,/etc/dnsmasq.conf,/etc/samba/samba.conf等。你可以用任意文本编辑器或命令行应用程序uci来修改它们,也可通过各种编程API(如Shell,LuaC)来修改它们,配置位置放在/etc/config下。如果要开机读取/etc/config下面的配置文件进行相应的设置,需要在/etc/init.d下面增加启动脚本。


UCI命令行工具:

uci:invalid option -- h


Usage:uci [<options>] <command> [<arguments>]




Commands:


batch


export [<config>]


import [<config>]


changes [<config>]


commit [<config>]


add <config> <section-type>


add_list <config>.<section>.<option>=<string>


del_list <config>.<section>.<option>=<string>


show [<config>[.<section>[.<option>]]]


get <config>.<section>[.<option>]


set <config>.<section>[.<option>]=<value>


delete <config>[.<section>[[.<option>][=<id>]]]


rename <config>.<section>[.<option>]=<name>


revert <config>[.<section>[.<option>]]


reorder <config>.<section>=<position>



UCI的依赖项

libuci一个用C语言写的UCI的小程序库

libuci-lua 一个Lua语言的插件,可被像LuCI的程序使用.


举例子:/etc/config/network


root@OpenWrt:/#cat /etc/config/network




configinterface 'loopback'


optionifname 'lo'


optionproto 'static'


optionipaddr '127.0.0.1'


optionnetmask '255.0.0.0'




configglobals 'globals'


optionula_prefix 'auto'




configinterface 'wan0'


optionifname 'eth1'


optionproto 'dhcp'




configinterface 'lan0'


optionifname 'eth0_1 eth0_2 eth0_3 eth0_4'


optiontype 'bridge'




configinterface 'lan0_zone0'


optionifname 'br-lan0'


optionproto 'static'


optionipaddr '192.168.1.1'


optionnetmask '255.255.255.0'



root@OpenWrt:/#uci show network


network.loopback=interface


network.loopback.ifname='lo'


network.loopback.proto='static'


network.loopback.ipaddr='127.0.0.1'


network.loopback.netmask='255.0.0.0'


network.globals=globals


network.globals.ula_prefix='auto'


network.wan0=interface


network.wan0.ifname='eth1'


network.wan0.proto='dhcp'


network.lan0=interface


network.lan0.ifname='eth0_1eth0_2 eth0_3 eth0_4'


network.lan0.type='bridge'


network.lan0_zone0=interface


network.lan0_zone0.ifname='br-lan0'


network.lan0_zone0.proto='static'


network.lan0_zone0.ipaddr='192.168.1.1'


network.lan0_zone0.netmask='255.255.255.0'



api函数:

uci_lookup_ptr()

uci_foreach_element()

uci_to_section()

uci_lookup_option_string()

uci_set()

uci_commit()

...

...



如果你想在OpenWrt之外使用libuci(例如:您正在计算机上用C语言开发一个应用程序),下载源码生成库,编译连接

$(CC)test.o -o test -luci


注意:所有的uciset,uci add,uci renameucidelete命令都是在/tmp中实现,并用ucicommit命令立即写入flash.


    1. 3.2ubus

ubus是为了OpenWrt中守护进程和应用程序之间通讯开发的,类似桌面的DBus,设计理念上与DBus基本保持一致,区别是简化的API和简练的模型,以适应embeddedrouter的特殊环境。与DBus一样也是使用socket实现。

核心部分是ubusd守护进程,它提供了其他守护进程将自己注册以及发送消息的接口。因为这个,接口通过使用Unixsocket来实现,并使用TLV(type-length-value)消息,ubus内部使用Blob_buf,Blob_attr等结构来表示。

ubus有两种调用,一个是method调用,一个是notification,其中method包括等待函数返回和不用等待返回,notification是广播和DBussignal类似。ubus使用是先建立连接,然后把连接加入epollset中。下面是它的一些调用API

uloop_init() 创建epoll句柄,最多监听32fd

ubus_connect(); 创建ubus连接

ubus_add_uloop(); 把创建的ubus连接注册到epoll中。

ubus_add_object(); 注册对象到的ubus连接。

uloop_run(); 等待I/O事件发生,调用相对应的对象的功能函数。

ubus_free(); 关闭ubus连接

uloop_done(); 关闭epoll句柄


ubus调试有一个命令行工具叫ubusubus可以和ubusd服务器交互(和当前所有已经注册的服务).它对研究和调试注册的命名空间以及编写脚本非常有用。可以调用带参数和返回信息的方法,它使用友好的JSON格式。JSONJavaScriptObjects Notation)是一种轻量级的数据交换方式,JSONhumannice type,有两种数据结构对象和数组。

对象: {“firstname”Brett”lastname”Mical”}

数组:[“aaa”,“bbb”, “ccc”]


下面是它的命令说明。

ubus Commands:


-list [<path>] List objects


-call <path> <method> [<message>] Call an objectmethod


-listen [<path>...] Listen for events


-send <type> [<message>] Send an event


-wait_for <object> [<object>...] Wait for multipleobjects to appear on


ubuslist缺省列出所有向服务器注册的命名空间,如果调用时包含参数-v,将会显示指定命名空间更多方法参数等信息。

ubuscall 调用指定命名空间中指定的方法,并且通过消息传递给它,消息参数必须是有效的JSON字符串,并且携带函数所要求的键及值。

Ubuslisten 设置一个监听socket并观察进入的事件。

Ubussend 发送一个事件提醒。

root@uplink:~#ubus listen &


举例:

server.c

structblobmsg_policy discovery_pair_device_policy[] = {

[0]= { .name = "mac", .type = BLOBMSG_TYPE_STRING },

};

intdiscovery_pair_device_policy_sz = ARRAY_SIZE(discovery_pair_device_policy );

staticstruct ubus_method discovery_methods[] = {

UBUS_METHOD_NOARG("broadcast", discovery_broadcast ),

UBUS_METHOD_NOARG("get_devices", discovery_get_devices ),

UBUS_METHOD("pair_device", discovery_pair_device,discovery_pair_device_policy ),

UBUS_METHOD_NOARG("get_paired_devices", discovery_get_paired_devices ),

UBUS_METHOD("get_pairing_status", discovery_get_pairing_status,discovery_get_pairing_status_policy ),

};

staticstruct ubus_object_type discovery_object_type = UBUS_OBJECT_TYPE("controller.discovery", discovery_methods );


staticstruct ubus_object discovery_object = {

.name= "controller.discovery",

.type= &discovery_object_type,

.methods= discovery_methods,

.n_methods= ARRAY_SIZE( discovery_methods ),

};


int main( int argc, char** argv)

{

structubus_context* ubus_context;

intret;

uloop_init();

ubus_context= ubus_connect( NULL );

if( !ubus_context )

{

fprintf(stderr, "Failed to connect to ubus\n" );

gotoout;

}

ubus_add_uloop(ubus_context );

ret= ubus_add_object( ubus_context, &discovery_object );

if( ret ) {

pr_debug("Failed to add discovery object: %s\n", ubus_strerror( ret) );

}

uloop_run();


ubus_free(ubus_context );

uloop_done();


out:

return0;

}

//=============================================================================

int

discovery_pair_device(

structubus_context* ubus_context,

structubus_object* obj,

structubus_request_data* req,

constchar* method,

structblob_attr* msg

)

//=============================================================================

{

structdevice_descriptor* node;

structblob_attr* args[1];

intstatus = 0;

charmac[AP_ID_LEN + 1];


blobmsg_parse(

discovery_pair_device_policy,

discovery_pair_device_policy_sz,

args,

blob_data(msg ),

blob_len(msg ) );


if( args[0] ) {

memset(mac, 0, sizeof ( mac ) );

strncpy(mac, blobmsg_data( args[0] ), sizeof ( mac ) - 1 );

}

else

gotoerror;


//dosomething and set status


blob_buf_init(&reply, 0 );

blobmsg_add_u32(&reply, "status", status );

ubus_send_reply(ubus_context, req, reply.head );


returnUBUS_STATUS_OK;

}


client程序

staticstruct ubus_context *ctx;

staticstruct blob_buf b;

int main(int argc, char **argv)

//=============================================================================

{

constchar *progname;

intoption_index;

intc;

charbuf[80];


progname= argv[0];

uint32_tid;

ubus_ctx= ubus_connect( NULL );

if(!ubus_ctx ) {

fprintf(stderr, "Couldn't connect to UBUS.\n");

return0;

}

if(ubus_lookup_id( ubus_ctx, "controller.discovery", &id )) {

fprintf(stderr, "Couldn't find controller.discovery.\n");

return0;

}


blob_buf_init(&b, 0 );

if( argc == 1 ) return print_usage( progname );

while( (c = getopt_long( argc, argv, "hdlp:s:", long_options,&option_index)) != -1 ) {

switch( c ) {

case'd':

ubus_invoke( ubus_ctx, id, "broadcast", b.head,print_json, NULL, 1000 );

return 1;

case'p':

sprintf( buf, "{ \"mac\" : \"%s\" }",optarg );

blobmsg_add_json_from_string( &b, string_tolower(buf) );

ubus_invoke( ubus_ctx, id, "pair_device", b.head,print_json, NULL, 1000 );

return 1;

default:

return print_usage( progname );

}

}

return0;

}


ubus命令执行:

root@OpenWrt:/#ubus list controller.discovery -v


'controller.discovery'@1cabe8bf


"broadcast":{}


"get_devices":{}


"pair_device":{"mac":"String"}


"get_paired_devices":{}


"get_pairing_status":{"mac":"String"}



root@OpenWrt:/#ubus call controller.discovery pair_device '{"mac":"00010203040


506"}'


{


"status":"(error)"


}




notification函数有:

ubus_notify();

ubus_notify_async();

ubus_register_subscriber();

ubus_subscribe(

ubus_unsubscribe();

  1. 4LuCI

OpenWrt利用uhttpd作为web服务器,OpenWrt使用LuCI实现客户端web页面配置功能。LuCI采用了MVC(模型-视图-控制)三层架构,同时其使用Lua脚本,在/usr/lib/lua/luci下面对应有modelviewcontroller目录。开发LuCI的配置界面一般不需要编辑任何的Html代码,除非想自己单独去创建网页(View层)。

资源目录放在/www目录下面,index.html指向了目录下的cgi-bin/luci。对于request处理方式,采用的是cgi,而所用的cgi程序就是luci。调用工作框架如下图所示:











Client端和server端采用cgi方式交互,uhttpd服务器的cgi方式中,fork出一个子进程,子进程利用execl替换为luci进程空间,并通过setenv环境变量的方式,传递一些固定格式的数据(如PATH_INFO)给luci。另外一些非固定格式的数据(post-data)则由父进程通过一个w_pipe写给lucistdin,而luci的返回数据则写在stdout上,由父进程通过一个r_pipe读取。

LuCI的配置文件默认放在/etc/config目录下,如ip网关,等信息,可以使用UCIunifiedconfiguration Interface)对文件进行读写,有命令和api接口两种方式,但修改,删除操作后需要执行ucicommit,否则不会保存到文件中。


LuCI的安装两种方式:


a.使用OpenWrt源:

*转到OpenWrt根目录,编辑feed.config.default打开luci源。

*输入./scripts/feedsupdate

*输入./scripts/feedsinstall -a -p luci

*输入makemenuconfig

*LuCI”菜单下你将找到所有的组件。

*make V=99


b.使用OpenWrt安装包版本库:

*添加一行文字到你的/etc/opkg.conf中,即将LuCI添加到版本库中:

srclucihttp://downloads.openwrt/kamikaze/8.09.2/YOUR_ARCHITECTURE/packages

*输入opkg update

*LuCI简版,输入:opkg install luci-light

LuCI普通版:opkg install luci


  1. 5参考

https://wiki.openwrt.org/doc/techref/start

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenWrt的学习和总结 的相关文章

  • LVGL 源码分析大全

    LVGL 源码分析大全目录 1 概述 2 已完成源码分析文章列表 2 1 硬件抽像层 hal 2 2 核心框架 core misc 2 3 定制功能 2 4 内部接口 2 5 案例讲解 2 6 其它 3 样式 4 组件 5 主题 6 开发环
  • Linux的10个最危险的命令

    Linux命令行佷有用 很高效 也很有趣 但有时候也很危险 尤其是在你不确定你自己在正在做什么时候 这篇文章将会向你介绍十条命令 但你最好不要尝试着去使用 当然 以下命令通常都是在root权限下才能将愚蠢发挥到无可救药 在普通用户身份下 破
  • Docker关闭容器命令(docker stop)

    关闭容器 一定要是容器的名称 也就是 NAMES 下面的名称 关闭容器 docker stop 容器名称 非root用户 关闭容器 sudo docker stop 容器名称
  • Android Studio在build时一直停在:gradle: download maven-metadata.xml

    AndroidStudio在gradle build时 一直停在 gradle download maven metadata xml 解决 如果使用阿里云maven 看看使用的是不是旧版的maven 如果是 则更新为新版本的仓库地址 新版
  • Ubuntu18.04 安装速腾聚创最新驱动RSLidar_SDK采集XYZIRT格式的激光点云数据 --SLAM不学无术小问题

    Ubuntu18 04 安装速腾聚创最新驱动RSLidar SDK采集XYZIRT格式的激光点云数据 新款驱动支持RS16 RS32 RSBP RS128 RS80 RSM1 B3 RSHELIOS等型号 注意 该教程旨在引导安装 可能现在
  • C#相等性比较

    本文阐述C 中相等性比较 其中主要集中在下面两个方面 和 运算符 什么时候它们可以用于相等性比较 什么时候它们不适用 如果不使用 那么它们的替代方式是什么 什么时候 需要自定一个类型的相等性比较逻辑 在阐述相等性比较 以及如何自定义相等性比
  • npm常用命令

    一 npm更新所有依赖最新版本 安装组件 npm install g npm check updates 查看所有依赖最新版本 ncu 更新所有依赖到最新版本 ncu u 二 查看单个依赖版本 npm info 依赖包名称 version
  • 【ChatGPT本地部署-- ChatGLM】

    这里写自定义目录标题 ChatGPT本地部署 ChatGLM 转载 一 什么是ChatGLM 二 本地部署 三 模型与ChatGPT和GPT4AII 效果对比 ChatGPT本地部署 ChatGLM 转载 目录 一 什么是ChatGLM 二
  • golang cli_Go CLI教程:财富克隆

    golang cli I ve written two CLI app tutorials to build gololcat and gocowsay In both I used fortune as the input generat
  • 解决smplayer中文字幕乱码

    首先 打开选项 gt 首选项 选择字幕选项卡 找到 默认字符编码 选项 在下拉框中选择 简体中文 cp936 再打开 字体和颜色 页卡 上边 选择 系统字体 在下拉选框中选择一种简体中文字体 转载于 https www cnblogs co
  • 追忆我那为之奋斗了5年的地方

    虽然已经下定决心离开 但是当邮件发出那一刻 我的手还是忍不住的发抖 心跳在不停的加速 这毕竟是我工作了5年的地方 这里有我熟悉的面孔 直到过了几分钟 Boss回了个信息 过来下 我才深吸一口气 缓缓的走向他的办公室 追忆 初入职顺利过关斩将
  • Linux 编译安装 openssl库

    Linux 编译安装 openssl库 如果是不需要特定版本的openssl库的安装非常简单 直接sudo apt install opensll即可 而且像Ubuntu这种应该是自带了openssl库的 运行openssl version
  • vmware启动虚拟机黑屏解决办法

    以管理员身份在命令提示符窗口中输入 netsh winsock reset 然后重启计算机即可解决
  • stm32每周学习报告2.0

    STM32 通用定时器简介 STM32 的通用定时器是一个通过可编程预分频器 PSC 驱动的 16 位自动装载计数器 CNT 构成 STM32 的通用定时器可以被用于 测量输入信号的脉冲长度 输入捕获 或者产生输出波 形 输出比较和 PWM
  • SQL之视图、变量、存储过程、函数

    视图 虚拟表 和正常表一样使用 视图的好处 修改视图 方式一 视图不存在就创建 存在就替换 create or replace view name as select 方式二 alter view name as select 删除视图 d
  • 简单运行C程序的步骤和方法

    1 安装C Free程序 如下图可见 2 新建一个源程序 点击左上角 文件 然后选择新建 进行命名 3 开始编译程序 以This is a C program 为例 4 点击编译 并开始运行 如出现错误和警告 系统会给予提醒 改正后就可以运
  • C语言函数大全-- v 开头的函数

    v 开头的函数 1 va start 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 va arg 2 1 函数说明 2 2 演示示例 3 va copy 3 1 函数说明 3 2 演示示例 4 va end 4 1 函数说明 4
  • 【Clion+CubeMX开发STM32】(三)为你的工程创建GIT远程仓库

    目录 下载安装git 链接github远程仓库 上传 下载安装git 网上已经有很多git的安装教程 本文就不再赘述了 推荐一个链接 下载安装Git链接 链接github远程仓库 一 注册你的Git账号并登录 二 创建远程仓库 填写仓库名
  • 小程序云开发多表查询

    原文链接 https juejin im post 5baadb086fb9a05ce2740968 关联表学习 文中代码并不是实际代码 伪代码不可直接运行 功能 用户 喜欢 文章 表 用户表 users id username 唯一标识

随机推荐

  • Java常用配置项和命令行

    JVM配置项说明 1 java虚拟机可配置参数整理 java参数配置参数分成三类 标准参数 开头 如 version 非标准参数 X开头 非稳定参数 XX开头 第一部分 JMM配置参数 Xmn 新生代大小 Xms 初始内存大小 Xmx 堆最
  • 【Gale Shapley 婚姻稳定匹配算法实现】

    原理 所有男性按照好感的高低向对应女性求婚 每个女性在所有的向她发出求婚的男性和其丈夫 如果暂无丈夫则不做比较 选择一个最喜欢的 如果这个最喜欢的是当前的丈夫 则婚姻关系不变 否则与当前丈夫离婚 并与向她发出求婚请求的男性结婚 在每个女性处
  • facebook NLP 自然语言处理框架 Pytext 简介

    自然语言处理 NLP 在现代深度学习生态中越来越常见 从流行的深度学习框架到云端API的支持 例如Google云 Azure AWS或Bluemix NLP是深度学习平台不可或缺的部分 尽管已经取得了令人难以置信的进步 但构建大规模的NLP
  • 开源OLAP引擎测评报告(SparkSql、Presto、Impala、HAWQ、ClickHouse、GreenPlum) ...

    本文为博主公司原创文章 仿冒必究 转载请回复留言 开源OLAP引擎测评报告 SparkSql Presto Impala HAWQ ClickHouse GreenPlum 易观CTO 郭炜 序现在大数据组件非常多 众说不一 在每个企业不同
  • 大语言模型(LLM)及使用方法

    大语言模型 LLM Large Language Model 是一种基于深度学习的自然语言处理技术 它使用深度神经网络来学习自然语言的统计规律 以便能够自动地生成 理解和处理自然语言 LLM通常具有数亿个参数和数十亿个标记 能够处理大规模的
  • spring websocket 利用注解接收和发送消息

    websocket只定义了文字和字节俩种形式的消息格式 没有像http协议那样子有那么丰富的协议规范 我们看看http的协议格式 websocket之所以没有自己定义那么多的协议格式 是希望有框架自己来实现定义这些格式 我们称之为webso
  • RAC+Dataguard环境中JDBC Failover配置

    在rac dataguard的环境中 为了减少应用宕机时间及改动 提高业务的可用性 要求jdbc客户端配置failover 同时为了很好地做应用分割 又不能load balance 即在第一个实例不行时 去偿试第二个实例 两个实例都不行时
  • 安卓调试

    欢迎关注 全栈工程师修炼指南 公众号 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 花开堪折直须折 莫待无花空折枝 作者主页 https www weiyigeek top 博客 https b
  • python中一个函数调用另一个函数中的变量

    我们在一个函数func2 中想使用另一个函数func1 中的变量 通常会使用返回值的方法 但是在调用的时候 也会将func2 整体运行一遍 如果func2 函数体的运行对于func1 取返回值没有影响则完全可以 但是如果func2 函数体的
  • linux操作分析lab3

    内核准备 内核和相关环境 wget https raw github com mengning mykernel master mykernel 2 0 for linux 5 4 34 patch sudo apt install axe
  • 浅谈TCP/IP协议

    一 TCP IP协议 TCP 传输控制协议 IP 因特网互联协议 TCP IP 合称网络通讯协议 是Internet最基本的协议 Internet国际互联网络的基础 由网络层的IP协议和传输层的TCP协议组成 TCP IP 定义了电子设备如
  • c++链表对应节点相加

    题目 给定两个链表 分别表示两个非负整数 它们的数字逆序存储在链表中 并且每个节点只存储一个数字 计 算两个数的和 并且返回和的链表头指针 如 输入 3 gt 6 gt 9 2 gt 5 gt 7 输出 5 gt 1 gt 7 gt 1 思
  • 自己动手写线程池——向JDK线程池进发

    优质资源分享 学习路线指引 点击解锁 知识定位 人群定位 Python实战微信订餐小程序 进阶级 本课程是python flask 微信小程序的完美结合 从项目搭建到腾讯云部署上线 打造一个全栈订餐系统 Python量化交易实战 入门级 手
  • Java七大设计原则

    文章目录 一 单一职责原则 1 不遵守单一职责原则 2 遵守单一职责原则 二 接口隔离原则 编辑 三 依赖倒转原则 1 改进前 2 改进后 四 里氏替换原则 五 开闭原则OCP 六 迪米特法则 七 合成复用原则 八 UML类图 一 单一职责
  • form-group 两种常用使用

    用法一 运行结果如下 form group 增加盒子的下边界 form control 充满整个父元素 并且有换行作用 用法二 运行结果如下 control label 元素内实现包含内容右对齐 FR 海涛高软 QQ技术交流群 386476
  • java的反射

    一 反射的定义 基于 JDK8 Oracle官网对反射的解释是本文基于 JDK8 Oracle官网对反射的解释是 反射使 Java 代码可以发现有关已加载类的字段 方法和构造函数的信息 并在安全性限制内使用反射对这些字段 方法和构造函数进行
  • 熵,信息熵,香农熵,微分熵,交叉熵,相对熵

    2019 07 13 https blog csdn net landstream article details 82383503 https blog csdn net pipisorry article details 5169528
  • Java 实现二分法查找

    二分法 public class BinarySearch public static void main String args int array 1 5 8 11 19 22 31 35 40 45 48 49 50 int targ
  • Java面向对象,你真的会用吗?

    就在今天 自己写的一个C 项目 同事说用面向对象的思想写比较好 其实面向对象思想 感觉这个东西谁不知道啊 但是 确实这个程序没有使用 使用的好处是什么呢 又要如何使用呢 这是很值得思考的问题 面向对象简称OO Object Oriented
  • OpenWrt的学习和总结

    OpenWrt的学习和总结 内容目录 1OpenWrt背景知识 2 2OpenWrt 基础知识 2 2 1目录结构 2 2 2扩展软件包feeds 3 2 3OpenWrt SDK 4 2 4固件升级 8 3OpenWrt内部机制 8 3