Linux内核移植之一:内核源码结构与Makefile分析_、如果linux内核版本号写法形如version.patchlevel.sublevel-build-程序员宅基地

技术标签: 嵌入式Linux  

内容来自 韦东山《嵌入式Linux应用开发完全手册》

 

一、内核介绍

1、版本及其特点

        Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如下面几行它们构成了Linux的版本号:2.6.22.6。
 

VERSION=2
PATCHLEVEL=6SUBLEVEL =22
EXTRAVERSION=.6

        其中的“VERSION”和“PATCHLEVEL”组成主版本号,比如2.4、2.5、2.6等,稳定版本的主版本号用偶数表示(比如2.4、2.6),每隔2~3年出现一个稳定版本。开发中的版本号用奇数来表示(比如2.3、2.5),它是下一个稳定版本的前身。
        “SUBLEVEL”称为次版本号,它不分奇偶,顺序递增。每隔1~2个月发布一个稳定版本。

        “EXTRAVERSION”称为扩展版本号,它不分奇偶,顺序递增。每周发布几次扩展版本号,修正最新的稳定版本的问题。值得注意的是,“EXTRAVERSION”也可以不是数字,而是类似“-rc6”的字样,表示这是一个测试版本。在新的稳定版本发布之前,会先发布几个测试版本用于测试。
        Linux内核的最初版本在1991年发布,这是Linus Torvalds为他的386开发的一个类Minix的操作系统。

2、获取内核源码

        登录https://www.kernel.org/,可以看到如下:

        上面列举了当前内核的最新稳定版本、测试版本、补丁等。

        一般而言,各种补丁文件都是基内核的某个正式版本生成的。比如有补丁文件patch-2.6.xx.1、patch-2.6.xx.2、patch-2.6.xx.3,它们都是基于内核2.6.xx生成的补丁文件。使用时可以在内核2.6.xx上直接打补丁patch-2.6.xx.3,并不需要先打上补丁patch-2.6.xx.1、patch-2.6.xx.2;相应地,如果已经打了补丁patch-2.6.xx.2,在打补丁patch-2.6.xx.3前,要先去除patch-2.6.xx.2。
        本书在Linux2.6.22.6上进行移植、开发,下载linux-2.6.22.6.tar.bz2后用如下命令解压即可得到目录linux-2.6.22.6,里面存放了内核源码:

$ tar xjf linux-2.6.22.6.tar.bz2

        也可以先下载内核源文件linux-2.6.22.tar.bz2、补丁文件patch-2.6.22.6.bz2,然后解压、打补丁(假设源文件、补丁文件放在同一个目录下),命令如下所示:

tar xjf linux-2.6.22.tar.bz2
tar xjf patch-2.6.22.6.bz2
cd linux-2.6.22
patch -pl < ../patch-2.6.22.6

        下面的内容,都假设内核源码所在目录为linux-2.6.22.6。

3、内核源码结构

        Linux内核文件数目将近2万,除去其他架构CPU的相关文件,支持S3C2410、S3C2440这两款芯片的完整内核文件有1万多个。这些文件的组织结构并不复杂,它们分别位于顶层目录下的17个子目录,各个目录功能独立。下表描述了各目录的功能,最后2个目录不包含内核代码。

表1

