OpenWrt KernelPackage分析
一. 前言
KernelPackage是OpenWrt用来编译内核模块的函数,其实KernelPackage后面会调用BuildPackage,这里会一块将BuildPackage也顺便分析,本文以gpio-button-hotplug驱动模块为例,讲解整个编译过程。
gpio-button-hotplug驱动编译命令如下:
make package/kernel/gpio-button-hotplug/compile V=s
二. 编译过程分析
1. 编译命令运行分析
由前面的文章介绍了subdir的作用后,我们可以知道,subdir会将make package/kernel/gpio-button-hotplug/compile V=s命令转换为如下命令:
make -C package/kernel/gpio-button-hotplug compile
make会去到package/kernel/gpio-button-hotplug下找Makfile,并且找其中的compile目标运行,gpio-button-hotplug的Makefile如下:
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mkPKG_NAME:=gpio-button-hotplug
PKG_RELEASE:=3
PKG_LICENSE:=GPL-2.0include $(INCLUDE_DIR)/package.mkdefine KernelPackage/gpio-button-hotplug
......
endef
......
MAKE_OPTS:= $(KERNEL_MAKE_FLAGS) M="$(PKG_BUILD_DIR)"define Build/Compile$(MAKE) -C "$(LINUX_DIR)" $(MAKE_OPTS) modules
endef$(eval $(call KernelPackage,gpio-button-hotplug))
可以看到,这里会调用到KernelPackage,KernelPackage内容如下:
include/kernel.mk:
define KernelPackageNAME:=$(1)$(eval $(call Package/Default))$(eval $(call KernelPackage/Defaults))$(eval $(call KernelPackage/$(1)))$(eval $(call KernelPackage/$(1)/$(BOARD)))$(eval $(call KernelPackage/$(1)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)))define Package/kmod-$(1)TITLE:=$(TITLE)SECTION:=kernelCATEGORY:=Kernel modulesDESCRIPTION:=$(DESCRIPTION)EXTRA_DEPENDS:=kernel (=$(LINUX_VERSION)-$(LINUX_RELEASE)-$(LINUX_VERMAGIC))VERSION:=$(LINUX_VERSION)$(if $(PKG_VERSION),+$(PKG_VERSION))-$(if $(PKG_RELEASE),$(PKG_RELEASE),$(LINUX_RELEASE))PKGFLAGS:=$(PKGFLAGS)$(call KernelPackage/$(1))$(call KernelPackage/$(1)/$(BOARD))$(call KernelPackage/$(1)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic))endefifdef KernelPackage/$(1)/conffilesdefine Package/kmod-$(1)/conffiles
$(call KernelPackage/$(1)/conffiles)endefendififdef KernelPackage/$(1)/descriptiondefine Package/kmod-$(1)/description
$(call KernelPackage/$(1)/description)endefendififdef KernelPackage/$(1)/configdefine Package/kmod-$(1)/config
$(call KernelPackage/$(1)/config)endefendif$(call KernelPackage/depends)$(call KernelPackage/hooks)ifneq ($(if $(filter-out %=y %=n %=m,$(KCONFIG)),$(filter m y,$(foreach c,$(filter-out %=y %=n %=m,$(KCONFIG)),$($(c)))),.),)define Package/kmod-$(1)/install@for mod in $$(call version_filter,$$(FILES)); do \if grep -q "$$$$$$$${mod##$(LINUX_DIR)/}" "$(LINUX_DIR)/modules.builtin"; then \echo "<<2>>" \echo "NOTICE: module '$$$$$$$$mod' is built-in."; \elif [ -e $$$$$$$$mod ]; then \echo "<<3>> $$$$$$$$mod" ; \mkdir -p $$(1)/$(MODULES_SUBDIR) ; \$(CP) -L $$$$$$$$mod $$(1)/$(MODULES_SUBDIR)/ ; \else \echo "ERROR: module '$$$$$$$$mod' is missing." >&2; \exit 1; \fi; \done;$(call ModuleAutoLoad,$(1),$$(1),$(filter-out 0-,$(word 1,$(AUTOLOAD))-),$(filter-out 0,$(word 2,$(AUTOLOAD))),$(sort $(wordlist 3,99,$(AUTOLOAD))))$(call KernelPackage/$(1)/install,$$(1))endef$(if $(CONFIG_PACKAGE_kmod-$(1)),elsecompile: $(1)-disabled$(1)-disabled:@echo "WARNING: kmod-$(1) is not available in the kernel config - generating empty package" >&2define Package/kmod-$(1)/installecho "<<1>>"trueendef)endif$$(eval $$(call BuildPackage,kmod-$(1)))$$(IPKG_kmod-$(1)): $$(wildcard $$(call version_filter,$$(FILES)))endef
细节先不分析,先找compile目标,可以看到KernelPackage下没有compile目标,原因如下:
ifneq ($(if $(filter-out %=y %=n %=m,$(KCONFIG)),$(filter m y,$(foreach c,$(filter-out %=y %=n %=m,$(KCONFIG)),$($(c)))),.),)
如上代码KCONFIG变量(package/kernel/linux/modules/*.mk才有定义)为空,所以以上表达式可以写为ifneq (.,),结果为真,执行上面的分支,上面分支没有compile目标。
此时,就要到BuildPackage里面寻找了。BuildPackage代码如下:
define BuildPackage$(eval $(Package/Default))$(eval $(Package/$(1)))ifdef DESCRIPTION
$$(error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description)
endififndef Package/$(1)/description
define Package/$(1)/description$(TITLE)
endef
endifBUILD_PACKAGES += $(1)$(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(1)))$(foreach FIELD, TITLE CATEGORY SECTION VERSION,ifeq ($($(FIELD)),)$$(error Package/$(1) is missing the $(FIELD) field)endif)$(if $(DUMP), \$(if $(CHECK),,$(Dumpinfo/Package)), \$(foreach target, \$(if $(Package/$(1)/targets),$(Package/$(1)/targets), \$(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \), $(BuildTarget/$(target)) \) \)$(if $(PKG_HOST_ONLY),,$(call Build/DefaultTargets,$(1)))
endef
这里也没有compile目标。我们分析里面如下代码:
$(if $(DUMP), \
$(if $(CHECK),,$(Dumpinfo/Package)), \
$(foreach target, \
$(if $(Package/$(1)/targets),$(Package/$(1)/targets), \
$(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \
), $(BuildTarget/$(target)) \
) \
)
以上代码不难分析,结果为$(BuildTarget/ipkg)。接下来分析BuildTarget/ipkg,代码如下:
define BuildTarget/ipkgABIV_$(1):=$(call FormatABISuffix,$(1),$(ABI_VERSION))PDIR_$(1):=$(call FeedPackageDir,$(1))IPKG_$(1):=$$(PDIR_$(1))/$(1)$$(ABIV_$(1))_$(VERSION)_$(PKGARCH).ipkIDIR_$(1):=$(PKG_BUILD_DIR)/ipkg-$(PKGARCH)/$(1)KEEP_$(1):=$(strip $(call Package/$(1)/conffiles))ifeq ($(BUILD_VARIANT),$$(if $$(VARIANT),$$(VARIANT),$(BUILD_VARIANT)))do_install=ifdef Package/$(1)/installdo_install=yesendififdef Package/$(1)/install-overlaydo_install=yesendififdef do_installifneq ($(CONFIG_PACKAGE_$(1))$(DEVELOPER),)IPKGS += $(1)$(_pkg_target)compile: $$(IPKG_$(1)) $(PKG_INFO_DIR)/$(1).provides $(PKG_BUILD_DIR)/.pkgdir/$(1).installedprepare-package-install: $$(IPKG_$(1))compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installedelse$(if $(CONFIG_PACKAGE_$(1)),$$(info WARNING: skipping $(1) -- package not selected))endif.PHONY: $(PKG_INSTALL_STAMP).$(1)ifeq ($(CONFIG_PACKAGE_$(1)),y)compile: $(PKG_INSTALL_STAMP).$(1)endif$(PKG_INSTALL_STAMP).$(1): prepare-package-installecho "$(1)" >> $(PKG_INSTALL_STAMP)else$(if $(CONFIG_PACKAGE_$(1)),$$(warning WARNING: skipping $(1) -- package has no install section))endifendifDEPENDS:=$(call PKG_FIXUP_DEPENDS,$(1),$(DEPENDS))IDEPEND_$(1):=$$(call filter_deps,$$(DEPENDS))IDEPEND += $$(patsubst %,$(1):%,$$(IDEPEND_$(1)))$(FixupDependencies)$(FixupReverseDependencies)$(eval $(call BuildIPKGVariable,$(1),conffiles))$(eval $(call BuildIPKGVariable,$(1),preinst,,1))$(eval $(call BuildIPKGVariable,$(1),postinst,-pkg,1))$(eval $(call BuildIPKGVariable,$(1),prerm,-pkg,1))$(eval $(call BuildIPKGVariable,$(1),postrm,,1))$(PKG_BUILD_DIR)/.pkgdir/$(1).installed : export PATH=$$(TARGET_PATH_PKG)$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@Package/$(1)/DEPENDS := $$(call mergelist,$$(foreach dep,$$(filter-out @%,$$(IDEPEND_$(1))),$$(dep)$$(call GetABISuffix,$$(dep))))ifneq ($$(EXTRA_DEPENDS),)Package/$(1)/DEPENDS := $$(EXTRA_DEPENDS)$$(if $$(Package/$(1)/DEPENDS),$$(comma) $$(Package/$(1)/DEPENDS))endif$(_define) Package/$(1)/CONTROL
Package: $(1)$$(ABIV_$(1))
Version: $(VERSION)
$$(call addfield,Depends,$$(Package/$(1)/DEPENDS)
)$$(call addfield,Conflicts,$$(call mergelist,$(CONFLICTS))
)$$(call addfield,Provides,$$(call mergelist,$$(filter-out $(1)$$(ABIV_$(1)),$(PROVIDES)$$(if $$(ABIV_$(1)), $(1) $(foreach provide,$(PROVIDES),$(provide)$$(ABIV_$(1))))))
)$$(call addfield,Alternatives,$$(call mergelist,$(ALTERNATIVES))
)$$(call addfield,Source,$(SOURCE)
)$$(call addfield,SourceName,$(1)
)$$(call addfield,License,$(LICENSE)
)$$(call addfield,LicenseFiles,$(LICENSE_FILES)
)$$(call addfield,Section,$(SECTION)
)$$(call addfield,Require-User,$(USERID)
)$$(call addfield,SourceDateEpoch,$(PKG_SOURCE_DATE_EPOCH)
)$$(if $$(ABIV_$(1)),ABIVersion: $$(ABIV_$(1))
)$(if $(PKG_CPE_ID),CPE-ID: $(PKG_CPE_ID)
)$(if $(filter hold,$(PKG_FLAGS)),Status: unknown hold not-installed
)$(if $(filter essential,$(PKG_FLAGS)),Essential: yes
)$(if $(MAINTAINER),Maintainer: $(MAINTAINER)
)Architecture: $(PKGARCH)
Installed-Size: 0
$(_endef)$$(IPKG_$(1)) : export CONTROL=$$(Package/$(1)/CONTROL)$$(IPKG_$(1)) : export DESCRIPTION=$$(Package/$(1)/description)$$(IPKG_$(1)) : export PATH=$$(TARGET_PATH_PKG)$$(IPKG_$(1)) : export PKG_SOURCE_DATE_EPOCH:=$(PKG_SOURCE_DATE_EPOCH)$(PKG_INFO_DIR)/$(1).provides $$(IPKG_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-ipkg.mk@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)$(call Package/$(1)/install,$$(IDIR_$(1)))$(if $(Package/$(1)/install-overlay),mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/rootfs-overlay)$(call Package/$(1)/install-overlay,$$(IDIR_$(1))/rootfs-overlay)-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides$(if $(PROVIDES),@for pkg in $(filter-out $(1),$(PROVIDES)); do cp $(PKG_INFO_DIR)/$(1).provides $(PKG_INFO_DIR)/$$$$pkg.provides; done)$(CheckDependencies)$(RSTRIP) $$(IDIR_$(1))ifneq ($$(CONFIG_IPK_FILES_CHECKSUMS),)(cd $$(IDIR_$(1)); \( \find . -type f \! -path ./CONTROL/\* -exec mkhash sha256 -n \{\} \; 2> /dev/null | \sed 's|\([[:blank:]]\)\./| \1/|' > $$(IDIR_$(1))/CONTROL/files-sha256sum \) || true \)endif(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)ifneq ($$(KEEP_$(1)),)@( \keepfiles=""; \for x in $$(KEEP_$(1)); do \[ -f "$$(IDIR_$(1))/$$$$x" ] || keepfiles="$$$${keepfiles:+$$$$keepfiles }$$$$x"; \done; \[ -z "$$$$keepfiles" ] || { \mkdir -p $$(IDIR_$(1))/lib/upgrade/keep.d; \for x in $$$$keepfiles; do echo $$$$x >> $$(IDIR_$(1))/lib/upgrade/keep.d/$(1); done; \}; \)endif$(INSTALL_DIR) $$(PDIR_$(1))$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))@[ -f $$(IPKG_$(1)) ]$(1)-clean:$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))clean: $(1)-cleanendef
里面一段一段分析,如下:
ifeq ($(BUILD_VARIANT),$$(if $$(VARIANT),$$(VARIANT),$(BUILD_VARIANT)))
do_install=
ifdef Package/$(1)/install
do_install=yes
endif
ifdef Package/$(1)/install-overlay
do_install=yes
endif
ifdef do_install
ifneq ($(CONFIG_PACKAGE_$(1))$(DEVELOPER),)
IPKGS += $(1)
$(_pkg_target)compile: $$(IPKG_$(1)) $(PKG_INFO_DIR)/$(1).provides $(PKG_BUILD_DIR)/.pkgdir/$(1).installed
prepare-package-install: $$(IPKG_$(1))
compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installed
else
$(if $(CONFIG_PACKAGE_$(1)),$$(info WARNING: skipping $(1) -- package not selected))
endif.PHONY: $(PKG_INSTALL_STAMP).$(1)
ifeq ($(CONFIG_PACKAGE_$(1)),y)
compile: $(PKG_INSTALL_STAMP).$(1)
endif
$(PKG_INSTALL_STAMP).$(1): prepare-package-install
echo "$(1)" >> $(PKG_INSTALL_STAMP)
else
$(if $(CONFIG_PACKAGE_$(1)),$$(warning WARNING: skipping $(1) -- package has no install section))
endif
endif
由于BUILD_VARIANT和VARIANT都未定义,都为空,自然相等,所以条件成立。
由于Package/$(1)/install定义在include/kernel.mk中,如下:
include/kernel.mk:
define Package/kmod-$(1)/install@for mod in $$(call version_filter,$$(FILES)); do \if grep -q "$$$$$$$${mod##$(LINUX_DIR)/}" "$(LINUX_DIR)/modules.builtin"; then \echo "NOTICE: module '$$$$$$$$mod' is built-in."; \elif [ -e $$$$$$$$mod ]; then \mkdir -p $$(1)/$(MODULES_SUBDIR) ; \$(CP) -L $$$$$$$$mod $$(1)/$(MODULES_SUBDIR)/ ; \else \echo "ERROR: module '$$$$$$$$mod' is missing." >&2; \exit 1; \fi; \done;$(call ModuleAutoLoad,$(1),$$(1),$(filter-out 0-,$(word 1,$(AUTOLOAD))-),$(filter-out 0,$(word 2,$(AUTOLOAD))),$(sort $(wordlist 3,99,$(AUTOLOAD))))$(call KernelPackage/$(1)/install,$$(1))endef
所以do_install变量的值是yes。以上的结果可以通过$(warning $(Package/$(1)/install))打印出来证实。
由于,do_install=yes,此时,可以找到compile目标了,这里有两个compile目标,两个compile目标会依次运行,compile目标和为什么这么运行的例子如下:
include/package-ipkg.mk:
compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installed
......
compile: $(PKG_INSTALL_STAMP).$(1)example:
act1:@echo act1;
act2:@echo act2;compile: act1
compile: act2all: compile@echo allresult:
act1
act2
all
2. 先来分析第一个compile目标
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed依赖于$(PKG_BUILD_DIR)/.pkgdir/$(1).installed,实现如下:
include/package-ipkg.mk:
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed依赖于STAMP_BUILT,实现如下:
include/package-ipkg.mk:
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@
STAMP_BUILT定义在Build/CoreTargets,Build/CoreTargets实现如下:
include/package.mk:
define Build/CoreTargetsSTAMP_PREPARED:=$$(STAMP_PREPARED)STAMP_CONFIGURED:=$$(STAMP_CONFIGURED)$(if $(QUILT),$(Build/Quilt))$(call Build/Autoclean)$(call DefaultTargets)$(DL_DIR)/$(FILE): FORCEdownload:$(foreach hook,$(Hooks/Download),$(call $(hook))$(sep))$(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)$(STAMP_PREPARED): $(STAMP_PREPARED_DEPENDS)@-rm -rf $(PKG_BUILD_DIR)@mkdir -p $(PKG_BUILD_DIR)touch $$@_check$(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))$(Build/Prepare)$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))touch $$@$(call Build/Exports,$(STAMP_CONFIGURED))$(STAMP_CONFIGURED): $(STAMP_PREPARED) $(STAMP_CONFIGURED_DEPENDS)rm -f $(STAMP_CONFIGURED_WILDCARD)$(CleanStaging)$(foreach hook,$(Hooks/Configure/Pre),$(call $(hook))$(sep))$(Build/Configure)$(foreach hook,$(Hooks/Configure/Post),$(call $(hook))$(sep))touch $$@$(call Build/Exports,$(STAMP_BUILT))$(STAMP_BUILT): $(STAMP_CONFIGURED) $(STAMP_BUILT_DEPENDS)rm -f $$@touch $$@_check$(foreach hook,$(Hooks/Compile/Pre),$(call $(hook))$(sep))$(Build/Compile)$(foreach hook,$(Hooks/Compile/Post),$(call $(hook))$(sep))$(Build/Install)$(foreach hook,$(Hooks/Install/Post),$(call $(hook))$(sep))touch $$@$(STAMP_INSTALLED) : export PATH=$$(TARGET_PATH_PKG)$(STAMP_INSTALLED): $(STAMP_BUILT)rm -rf $(TMP_DIR)/stage-$(PKG_DIR_NAME)mkdir -p $(TMP_DIR)/stage-$(PKG_DIR_NAME)/host $(STAGING_DIR)/packages$(foreach hook,$(Hooks/InstallDev/Pre),\$(call $(hook),$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(sep)\)$(call Build/InstallDev,$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(foreach hook,$(Hooks/InstallDev/Post),\$(call $(hook),$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(sep)\)if [ -f $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) ]; then \$(SCRIPT_DIR)/clean-package.sh \"$(STAGING_DIR)/packages/$(STAGING_FILES_LIST)" \"$(STAGING_DIR)"; \fiif [ -d $(TMP_DIR)/stage-$(PKG_DIR_NAME) ]; then \(cd $(TMP_DIR)/stage-$(PKG_DIR_NAME); find ./ > $(TMP_DIR)/stage-$(PKG_DIR_NAME).files); \$(call locked, \mv $(TMP_DIR)/stage-$(PKG_DIR_NAME).files $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) && \$(CP) $(TMP_DIR)/stage-$(PKG_DIR_NAME)/* $(STAGING_DIR)/; \,staging-dir); \firm -rf $(TMP_DIR)/stage-$(PKG_DIR_NAME)touch $$@ifdef Build/InstallDev$(_pkg_target)compile: $(STAMP_INSTALLED)endif$(_pkg_target)prepare: $(STAMP_PREPARED)$(_pkg_target)configure: $(STAMP_CONFIGURED)$(_pkg_target)dist: $(STAMP_CONFIGURED)$(_pkg_target)distcheck: $(STAMP_CONFIGURED)ifneq ($(CONFIG_AUTOREMOVE),)compile:-touch -r $(PKG_BUILD_DIR)/.built $(PKG_BUILD_DIR)/.autoremove 2>/dev/null >/dev/null$(FIND) $(PKG_BUILD_DIR) -mindepth 1 -maxdepth 1 -not '(' -type f -and -name '.*' -and -size 0 ')' -and -not -name '.pkgdir' | \$(XARGS) rm -rfendif
endefdefine Build/DefaultTargets$(if $(USE_SOURCE_DIR)$(USE_GIT_TREE)$(USE_GIT_SRC_CHECKOUT),,$(if $(strip $(PKG_SOURCE_URL)),$(call Download,default)))$(if $(DUMP),,$(Build/CoreTargets))define Build/DefaultTargetsendef
endefdefine BuildPackage
......$(if $(PKG_HOST_ONLY),,$(call Build/DefaultTargets,$(1)))
endef
其中
$(STAMP_BUILT): $(STAMP_CONFIGURED) $(STAMP_BUILT_DEPENDS)
rm -f $$@
touch $$@_check
$(foreach hook,$(Hooks/Compile/Pre),$(call $(hook))$(sep))
$(Build/Compile)
$(foreach hook,$(Hooks/Compile/Post),$(call $(hook))$(sep))
$(Build/Install)
$(foreach hook,$(Hooks/Install/Post),$(call $(hook))$(sep))
touch $$@
STAMP_BUILT依赖于STAMP_CONFIGURED和STAMP_BUILT_DEPENDS,STAMP_CONFIGURED实现如下:
$(STAMP_CONFIGURED): $(STAMP_PREPARED) $(STAMP_CONFIGURED_DEPENDS)
rm -f $(STAMP_CONFIGURED_WILDCARD)
$(CleanStaging)
$(foreach hook,$(Hooks/Configure/Pre),$(call $(hook))$(sep))
$(Build/Configure)
$(foreach hook,$(Hooks/Configure/Post),$(call $(hook))$(sep))
touch $$@
STAMP_CONFIGURED依赖于STAMP_PREPARED,STAMP_PREPARED实现如下:
(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)
$(STAMP_PREPARED): $(STAMP_PREPARED_DEPENDS)
@-rm -rf $(PKG_BUILD_DIR)
@mkdir -p $(PKG_BUILD_DIR)
touch $$@_check
$(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))
$(Build/Prepare)
$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))
touch $$@
经过这几个步骤,kmod-*.ipkg的代码就编译好了。步骤分别是BUild/Prepare,CleanStaging,Build/Configure,Build/Compile和Build/Install。
在之后,执行如下代码
include/package-ipkg.mk:
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed : export PATH=$$(TARGET_PATH_PKG)$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@
实际运行代码:
rm -rf /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules.d; ( echo "gpio-button-hotplug"; ) > /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules.d/30-gpio-button-hotplug; mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules-boot.d; ln -sf ../modules.d/30-gpio-button-hotplug /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules-boot.d/;
touch /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed
创建build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed文件。
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@
执行如下:
mkdir -p /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/stamp
SHELL= flock /root/mt7981/openwrt/tmp/.root-copy.flock -c 'cp -fpR /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/. /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/'
touch /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/stamp/.kmod-gpio-button-hotplug_installed
至此,第一个compile目标就分析完成了,接下来第二个compile主要是生成ipkg包。
3. 第二个compile分析
第二个compile代码如下:
include/package-ipkg.mk:
compile: $(PKG_INSTALL_STAMP).$(1)
$(PKG_INSTALL_STAMP).$(1)依赖于prepare-package-install,prepare-package-install代码如下:
include/package-ipkg.mk:
prepare-package-install: $$(IPKG_$(1))
$$(IPKG_$(1))的定义如下:
include/package-ipkg.mk:$$(IPKG_$(1)) : export CONTROL=$$(Package/$(1)/CONTROL)$$(IPKG_$(1)) : export DESCRIPTION=$$(Package/$(1)/description)$$(IPKG_$(1)) : export PATH=$$(TARGET_PATH_PKG)$$(IPKG_$(1)) : export PKG_SOURCE_DATE_EPOCH:=$(PKG_SOURCE_DATE_EPOCH)$(PKG_INFO_DIR)/$(1).provides $$(IPKG_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-ipkg.mk@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)$(call Package/$(1)/install,$$(IDIR_$(1)))$(if $(Package/$(1)/install-overlay),mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/rootfs-overlay)$(call Package/$(1)/install-overlay,$$(IDIR_$(1))/rootfs-overlay)-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides$(if $(PROVIDES),@for pkg in $(filter-out $(1),$(PROVIDES)); do cp $(PKG_INFO_DIR)/$(1).provides $(PKG_INFO_DIR)/$$$$pkg.provides; done)$(CheckDependencies)$(RSTRIP) $$(IDIR_$(1))ifneq ($$(CONFIG_IPK_FILES_CHECKSUMS),)(cd $$(IDIR_$(1)); \( \find . -type f \! -path ./CONTROL/\* -exec mkhash sha256 -n \{\} \; 2> /dev/null | \sed 's|\([[:blank:]]\)\./| \1/|' > $$(IDIR_$(1))/CONTROL/files-sha256sum \) || true \)endif(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)ifneq ($$(KEEP_$(1)),)@( \keepfiles=""; \for x in $$(KEEP_$(1)); do \[ -f "$$(IDIR_$(1))/$$$$x" ] || keepfiles="$$$${keepfiles:+$$$$keepfiles }$$$$x"; \done; \[ -z "$$$$keepfiles" ] || { \mkdir -p $$(IDIR_$(1))/lib/upgrade/keep.d; \for x in $$$$keepfiles; do echo $$$$x >> $$(IDIR_$(1))/lib/upgrade/keep.d/$(1); done; \}; \)endif$(INSTALL_DIR) $$(PDIR_$(1))$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))@[ -f $$(IPKG_$(1)) ]
逐行代码分析,
@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))
这句指令实际执行命令如下:
rm -rf /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug;
删除ipkg目录。
mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)
实际执行命令如下:
mkdir -p /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/CONTROL /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/pkginfo
新建ipkg以及其下面的目录。
$(call Package/$(1)/install,$$(IDIR_$(1)))
实际执行命令如下:
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules.d; ( echo "gpio-button-hotplug"; ) > /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules.d/30-gpio-button-hotplug; mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules-boot.d; ln -sf ../modules.d/30-gpio-button-hotplug /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules-boot.d/;
其上面的命令都在include/kernel.mk的Package/kmod-$(1)/install中的ModuleAutoLoad实现的。
-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides
实际执行命令如下:
find /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| xargs -r rm -rf
删除掉ipkg目录下一些无关的文件。
$(RSTRIP) $$(IDIR_$(1))
实际执行命令如下:
export CROSS="aarch64-openwrt-linux-musl-" NO_RENAME=1 ; NM="aarch64-openwrt-linux-musl-nm" STRIP="/root/mt7981/openwrt/staging_dir/host/bin/sstrip -z" STRIP_KMOD="/root/mt7981/openwrt/scripts/strip-kmod.sh" PATCHELF="/root/mt7981/openwrt/staging_dir/host/bin/patchelf" /root/mt7981/openwrt/scripts/rstrip.sh /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug
rstrip.sh: /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/lib/modules/5.4.203/gpio-button-hotplug.ko: relocatable
去掉ipkg中文件的符号表。
(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)
实际执行命令如下:
(cd /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/CONTROL; ( echo "$CONTROL"; printf "Description: "; echo "$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; ) > control; chmod 644 control; ( echo "#!/bin/sh"; echo "[ \"\${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; echo "[ -s "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_postinst \$0 \$@"; ) > postinst; ( echo "#!/bin/sh"; echo "[ -s "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_prerm \$0 \$@"; ) > prerm; chmod 0755 postinst prerm; )
生成ipkg下的control,postinst和prerm文件。
$(INSTALL_DIR) $$(PDIR_$(1))
实际执行命令如下:
install -d -m0755 /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages
创建bin下的packages目录。
$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))
实际执行命令如下:
/root/mt7981/openwrt/staging_dir/host/bin/fakeroot /root/mt7981/openwrt/scripts/ipkg-build -m "" /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages
Packaged contents of /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug into /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages/kmod-gpio-button-hotplug_5.4.203-3_aarch64_cortex-a53.ipk
调用ipkg-build命令生成ipkg文件。
echo "$(1)" >> $(PKG_INSTALL_STAMP)
实际执行命令如下:
echo "kmod-gpio-button-hotplug" >> /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/pkginfo/gpio-button-hotplug.default.install
三. 总结
1. kmod的ipkg和普通的应用程序的ipkg相同点
毫无疑问,它们都必须包含ipkg的基本元素,符合ipkg的规范,有CONTROL目录,CONTROL目录下有对应的control,postinst和prerm文件,还有其他的目录,例如内核驱动有etc和lib目录,应用程序有usr目录等等。
它们都要调用include/package.mk中的BuildPackage,再调用到include/package-ipkg.mk的BuildTarget/ipkg,BuildPackage的子功能定义编译ipkg源代码的一整套流程,Prepare,Configure,Compile和Install步骤,虽然两者区别是其中一个一些步骤会省略(Build/xxx为空),例如,kmod就不需要Configure。
2. kmod的ipkg和普通的应用程序的ipkg不同点
编译kmod和普通应用的代码方式不一样。这个就体现在Build/Compile中,kmod的Makefile需要在自己的Makefile中定义这个宏,表示如何编译kmod,而应用代码Build/Compile是不推荐自定义的。
安装的方式不一样。kmod需要etc/modules.d/这样的目录,而应用程序不需要,在kmod的安装中,include/package-ipkg.mk中$(call Package/$(1)/install,$$(IDIR_$(1)))会调用到include/kernel.mk中的Package/kmod-$(1)/install,而如果再有需要,由于KernelPackage/$(1)/install定义在Package/kmod-$(1)/install,kmod的Makefile可以自定义KernelPackage/$(1)/install,做一些特殊处理。但是普通应用程序要在自己的Makefile中定义Package/$(1)/install,做一些自己需要的操作。
相关文章:
OpenWrt KernelPackage分析
一. 前言 KernelPackage是OpenWrt用来编译内核模块的函数,其实KernelPackage后面会调用BuildPackage,这里会一块将BuildPackage也顺便分析,本文以gpio-button-hotplug驱动模块为例,讲解整个编译过程。 gpio-button-hotplug驱动编译…...
第 363 场 LeetCode 周赛题解
A 计算 K 置位下标对应元素的和 模拟 class Solution { public:int pop_cnt(int x) {//求x的二进制表示中的1的位数int res 0;for (; x; x >> 1)if (x & 1)res;return res;}int sumIndicesWithKSetBits(vector<int> &nums, int k) {int res 0;for (int i…...
ffplay源码解析-main入口函数
main入口函数 初始化 变量、缓存区、SDL窗口初始化等 int main(int argc, char **argv) {int flags;VideoState *is; // av_log_set_level(AV_LOG_TRACE);init_dynload();av_log_set_flags(AV_LOG_SKIP_REPEATED);parse_loglevel(argc, argv, options);/// av_log_set_le…...
这些Coding套路你不会还不知道吧?
对于一名程序员来说,编码进阶是成为优秀工程师非常重要的一步,它可以让我们更加熟练地掌握编程,深入理解数据结构和算法,从而更好地完成复杂的任务,提高工作效率。而我认为熟练使用设计模式就是编码进阶的最好方式之一…...
Spring Boot深度解析:快速开发的秘密
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
mysql数据库备份(mysqldump)
mysqldump命令备份数据 mysqldump -u root -p --databases 数据库1 数据库2 > xxx.sqlmysqldump常用操作示例 1. 备份全部数据库的数据和结构 mysqldump -uroot -p123456 -A > /data/mysqlbackup/mydb.sql2. 备份全部数据库的结构(加 -d 参数) …...
linux Nginx+Tomcat负载均衡、动静分离
linux NginxTomcat负载均衡、动静分离 1、Tomcat的基本介绍1.1Tomcat是什么?1.2Tomcat的构成组件1.3Tomcat的核心功能1.4Tomcat请求过程 2、Tomcat部署2.1安装tomcat2.2优化tomcat启动速度2.4主要目录说明 3、Tomcat 虚拟主机配置3.1创建fsj和mws项目目录和文件3.2修…...
ts 枚举类型原理及其应用详解
ts 枚举类型介绍 TypeScript的枚举类型是一种特殊的数据类型,它允许开发者为一组相关值定义一个共同的名称,使我们可以更清晰、更一致地使用这些值。 枚举类型在TypeScript中用enum关键字定义,每个枚举值默认都是数字类型,从0开…...
腾讯mini项目-【指标监控服务重构】2023-08-23
今日已办 进度和问题汇总 请求合并 feature/venus tracefeature/venus metricfeature/profile-otel-baserunner-stylebugfix/profile-logger-Syncfeature/profile_otelclient_enable_config 完成otel 开关 trace-采样metrice-reader 已经都在各自服务器运行,并接入…...
C- ssize_t size_t
size_t 和 ssize_t 都是在 C 和 C 的标准库中定义的数据类型,它们通常用于表示大小和长度。然而,它们有关键的区别。 size_t: 定义:size_t 是一个无符号整数类型,它是适合表示对象的大小的类型。在 POSIX 中,它也用于…...
ubuntu20.04 Supervisor 开机自启动脚本一文配置
前言: 最近发现一种非常好的开机启动服务方式,不光可以开机自启动,而且还可以进行开机节点的进程守护,这样大大确保了线程的稳定情况,这种服务甚至可以守护开机的进程,所以比之前设置 rc.local 开机自启动脚本一文配置节点好出很多,它甚至可以使用网页登录监管我开机自启…...
【面试刷题】——函数指针和指针函数
“函数指针”(function pointer)和 “指针函数”(pointer to function)是两个不同的概念,它们涉及到指针和函数的结合使用。 函数指针(Function Pointer): 函数指针是指向函数的指…...
目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)
目标分类 一、目标分类介绍1.1 二分类和多分类的区别1.2 单标签和多标签输出的区别 二、代码获取三、数据集准备四、环境搭建4.1 环境测试 五、模型训练六、模型测试6.1 多标签训练-单标签输出结果6.2 多标签训练-多标签输出结果 一、目标分类介绍 目标分类是一种监督学习任务…...
【100天精通Python】Day61:Python 数据分析_Pandas可视化功能:绘制饼图,箱线图,散点图,散点图矩阵,热力图,面积图等(示例+代码)
目录 1 Pandas 可视化功能 2 Pandas绘图实例 2.1 绘制线图 2.2 绘制柱状图 2.3 绘制随机散点图 2.4 绘制饼图 2.5 绘制箱线图A 2.6 绘制箱线图B 2.7 绘制散点图矩阵 2.8 绘制面积图 2.9 绘制热力图 2.10 绘制核密度估计图 1 Pandas 可视化功能 pandas是一个强大的数…...
2023华为产品测评官-开发者之声 | 华为云CodeArts征文活动,多重好礼邀您发声!
"2023华为产品测评官-开发者之声"活动激发了众多开发者和技术爱好者的热情,他们纷纷递交了精心编写的产品测评报告。活动社群充满活力,参与者们热衷于交流讨论,互相帮助解决问题,一起探索云技术的无限可能。…...
Python 图形化界面基础篇:获取文本框中的用户输入
Python 图形化界面基础篇:获取文本框中的用户输入 引言 Tkinter 库简介步骤1:导入 Tkinter 模块步骤2:创建 Tkinter 窗口步骤3:创建文本框步骤4:获取文本框中的用户输入步骤5:启动 Tkinter 主事件循环 完整…...
【驱动开发】实现三盏灯的控制,编写应用程序测试
head.h #ifndef __HEAD_H__ #define __HEAD_H__//LED1:PE10 //LED2:PF10 //LED3:PE8#define LED_RCC 0X50000A28 //使能GPIO#define LED_MODER 0X50006000 //设置输出模式 #define LED_ODR 0X50006014 //设置输出高低电平#define LED2_MODER 0X50007000 …...
Vue3+ElementUI使用
<!DOCTYPE html> <html> <head><meta charset"UTF-8"><meta name"viewport" content"initial-scale1.0,maximum-scale1.0,minimum-scale1.0,user-scalable0, widthdevice-width"/><!-- 引入样式 --><lin…...
MySQL 和 MariaDB 版本管理的历史背景及差异
目录 MariaDB MySQL 差异 关于 SQLE SQLE 获取 了解更多 需要说明的是 MySQL 和 MariaDB 都有社区版和企业版。对于 MySQL,这两个版本都是由同一家公司(Oracle)提供,遵循相同的版本编号体系,企业版包含更丰富…...
linux驱动开发--day4(字符设备驱动注册内部流程、及实现备文件和设备的绑定下LED灯实验)
一、字符设备驱动注册的内部过程 1.分配struct cdev对象空间 2.初始化struct cdev对象 3.注册cdev对象 二、注册字符设备驱动分步实现 1.分配字符设备驱动对象 2.字符设备驱动对象初始化 3.设备号的申请 4.根据申请的设备号和驱动对象注册驱动 三、open函数回调驱动中…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