目录名 描述
arch 体系结构相关的代码,对于每个架构的CPU,arch目录下都有一个对应的子目录,比如arch/arm/、arch/i386/
block 块设备的通用函数
crypto 常用加密和散列算法(如AES、SHA等),还有一些压缩和CRC校验算法
drivers 所有的设备驱动程序,里面每一个子目录对应一类驱动程序,比如drivers/block/为块设备驱动程序,drivers/char/为字符设备驱动程序,drivers/mtd/为NOR Flash、NAND Flash等存储设备的驱动程序
fs Linux支持的文件系统的代码,每个子目录对应一种文件系统,比如fsjffs2/、fs/ext2/、fs/ext3/
include 内核头文件,有基本头文件(存放在include/linux/目录下)、各种驱动或功能部件的头文件(比如include/media/、include/mtd/、include/net/)、各种体系相关的头文件(比如include/asm-arm/、include/asm-i386/)。当配置内核后,include/asm/是某个include/asm-xxx/(比如include/asm-arm/)的链接
init 内核的初始化代码(不是系统的引导代码),其中的main.c文件中的start_kernel函数是内核引导后运行的第一个函数
ipc 进程间通信的代码
Kermel 内核管理的核心代码,与处理器相关的代码位于arch/*/kemel/目录下
lib 内核用到的一些库函数代码,比如crc32.c、string.c,与处理器相关的库函数代码位于arch/*/lib/目录下
mm 内存管理代码,与处理器相关的内存管理代码位于arch/*/mm/目录下
net 网络支持代码,每个子目录对应于网络的一个方面
security 安全、密钥相关的代码
sound 音频设备的驱动程序
usr 用来制作一个压缩的cpio归档文件:initrd的镜像,它可以作为内核启动后挂接(mount)的第一个文件系统(一般用不到)
Documentation 内核文档
scripts 用于配置、编译内核的脚本文件

        对于ARM架构的S3C2410、S3C2440,其体系相关的代码在arch/arm/目录下,在后面进行Linux移植时,开始的工作正是修改这个目录下的文件。如下图所示为内核代码的层次结构。

二、Makefile分析

        Makefile的作用主要有以下3点:
(1)决定编译哪些文件。
(2)怎样编译这些文件?
(3)怎样连接这些文件,最重要的是它们的顺序如何?
        Linux内核源码中含有很多个Makefile文件,这些Makefile文件又要包含其他一些文件(比如配置信息、通用的规则等)。这些文件构成了Linux的Makefile体系,可以分为下表中的5类。

名称 描述
顶层Makefile 它是所有Makefile文件的核心,从总体上控制着内核的编译、连接
.config 配置文件,在配置内核时生成。所有Makefile文件(包括顶层目录及各级子目录)都是根据.config来决定使用哪些文件
arch/$(ARCH)/Makefile 对应体系结构的Makefile,它用来决定哪些体系结构相关的文件参与内核的生成,并提供一些规则来生成特定格式的内核映象
scripts/Makefile.* Makefile共用的通用规则、脚本等
kbuild Makefiles 各级子目录下的Makefile,它们相对简单,被上一层Makefile 调用来编译当前目录下的文件

        内核文档Documentation/kbuild/makefiles.txt对内核中Makefile的作用、用法讲解得非常透彻,以下根据前面总结的Makefile的3大作用分析这5类文件。

1、决定编译哪些文件

        Linux内核的编译过程从顶层Makefile开始,然后递归地进入各级子目录调用它们的Makefile,分为3个步骤。
(1)顶层Makefile决定内核根目录下哪些子目录将被编进内核。
(2)arch/$(ARCH)/Makefile 决定arch/$(ARCH)目录下哪些文件、哪些目录将被编进内核。
(3)各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编成模块(即驱动程序),进入哪些子目录继续调用他们的Makefile。

(1)步骤1

        先看步骤(1),在顶层Makefile中可以看到如下内容:

433 init-y    :=init/
434 drivers-y :=drivers/sound/
435 net-y     :=net/
436 libs-y    :=lib/
437 core-y    :=usr/
...
556 core-y    +=kernel/mm/fs/ipc/security/crypto/block/

        可见,顶层Makefile将这13个子目录分为5类:init-y、drivers-y、net-y、libs-y和core-y。表1中有17个子目录,除去include目录和后面两个不包含内核代码的目录外,还有一个arch目录没有出现在内核中。它在arch/$(ARCH)/Makefile中被包含进内核,在顶层Makefile中直接包含了这个Makefile,如下所示:

491 include $(srctree)/arch/$(ARCH)/Makefile

        对于ARCH变量,可以在执行make命令时传入,比如“make ARCH=arm …”。另外,对于非x86平台,还需要指定交叉编译工具,这也可以在执行make命令时传入,比如“make CROSS_COMPILE=arm-linux- …”。为了方便,常在顶层Makefile中进行如下修改。

修改前:
185 ARCH          ?= &(SUBARCH)
186 CROSS_COMPILE ?=
修改后:
185 ARCH          ?=arm
186 CROSS_COMPILE ?=arm-linux-

(2)步骤2
        对于步骤(2)的arch/$(ARCH)/Makefile,以ARM体系为例,在arch/arm/Makefile中可以看到如下内容:

94  head-y :=arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
...
171 core-y +=arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
172 core-y +=$(MACHINE)
173 core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2400/
174 core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2412/
175 core-$(CONFIG_ARCH S3C2410) += arch/arm/mach-s3c2440/
...
191 1ibs-y := arch/arm/lib/ $(libs-y)
...


        从第94行可知,除前面的5类子目录外,又出现了另一类:head-y,不过它直接以文件名出现。MMUEXT在arch/arm/Makefile前面定义,对于没有MMU的处理器,MMUEXT的值为-nommu,使用文件head-nommu.S;对于有MMU的处理器,MMUEXT的值为空,使用文件head.S。
        arch/arm/Makefile中类似第171、172、173行的代码进一步扩展了core-y的内容,第191行扩展了libs-y的内容,这些都是体系结构相关的目录。

        第173~175行中的CONFIG_ARCH_S3C2410在配置内核时定义,它的值有3种:y、m或空。y表示编进内核,m表示编为模块,空表示不使用
        编译内核时,将依次进入init-y、core-y、libs-y、drivers-y和net-y所列出的目录中执行它们的Makefile,每个子目录都会生成一个built-in.o(libs-y所列目录下,有可能生成lib.a文件)。最后,head-y所表示的文件将和这些built-in.o、lib.a一起被连接成内核映象文件vmlinux。

(3)步骤3
        最后,看一下步骤(3)是怎么进行的。
        在配置内核时,生成配置文件.config(具体的配置过程在后面会讲述)。内核顶层Makefile 使用如下语句间接包含.config文件,以后就根据.config中定义的各个变量决定编译哪些文件。之所以说是“间接”包含,是因为包含的是include/config/auto.conf文件,而auto.conf文件只是将.config文件中的注释去掉,再根据顶层Makefile中定义的变量增加了一些变量而已。

441 #Read in config
442 -include include/config/auto.conf 

        include/config/auto.conf文件的生成过程不再描述,它与.config的格式相同,摘选部分内容如下(注意,下面以“#”开头的行是本书加的注释):

CONEIG_ARCH_SMDK2410=y 
CONFIG_ARCH_S3C2440=y
#.config中没有下面这行,它是根据顶层Makefile中定义的内核版本号增加的
CONFIG_KERNELVERSION="2.6.22.6"
#.config中没有下面这行,它是根据顶层Makefile中定义的ARCH变量增加的
CONFIG_ARCH="arm"
CONFIG_JFFS2_FS=y 
CONFIG_LEDS_S3C24XX=m

        在include/config/auto.conf文件中,变量的值主要有两类:“y”和“m”。各级子目录的Makefile使用这些变量来决定哪些文件被编进内核中,哪些文件被编成模块(即驱动程序),要进入哪些下一级子目录继续编译,这通过以下4种方法来确定(obj-y、obj-m、lib-y是Makefile中的变量)。
        (1)obj-y用来定义哪些文件被编进(built-in)内核。
        obj-y中定义的.o文件由当前目录下的.c或.S文件编译生成,它们连同下级子目录的built-in.o文件一起被组合成(用“$(LD) -r”命令)当前目录下的built-in.o文件。这个built-in.o文件将被它的上一层Makefile使用。
        obj-y中各个.o文件的顺序是有意义的,因为内核中用module_init()或__initcall定义的函数将按照它们的连接顺序被调用。


        例16.1:当下面的CONFIG_ISDN、CONFIG_ISDN_PPP_BSDCOMP在.config中被定义为y时,isdn.c或isdn.S、isdn_bsdcomp.c或isdn_bsdcomp.S被编译成isdn.o、isdn_bsdcomp.o。这两个o文件被组合进built-in.o文件中,最后被连接进入内核。假如isdn.o、isdn_bsdcomp.o中分别用module_init(A)、module_init(B)定义了函数A、B,则内核启动时A先被调用,然后是B。

obj-$(CONFIG_ISDN)+=isdn.o 
obj-$(CONFIG_ISDN_PPP_BSDCOMP)+=isdn_bsdcomp.o


        (2)obj-m用来定义哪些文件被编译成可加载模块(Loadable module)。
        obj-m中定义的.o文件由当前目录下的.c或.S文件编译生成,它们不会被编进built-in.o中,而是被编成可加载模块。

        一个模块可以由一个或几个.o文件组成。对于只有一个源文件的模块,在obi-m中直接增加它的.o文件即可。对于有多个源文件的模块,除在obj-m中增加一个.o文件外,还要定义一个<module name>-objs变量来告诉Makefile这个.o文件由哪些文件组成。

        例16.2:当下面的CONFIG_ISDN_PPP_BSDCOMP在.config文件中被定义为m时,isdn_bsdcomp.c或isdn_bsdcomp.S将被编译成isdn_bsdcomp.o文件,它最后被制作成isdn_bsdcomp.ko模块,如下所示:

#drivers/isdn/i41/Makefile 
obj-$(CONEIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

        例16.3:当下面的CONFIG_ISDN在.config文件中被定义为m时,将会生成一个isdn.o文件,它由isdn-objs中定义的isdn_net_ lib.o、isdn_v110.o、isdn_common.o等3个文件组合而成。isdn.o最后被制作成isdn.ko模块。

#drivers/isdn/i41/Makefile
obj-$(CONFIG_ISDN) += isdn.o 
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

        (3)lib-y用来定义哪些文件被编成库文件。

        lib-y中定义的.o文件由当前目录下的.c或.S文件编译生成,它们被打包成当前目录下的个库文件:lib.a。

        同时出现在obj-y、lib-y中的.o文件,不会被包含进lib.a中。

        要把这个lib.a编进内核中,需要在顶层Makefile中libs-y变量中列出当前目录。要编成库文件的内核代码一般都在这两个目录下:lib/、arch/$(ARCH)/lib/。

        (4)obj-y、obj-m还可以用来指定要进入的下一层子目录。

        Linux中一个Makefile文件只负责生成当前日录下的目标文件,子目录下的目标文件由子目录的Makefile生成。Linux的编译系统会自动进入这些子目录调用它们的Makefile,只是在这之前需要指定这些子目录。

        这要用到obj-y、obj-m,只要在其中增加这些子目录名即可。

        例16.4:fs/Makefile中有如下一行,当CONFIG_JFFS2_FS被定义为y或m时,在编译时将会进入jffs2/目录进行编译。Linux的编译系统只会根据这些信息决定是否进入下一级目录,而下一级中的文件如何编译成built-in.o或模块由它的Makefile决定。

101 obj-$(CONFIG_JFFS2_FS) += jffs2/

 

2、怎样编译这些文件。

        即编译选项、连接选项是什么。这些选项分3类:全局的,适用于整个内核代码树;局部的,仅适用于某个Makefile中的所有文件;个体的,仅适用于某个文件。

        全局选项在顶层Makefile 和arch/$(ARCH)/Makefile中定义,这些选项的名称为:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS,它们分别是编译C文件的选项、编译汇编文件的选项、连接文件的选项、制作库文件的选项。

        需要使用局部选项时,它们在各个子目录中定义,名称为:EXTRA_CFLAGS、EXTRA_AFLAGS、EXTRA_LDFLAGS、EXTRA_ARFLAGS,它们的用途与前述选项相同,只是适用范围比较小,它们针对当前Makefile中的所有文件。

        另外,如果想针对某个文件定义它的编译选项,可以使用CFLAGS_$@,AFLAGS _$@。前者用于编译某个C文件,后者用于编译某个汇编文件。$@表示某个目标文件名,比如以下代码表示编译ahal52x.c时,选项中要额外加上“-DAHA152X_STAT-DAUTOCONF”。

#drivers/scsi/Makefile 
CFLAGS_aha152x.0 = -DAHA152X_STAT -DAUTOCONF

        需要注意的是,这3类选项是一起使用的,在scripts/Makefile.lib中可以看到。

_c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(basetarget).o)

 

3、怎样连接这些文件,它们的顺序如何。

        前面分析有哪些文件要编进内核时,顶层Makefile和arch/$(ARCH)/Makefile定义了6类目录(或文件):head-y、init-y、drivers-y、net-y、libs-y和core-y。它们的初始值如下(以ARM体系为例)。

        arch/arm/Makefile中:

94  head-y := arch/arm/kernel/head$ (MMUEXT) .o arch/arm/kernel/init_task.o
...
171 core-y += arch/arm/kernel/arch/arm/mm/arch/arm/common/
172 core-y += $(MACHINE)
173 core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2400/
174 core-$(CONFIG_ARCH_S3C2410) += arch/arm/mach-s3c2412/
175 core-$(CONEIG_ARCHS3C2410)  += arch/arm/mach-s3c2440/
...
191 libs-y := arch/arm/lib/$(libs-y)
...

        顶层Makefile中:

433 init-y    :=init/
434 drivers-y :=drivers/sound/
435 net-y     :=net/
436 libs-y    :=lib/
437 core-y    :=usr/
...
556 core-y += kernel/mm/fs/ipc/security/crypto/block/

        可见,除head-y外,其余的init-y、drivers-y等都是目录名。在顶层Makefile中,这些目录名的后面直接加上built-in.o或lib.a,表示要连接进内核的文件,如下所示:

567 init-y   :=$(patsubst %/,%/built-in.o,$(init-y))
568 core-y   :=$(patsubst %/,%/built-in.o,$core-y))
569 drivers-y:=$(patsubst %/,%/built-in.o,$(drivers-y))
570 net-y    :=$(patsubst %/,%/built-in.o,$(net-y))
571 libs-y1  :=$((patsubst %/,%/lib.a,$(libs-y))
572 libs-y2i :=$(patsubst %/,%/built-in.o,$(libs-y))
573 libs-y   :=$(libs-y1)  $(libs-y2)

        上面的patsubst是个字符串处理函数,它的用法如下:

$(patsubst pattern,replacement,text)

        表示寻找“text”中符合格式“pattern”的字,用“replacement”替换它们。比如上面的init-y初值为“init/”,经过第567行的交换后,“init-y”变为“init/built-in.o”。

        顶层Makefile中,再往下看。

602 vmlinux-init :=$(head-y) $(init-y)
603 vmlinux-main :=$(core-y) $(libs-y) $(drivers-y) $(net-y)
604 vmlinux-al1  :=$(vmlinux-init) $(vmlinux-main)
605 vmlinux-1ds  := arch/$(ARCH)/kerne1/vmlinux.lds

        第604行的vmlinux-all表示所有构成内核映象文件vmlinux的目标文件,从第602~604行可知这些目标文件的顺序为:head-y、init-y、core-y、libs-y、drivers-y、net-y,即arch/arm/kernel/head.o(假设有MMU,否则为head-nommu.o)、arch/arm/kernel/init task.o、init/built-in.o、usr/built-in.o等。

        第605行表示连接脚本为arch/$(ARCH)/kernel/ymlinux.lds。对于ARM体系,连接脚本就是arch/arm/kernel/vmlinux.lds,它由arch/arm/kernel/vmlinux.lds.S文件生成,规则在scripts/Makefile.build中,如下所示:

248 $(obj)/%.lds: $(src)/%.lds.S FORCE
249     $(ca1l if_changed_dep,cpp_lds_s)
250

        现将生成的arch/arm/kernel/vmlinux.lds摘录如下:

286 SECTIONS
287 {
291 .=(0xc0000000)+0X00008000; /*代码段起始地址,这是个虚拟地址*/
292
293 .text.head:{
294 _stext=.;
295 _sinittext=.;
296 *(.text.head)
297 }
298
299 .init:{/*内核初始化的代码和数据*/
...
343 }
344
...
355 .text:{/*真正的代码段*/
356 _text=.;/*代码段和只读数据段的开始地址*/
...
372}
373 /*只读数据*/
374 .=ALIGN((4096));.rodata:AT(ADDR(.rodata)-0){.……}.=ALIGN((4096));
375
376 _etext=.;/*代码段和只读数据段的结束地址*/
...
386 .data:Ar(_data_loc){/*数据段*/
387 data_start=.;/*数据段起始地址*/
...
422 edata=.;/*数据段结束地址*/
423
424 edataloc =_dataloc+SrZEOF(.data);/*数据段结束地址*/
425
426 .b33:{/*BSS段,没有初化或初值为0的全局、静态变量*/
427 _bs8_start=.;/*BSS段起始地址*/
428 *(.bss)
429 *(COMMON)
430 end=.;/*BsS段结束地址*/
431
432 /*调试信息段*/
433 .stab 0:{*(.stab)}
...
440}

        下面对本节分析Makefile的结果作一下总结。

(1)配置文件.config中定义了一系列的变量,Makefile将结合它们来决定哪些文件被编进内核、哪些文件被编成模块、涉及哪些子目录。

(2)顶层Makefile和arch/$(ARCH)/Makefile决定根目录下哪些子目录、arch/$(ARCH)目录下哪些文件和目录将被编进内核。

(3)最后,各级子目录下的Makefile决定所在目录下哪些文件将被编进内核,哪些文件将被编成模块(即驱动程序),进入哪些子目录继续调用它们的Makefile。

(4)顶层Makefile 和arch/$(ARCH)/Makefile 设置了可以影响所有文件的编译、连接选项:CFLAGS、AFLAGS、LDFLAGS、ARFLAGS。

(5)各级子日录下的Makefile中可以设置能够影响当前目录下所有文件的编译、连接选项:EXTRA CFLAGS、EXTRAAFLAGS、EXTRA_LDFLAGS、EXTRAARFLAGS;还可以设置可以影响某个文件的编译选项:CFLAGS_$@,AFLAGS_$@。

(6)顶层Makefile按照一定的顺序组织文件,根据连接脚本 arch/$(ARCH)/kermel/vmlinux.lds生成内核映象文件vmlinux。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qlexcel/article/details/106361638

智能推荐

springboot接收枚举值的默认方式_springboot get请求怎么接收前端传递的枚举数字-程序员宅基地

文章浏览阅读1.6k次。测试代码:@PostMapping() public void test(@RequestBody Student student){ System.out.println(student.getLover().name()); }class Student{ private Lover lover; public Lover getLover() { return lover; } public void setLover_springboot get请求怎么接收前端传递的枚举数字

【数学建模笔记】【第七讲】多元线性回归分析(二):虚拟变量的设置以及交互项的解释,以及基于Stata的普通回归与标准化回归分析实例_stata两个虚拟变量的交互项-程序员宅基地

文章浏览阅读1.5w次,点赞24次,收藏120次。简单来说就是去量纲后的回归(因为你要比较不同变量之间的显著性的大小,那么带着量纲怎么比,所以先把量纲去掉,然后再比较)官话:为了更为精准的研究影响评价量的重要因素(去除量纲的影响),我们可考虑使用标准化回归系数。_stata两个虚拟变量的交互项

mysql-程序员宅基地

文章浏览阅读203次。有时候安装mysql后使用mysql命令时报错 Can't connect to MySQL server on localhost (10061),或者用net start mysql 时报服务名无效,一般是因为mysql服务没有启动。这时候可以用管理身份运行cmd.exe(注意必须是管理..._c:\program files\mysql\mysql server 5.6\bin>mysqld --install install/remove

亚信科技java笔试题答案_亚信笔试题卷以及答案.docx-程序员宅基地

文章浏览阅读6.2k次,点赞3次,收藏44次。亚信联创科技校园招聘B 卷考试时间60_分钟 _考试方式(闭)卷(本试卷满分 100 分,答案请写在答题卡上)请不要在问卷上答题或涂改,笔试结束后请务必交回试卷部分内容分值备注一、计算机基础40分C/C++语言基础40分技能部分二、二选一JAVA 语言基础40分三、数据库20分总分100 分第一部分——计算机基础一、选择题(每题 2 分,总分 40分)1.CPU 状态分为目态和管态两种..._亚信科技java实习笔试题

三线城市程序员的薪资待遇怎么样?我分享提高java技术水平的几个方法_三线城市学java-程序员宅基地

文章浏览阅读1.3k次。3年对一个程序员来说是非常重要的。像我自己本身就是做程序员的,目前的薪资待遇是13K左右,虽然在我所在的公司不是最高的,但在所在的这个城市的消费水平来说,除了日常的开支,包括房租、水电、伙食、人际交往等费用之外,还能留下一部分闲钱自己存起来。不同城市的薪资待遇是不一样的,这主要是由于当地的消费水平和经济发展水平不同,所以如果你想要更高的薪资待遇,就要考虑在一线城市或者经济发达的城市工作。一个有着丰富工作经验的程序员,他的技能水平、经验和能力都比没有经验的程序员更加出色,所以他们的薪资待遇也会更高一些。_三线城市学java

Unity渲染顺序相关学习_unity overlaycamera depth-程序员宅基地

文章浏览阅读975次。1、camera(depth越小,越先渲染)2、sorting layer(值越小,越先渲染)(下面还有个sortingOrder 值越小,越先渲染)3、渲染队列renderQueue(值越小,越先渲染)4、深度值(距离相机越近该值越小,越远该值越大。)..._unity overlaycamera depth

随便推点

软件测试风险追踪表_软件测试风险管理表格-程序员宅基地

文章浏览阅读430次。软件测试风险追踪表风险追踪表 项目名称: 填制人: 编号 风险描述 影响 风险等级 发生的可能性 应对策略 状态 责任人 备注 ..._软件测试风险管理表格

AAC ADTS封装实现-程序员宅基地

文章浏览阅读1.2k次。一、AAC音频格式种类有哪些AAC音频格式是一种由MPEG-4标准定义的有损音频压缩格式。AAC包含两种格式 ADIF(Audio Data Interchange Format音频数据交换格式)和ADTS(Audio Data transport Stream音频数据传输流)。ADIF特点:可以确定的找到音视频数据的开始,不需要进行在音视频数据流中间开始的解码,它的解码必须在明确的定义开始。应用场景:常用在磁盘文件中。ADTS特点:具有同步字的比特流,解码可以在这个流中任何位置开始。类似于mp_aac adts

Unity基础概念_unity基本概念-程序员宅基地

文章浏览阅读213次。像要使用Resouce类,必须创建一个 Resouce 文件夹,然后把需要的资源放进去,才可以在代码中设置路径进行访问_unity基本概念

在gitlab中指定自定义 CI/CD 配置文件_gitlab配置cicd-程序员宅基地

文章浏览阅读2.4k次。指定自定义 CI/CD 配置文件,顾名思义就是在项目中指定文件来代替默认的.gitlab-ci.yml文件的方式来运行流水线。以往我们在使用流水线的时候,都是默认将.gitlab-ci.yml文件存在在项目的跟路径下,但是我们也可以指定备用文件名路径,或者不想在每个项目中来维护这个yml文件,那么通过自定义 CI/CD 配置文件便可以实现。_gitlab配置cicd

mysql出现#1063 - Incorrect column specifier for column 'id'的解决方法_sql 错误 [1063] [42000]: incorrect column specifier -程序员宅基地

文章浏览阅读1w次。出现这个表示如果设置了自动增长,字段类型应该设置为int整型。_sql 错误 [1063] [42000]: incorrect column specifier for column 'id' incorrec

java getlength_JAVA RSA-DerInputStream.getLength(): lengthTag=127, too big.-程序员宅基地

文章浏览阅读1k次。RSA 加载公钥时: Caused by: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=127, too big.加载公钥代码段:public static String getPubKeyByCer(String cerPath){String pubKey = "";..._java.security.invalidkeyexception: ioexception : derinputstream.getlength():