U-Boot的移植之(二)进阶篇:从源代码看系统启动过程

在Rviz仿真环境中搭建传送带 <geometry><material> 阅读详情

 

为什么要分析源代码?分析优秀的源代码本身就是一个学习的过程,也是进行深入研究的必经之路。不过在此我们的主要目的并非要研究U-boot或Bootloader技术本身,而仅仅是为了成功的并且恰当的将U-Boot移植到我们的开发板上。只有结合源代码了解了U-boot的系统引导过程,才能在移植和调试过程中保持清晰的思路,才能在碰到困难和问题时从根本上加以解决。

在动手分析之前,至少应该对U-Boot的源代码结构有基本的了解,很多参考书都有这方面的介绍,华清远见的《嵌入式Linux系统开发技术详解——基于ARM》的讲解就比较清晰。

本文以lubbock开发板为例,以系统启动的流程为线索进行纵向分析:后续的移植工作也将以此开发板为模板。Lubbock使用PXA255处理器。

首先要找到程序入口点。从board/lubbock/u-boot.lds可以发现,u-boot的程序入口为_start,在cpu/pxa/start.o当中。因此首先要分析start.S程序,U-Boot中所有的PXA系列的处理器都从这里开始执行第一条语句。

.globl _start

_start: b reset

ldr pc, _undefined_instruction

ldr pc, _software_interrupt

ldr pc, _prefetch_abort

ldr pc, _data_abort

ldr pc, _not_used

ldr pc, _irq

ldr pc, _fiq

0x0地址开始是ARM异常向量表,学过ARM体系结构与编程的都明白,非常简单,不多废话。一上电的第一条指令是跳转到reset复位处理程序:

reset:

/* 进入SVC模式 */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl cpu_init_crit /* we do sys-critical inits */

#endif

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:

......

一般不要定义CONFIG_SKIP_LOWLEVEL_INIT,因此,接下来跳转到cpu_init_crit处开始执行:

cpu_init_crit:

/* 屏蔽所有中断 */

/* 设置时钟源,关闭除FFUART,SRAM,SDRAM,FLASH以外的外设时钟 */

......

#ifdef CFG_CPUSPEED

ldr r0, CC_BASE /* 时钟控制寄存器基址 */

ldr r1, cpuspeed

/* cpuspeed: .word CFG_CPUSPEED */

str r1, [r0, #CCCR]

mov r0, #2

mcr p14, 0, r0, c6, c0, 0


setspeed_done:

#endif /* CFG_CPUSPEED */

/* 跳转到lowlevel_init,这里ip即r12,用作暂存寄存器 */

mov ip, lr

bl lowlevel_init

mov lr, ip

/* Memory interfaces are working. Disable MMU and enable I-cache. */

ldr r0, =0x2001

......

/* 关闭MMU,使能I-Cache(可选) */

mov pc, lr /* 这里是从cpu_init_crit返回到relocate标号 */


可见,在cpu_init_crit中的主要工作是设置时钟,配置处理器主频(这时CPU的工作频率还没有改变),调用lowlevel_init函数进行底层初始化(包括调整处理器工作频率、系统总线频率、存储器时钟频率以及存储系统的初始化等工作),随后关闭MMU并使能I-Cache,再返回。

lowlevel_init函数在board/lubbock/lowlevel_init.S中定义,其流程都是按照PXA27X的开发手册来的,所以不再赘述。仅指出,其中的寄存器在include/asm-arm/arch-pxa/pxa-regs.h头文件中定义,寄存器初始化值在include/configs/lubbock.h中定义。另外,在后面的实际移植工作中,由于目标板XSBASE270使用的PXA270处理器,可使用adsvix开发板的lowlevel_init.S文件(lubbock中没有开启turbo模式)。

接着程序的执行线索进行分析。从cpu_init_crit返回后就开始relocate(重定位),即将U-boot从FLASH存储器搬运到SDRAM中TEXT_BASE开始的存储空间(TEXT_BASE在board/lubbock/config.mk中定义),并初始化堆栈(清零.bss段),以在SDRAM中开始进入到Bootloader stage 2的C程序入口。Relocate部分开始的代码如下:

/* 之前已定义的部分变量有:

_TEXT_BASE: .word TEXT_BASE

_armboot_start: .word _start

_bss_start: .word __bss_start

_bss_end: .word _end */

relocate: /* relocate U-Boot to RAM */

adr r0, _start /* r0 <- current position of code */

ldr r1, _TEXT_BASE /* test if we run from flash or RAM */

cmp r0, r1 /* don't reloc during debug */

beq stack_setup


ldr r2, _armboot_start /* 读入_start到r2 */

ldr r3, _bss_start /* 读入__bss_start到r3 */

sub r2, r3, r2 /* r2 <- size of armboot */

add r2, r0, r2 /* r2 <- source end address */


copy_loop:

ldmia r0!, {r3-r10} /* copy from source address [r0] */

stmia r1!, {r3-r10} /* copy to target address [r1] */

cmp r0, r2 /* until source end addreee [r2] */

ble copy_loop


/* Set up the stack */

stack_setup:

ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */

sub r0, r0, #CFG_MALLOC_LEN /* malloc area */

sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */

#ifdef CONFIG_USE_IRQ

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub sp, r0, #12 /* leave 3 words for abort-stack */


clear_bss:

ldr r0, _bss_start /* find start of bss segment */

ldr r1, _bss_end /* stop here */

mov r2, #0x00000000 /* clear */


clbss_l:str r2, [r0] /* clear loop... */

add r0, r0, #4

cmp r0, r1

ble clbss_l


ldr pc, _start_armboot


_start_armboot: .word start_armboot

这是很经典的一段代码,相信学习凡是过ARM编程的,都分析过这段代码,所以也不再赘述。之所以列出这段代码,一是为了找到C程序入口start_armboot,二是为了给出U-Boot的一个存储器映射图:

这个图可以帮助我们更好地理解后续的C语言代码以及U-Boot对内存的分配与使用情况。

接下来进入到Bootloader Stage 2即C语言代码部分,入口是start_armboot,对应的源文件是lib_arm/board.c,这一文件对所有的ARM处理器都是通用的,因此在移植的时候不用修改。相关源代码如下:

DECLARE_GLOBAL_DATA_PTR;

/* 在include/asm-arm/global_data.h中定义的一个全局寄存器变量的声明:

* #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

* 用于存放全局数据结构体gd_t的地址。

*/

void start_armboot (void)

{

init_fnc_t **init_fnc_ptr;

char *s;

#ifndef CFG_NO_FLASH

ulong size;

#endif

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)

/* 本次移植暂不配置VFD和LCD,后面也将不考虑的部分略去 */

/* 初始化全局数据结构体指针gd */

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

....../* memset在lib_generic/string.c中定义*/

memset ((void*)gd, 0, sizeof (gd_t)); /*用0填充全局数据表*gd */

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

memset (gd->bd, 0, sizeof (bd_t)); /*用0填充(初始化) *gd->bd */


monitor_flash_len = _bss_start - _armboot_start;


for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

if ((*init_fnc_ptr)() != 0) {

hang (); /* 打印错误信息并死锁 */

}

}


#ifndef CFG_NO_FLASH

/* configure available FLASH banks */

size = flash_init (); /* drivers/cfi_flash.c或自定义 */

display_flash_config (size);

#endif /* CFG_NO_FLASH */


/*armboot_start is defined in the board-specific linker script*/

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

......

/* initialize environment */

env_relocate ();


/* IP Address */

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

/* MAC Address */

{

int i;

ulong reg;

char *s, *e;

char tmp[64];


i = getenv_r ("ethaddr", tmp, sizeof (tmp));

s = (i > 0) ? tmp : NULL;


for (reg = 0; reg < 6; ++reg) {

gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;

if (s)

s = (*e) ? e + 1 : e;

}

}


devices_init (); /* get the devices list going. */

.......

jumptable_init ();

console_init_r (); /* fully init console as a device */

enable_interrupts (); /* enable exceptions */


/* Perform network card initialisation if necessary */

#if defined(CONFIG_DRIVER_SMC91111)| |defined (CONFIG_DRIVER_LAN91C96)

if (getenv ("ethaddr"))

smc_set_mac_addr(gd->bd->bi_enetaddr);

#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */


/* Initialize from environment */

if ((s = getenv ("loadaddr")) != NULL) {

load_addr = simple_strtoul (s, NULL, 16);

}


#if defined(CONFIG_CMD_NET)

if ((s = getenv ("bootfile")) != NULL)

copy_filename (BootFile, s, sizeof (BootFile));

#endif

#ifdef BOARD_LATE_INIT

board_late_init ();

#endif

......

/*main_loop() can return to retry autoboot, if so just run it again.*/

for (;;) {

main_loop ();

}

}

gd_t是全局数据表类型,在include/asm-arm/global_data.h中定义如下:

/*

Keep it *SMALL* and remember to set CFG_GBL_DATA_SIZE > sizeof(gd_t)

*/

typedef struct global_data {

bd_t *bd;

unsigned long flags;

unsigned long baudrate;

unsigned long have_console; /* serial_init() was called */

unsigned long reloc_off; /* Relocation Offset */

unsigned long env_addr; /* Address of Environment struct */

unsigned long env_valid; /*Checksum of Environment valid?*/

unsigned long fb_base; /* base address of frame buffer */

.......

void **jt; /* jump table */

} gd_t;

其中,bd_t在include/asm-arm/u-boot.h中定义如下:

typedef struct bd_info {

int bi_baudrate; /* serial console baudrate */

unsigned long bi_ip_addr; /* IP Address */

unsigned char bi_enetaddr[6]; /* Ethernet adress */

struct environment_s *bi_env;

ulong bi_arch_number; /* unique id for this board */

ulong bi_boot_params; /* where this board expects params*/

struct /* RAM configuration */

{

ulong start;

ulong size;

}bi_dram[CONFIG_NR_DRAM_BANKS];

} bd_t;

jt是函数数组指针,随后将在jumptable_init()函数中初始化。

从lib_arm/board.c的源码不难分析出系统的启动流程:首先初始化全局数据表,然后顺序执行函数指针数组init_sequence中的一系列初始化函数——由其在本文件中的相关定义可得知初始化流程:

typedef int (init_fnc_t) (void);

init_fnc_t *init_sequence[] = {

cpu_init, /* basic cpu dependent setup -- cpu/pxa/cpu.c */

board_init, /* basic board setup --board/lubbock/lubbock.c */

interrupt_init, /* set up exceptions -- cpu/pxa/interrupts.c */

env_init, /* initialize environment -- common/env_flash.c */

init_baudrate, /* initialze baudrate settings--lib_arm/board.c */

serial_init, /* serial communications setup--cpu/pxa/serial.c */

console_init_f, /* stage 1 init of console -- common/console.c */

display_banner, /* say that we are here -- lib_arm/board.c */

#if defined(CONFIG_DISPLAY_BOARDINFO)

checkboard, /* display board info */

#endif

dram_init, /* configure available RAM banks --board/lubbock/lubbock.c */

display_dram_config, /* lib_arm/board.c */

NULL,

};

在执行这个函数序列的过程中,任何一个函数异常返回都会导致u-boot“死锁”或说“挂起”在hang()函数的死循环当中。

若一切顺利,接下来就调用flash_init()函数初始化CFI FLASH(针对NOR型闪存而言),该函数在drivers/cfi_flash.c中定义,不过,只有在目标板头文件中”#define CFG_FLASH_CFI_DRIVER”之后该驱动才会被编译;在lubbock的u-boot实现当中,include/configs/lubbock.h中没有定义CFG_FLASH_CFI_DRIVER,而是在board/lubbock/ flash.c中实现了自己的FLASH驱动,包括flash_init()在内。在移植U-Boot时,可以根据实际情况选择使用U-Boot自带的FLASH驱动还是自己编写新的驱动。如果配置了NAND闪存,还会对其进行初始化;笔者的XSABSE270板没有焊接NAND FLASH,故对此不作讨论。

接下来调用env_relocate()函数初始化环境变量,该函数在common/env_common.c文件中定义。在同一文件中可以发现还定义了一个字符数组default_environment[],用于描述缺省的环境变量,这些都要在include/configs/lubbock.h头文件中进行设置,包括启动命令CONFIG_BOOTCOMMAND,波特率CONFIG_BAUDRATE,IP地址CONFIG_IPADDR等等。

然后是获取自设置的目标板的网络地址,包括IP地址和MAC地址。

再然后是调用common/devices.c中定义的devices_init()函数来创建设备列表,并初始化相应的设备,主要是”stdin”,”stdout”,”stderr”以及自定义的设备如I2C,LCD等。这些相关代码是与平台无关的,因此从移植的角度考虑,不必作细致的研究与分析。

接着调用common/exports.c中定义的jumptable_init()函数,初始化全局数据表中的跳转表gd->jt,跳转表是一个函数指针数组,定义了u-boot中基本的常用的函数库;而gd->jt是这个函数指针数组的首指针。部分代码如下:

void jumptable_init (void) {

int i;

gd->jt = (void **) malloc (XF_MAX * sizeof (void *));

for (i = 0; i < XF_MAX; i++)

gd->jt[i] = (void *) dummy;

gd->jt[XF_get_version] = (void *) get_version;

gd->jt[XF_malloc] = (void *) malloc;

gd->jt[XF_free] = (void *) free;

gd->jt[XF_getenv] = (void *) getenv;

gd->jt[XF_setenv] = (void *) setenv;

......

}

上面的XF_get_version, XF_malloc, XF_free等在include/exports.h的枚举变量中定义,因此,实际上是作为”Label式整型序号”使用,即XF_get_version=1, XF_malloc=2, XF_free=3 ...,相关代码如下:

enum { /* include/exports.h */

#define EXPORT_FUNC(x) XF_ ## x ,

#include <_exports.h>

#undef EXPORT_FUNC

XF_MAX

};

EXPORT_FUNC(get_version)

EXPORT_FUNC(getc)

EXPORT_FUNC(tstc)

EXPORT_FUNC(putc)

EXPORT_FUNC(puts)

EXPORT_FUNC(printf)

......... /* include/_exports.h */

由于这些也是平台无关的代码,因此在移植过程中也不必深究。

然后是调用common/console.c中定义的函数console_init_r()初始化串口控制台,这同样是平台无关的代码,所以不必关心。

这时U-Boot的基本功能已经初始化完毕,便可开中断,并进行附加功能的配置与初始化,包括网卡驱动配置,目标板使用LAN91C1111网卡,对应SMC91111网卡驱动,可以根据需要配置其他的网卡驱动如CS8900等,这些都在include/configs/lubbock.h中定义。

然后是调用board/lubbock/lubbock.c中定义的board_late_init()函数进行板级的后期初始化,实际上是配置stdout和stderr的硬件设备。相关源代码如下:

int board_late_init(void)

{

setenv("stdout", "serial");

setenv("stderr", "serial");

return 0;

}

最后需要注意的一个很重要的文件是lib_arm/armlinux.c,它实现的功能包括设置内核启动参数,并负责将这些参数传递给内核,最后跳转到Linux内核入口函数,将控制权交给内核。

具体传递哪些参数,是通过在include/configs/lubbock.c中指定条件编译选项来控制的,对应于lib_arm/armlinux.c中的部分源代码形式如下:

#if defined (CONFIG_SETUP_MEMORY_TAGS) || /

defined (CONFIG_CMDLINE_TAG) || /

defined (CONFIG_INITRD_TAG) || /

defined (CONFIG_SERIAL_TAG) || /

defined (CONFIG_REVISION_TAG)

static void setup_start_tag (bd_t *bd);


# ifdef CONFIG_SETUP_MEMORY_TAGS

static void setup_memory_tags (bd_t *bd);

# endif

static void setup_commandline_tag (bd_t *bd, char *commandline);


# ifdef CONFIG_INITRD_TAG

static void setup_initrd_tag (bd_t *bd, ulong initrd_start,

ulong initrd_end);

# endif

static void setup_end_tag (bd_t *bd);

static struct tag *params;

#endif

......

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

ulong addr, ulong *len_ptr, int verify)

{

......

void (*theKernel)(int zero, int arch, uint params);

......

#ifdef CONFIG_CMDLINE_TAG

char *commandline = getenv ("bootargs");

#endif

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

......

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

}

关于这个参数列表中各个参数的定义及含义,以及参数列表的初始化过程,可以参考Booting ARM Linux一文。内核是如何找到这个参数列表在内存中的位置,以接收这些参数的呢?实际上,参数列表(tag list)在内存中的起始地址会保存在通用寄存器R2中,并传递给内核。而按照习惯或说惯例,通常tag list的首地址(物理地址)会设置为RAM起始地址+ 0x100偏移量,因此R2的值实际上是确定不变的。另外,还要正确设置R0和R1的值,在呼叫内核时,R0的值应为0,R1中则应保存机器类型(machine type)编号。R0,R1和R2都会作为参数传递给内核。

在上面的代码中,定义了一个函数指针theKernel,通过倒数第二条语句将内核入口地址赋给theKernel(hdr是include/image.h中定义的一个image_header结构体类型的数据,hdr->ih_ep中保存了内核入口地址,ntohl的功能是字节顺序的大小端转换,相关代码可以参考tools/mkimage.c),最后,根据APCS规则,将0, bd->bi_arch_number, bd->bi_boot_params 依次作为参数通过R0,R1和R2传递给theKernel函数,并进入内核启动部分。

至此,我们已经从源代码入手简要分析了U-Boot的启动流程,在这个过程中,我们对前一篇文章“添加新的目标板定义”也有了更进一步的理解和认识:为什么要添加这些文件;哪些文件是平台相关的并且必须要根据平台特性进行修改的;哪些文件是平台无关的,是不需要修改的,只需在头文件中作适当配置即可。

下一节,我们将给出移植U-Boot到XSBASE270开发板的实例。 

lammps构建高熵合金模型+结构优化初步筛选能量最小的结构 阅读详情

相关推荐

ESP32S3编译micropython固件

编译ESP32芯片的micropython固件

m0_73253860的博客 2554

EXPORT_FUNCTION导出函数

EXPORT_FUNCTION 通常用于在共享库(如 .so 文件)中导出函数,使得这些函数可以在其他程序中被调用。在 C 和 C++ 中,导出函数的方法因平台和编译器而异。

qq_41742701的博客 1131

信捷XDH PLC多轴运动控制实战:Ethercat总线下的C语言封装技巧

本文深入探讨了在信捷XDH PLC平台上,如何利用C语言对EtherCAT总线多轴运动控制进行高效封装。通过设计全局数据池、模块化状态机及用户友好接口,将底层复杂的运动控制函数库抽象为清晰、可复用的高级模块,显著提升程序稳定性与可维护性,并分享了关键的实战避坑经验。

weixin_29188043的博客 160

【GRUB】GRUB2代码初步解析

初步解析GRUB2代码。

jiangwei0512的博客 7257

多元相关性分析_PAST:最简便易用的统计学分析软件教程目录

欢迎来到PAST的世界!本教程并不是将PAST的所有功能均进行详细介绍,而只是挑选与生物、医学、微生物研究相关的统计学分析内容进行讲述。根据PAST内各项分析的标签分类,将该教程具体设置如下:PAST软件介绍PAST的基本信息数据的输入和修改数据的转换和标准化图像绘制 (Plot标签)点线图和直方图散点图条形图和箱须图饼图堆叠条形图三元相图气泡图单变量统计学分析 (Univariate标...

weixin_39605706的博客 3158

LTC3783 LED驱动控制器设计与效率优化详解

开关电源控制器是现代LED照明的核心器件,通过电流模式控制实现高效能量转换。其核心原理是通过采样电阻检测负载电流,配合误差放大器形成闭环调节,在汽车电子、显示背光等场景中确保±3%的电流精度。LTC3783作为典型代表,集成升压/降压-升压多拓扑支持,配合PWM调光技术可实现3000:1的无色偏调光范围。在工程实践中,需重点考量功率电感选型、MOSFET开关损耗与热管理设计,例如在38W功率方案中采用10μH/5A电感和4.5mΩ MOSFET可达成95%效率。该方案特别适用于需要应对9-36V宽输入电压波

weixin_36231030的博客 401

读U-Boot源码-C语言编程大法总结篇一

导读:如本人在《U-Boot架构浅析》所说,U-Boot具有十大黄金原则:小巧、快速、简单、可移植、可配置、可调试、易用、可维护、优雅、开源。面对如此精美的作品,如不深究,从提升编程技艺角度而言实为憾事。故本文试图观其形而悟其神,并把所见所思所得记录与诸君分享。文中观点或有错误疏漏,诚请交流指正,不甚感激! 说明: 文中绘图采用UML语言,或有不对也请一并指正。 代码分析基于u-...

嵌入式客栈 804

Potplayer + LAVFilters + madVR 配置教程

文章目录Potplayer + LAVFilters + madVR 配置教程工具安装篇设置调教1.potplayer播放器设置加载LAVFilters和madVR1.选择“源滤镜/分离器”选项,选择“滤镜/解码器管理”2.“添加外部滤镜”3.浏览到刚才LAVFilters解压的文件夹,分别添加图示项。4.添加后回到管理界面设置,点击左边的”LAV AudioDecoder”,将右边”音频解码器”...

re 16万+

C/C++中export的定义 C++/C宏定义中## 连接符与# 符的含义

C/C++中export的定义 分类: 编程知识收集 2004-08-03 17:47 1718人阅读 评论(0) 收藏 举报 C/C++中export的定义 Windows下的DLL、Linux下的库或是C++中调用C的库,均可能用到import/export。本文讨论一种通用的import/export宏定义的使用实践。 如下所示,"common.h"定义了

shuiyingzi5的专栏 1291

避坑指南:DeepSeek Function calling流式输出中的常见问题与解决方案

本文深入探讨了在DeepSeek Function Calling中实现流式输出时常见的参数拼接、函数依赖管理和错误处理等核心挑战。针对流式响应中参数碎片化的问题,提供了健壮的参数拼接方案与状态管理策略。同时,文章详细阐述了如何管理函数调用顺序、处理条件性执行,并构建了分阶段的错误处理与重试机制,旨在帮助开发者构建稳定、高效的流式AI应用。

weixin_29062671的博客 203

【Isaac Sim】无头服务器远程桌面连接实战:Turbovnc配置与常见问题解决

本文详细介绍了在无头GPU服务器上通过Turbovnc远程桌面连接并运行NVIDIA Isaac Sim的实战教程。针对无图形界面服务器运行仿真软件的核心痛点,文章提供了从Turbovnc安装配置、SSH隧道建立,到以普通用户身份启动服务、解决GLXBadFBConfig等常见图形错误的完整方案,帮助开发者在云端高效部署机器人仿真环境。

weixin_29254673的博客 774

19、Bootloader(3) -- U-Boot第一阶段代码start.S分析

/******************************************************************** Bootloader第一阶段的功能:(运行在Flash当中)         -- 硬件设备初始化  (cpu/arm920t/star

WuHan MediaTek~~辉煌在于不懈的努力和拼搏! 687

置顶必读(2) |《YOLOv8实战:从入门到深度优化》总目录导航 & 学习路线指南(持续更新中)

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 28万+

java与jsp的关系_浅谈servlet与jsp的关系

servlet是用java语言编写的,是一个java类。主要功能是用来接受、处理客户端的请求,并把处理结果返回到客户端显示。Jsp是servlet发展后期的产物。在没有jsp之前,servlet利用输出流动态生成整个HTML页面,输出内容包括每一个HTML标签和每个在HTML页面中出现的内容。HTML文件包含大量标签和大量静态文本及格式等,以及所有的表现逻辑,包括布局、色彩及图像等。这些内容都必须...

weixin_36414025的博客 376

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

JSP与Java Servlet的关系

                       JSP与Java Servlet的关系   相信很多人在学JSP时,都会被JSP和Servlet弄得云里雾里,不知所云。那么现在我就来讲下JSP与Servlet之间的区别与联系起到一定的帮助,希望对大家学习JSP,希望对大家学习JSP起到一定的帮助。下面我们将主要解决以下几个问题: 1、什么是JSP? 2、什么是Servet? 3、JSP...

代码之神 927

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

java .javascript servlet filter jsp关系

学习了一段时间,感觉非常有意思,一切变的渐渐的清晰了起来.... 小小的总结了一下,认识可能不是很全面, 随着深入的学习会继续改动和总结. java .javascript servlet filter jsp关系

Xinghf 2479

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

java与servlet JSP_浅谈servlet与jsp的关系

servlet是用java语言编写的,是一个java类。主要功能是用来接受、处理客户端的请求,并把处理结果返回到客户端显示。jsp是servlet发展后期的产物。在没有jsp之前,servlet利用输出流动态生成整个html页面,输出内容包括每一个html标签和每个在html页面中出现的内容。html文件包含大量标签和大量静态文本及格式等,以及所有的表现逻辑,包括布局、色彩及图像等。这些内容都必须...

weixin_30375113的博客 452

servlet / jsp 学习——java,关系,区别,核心概念,代码例子

servlet 就是 java,是继承父类 HttpServlet 的 java 类。在服务器端运行,用于处理用户的请求并给予响应。jsp 就是 servlet,所以 jsp 也就是 java

caitao_zhan 1947

jsp与java的关系_JSP与Servlet的关系

之前的文章里有说过,其实Servlet比JSP出现的早,JSP的本质就是Servlet,但是一个是jsp页面,一个是java类,硬要说他们的本质是一样的,搁谁都有点难以置信。后面的结论会更加让你吃惊。首先,我们来说JSP的运行过程。如果用户第一次访问某个页面jsp---->编译为java文件---->翻译为字节码文件---->生成静态页面html等。那我们来验证到底是不是这样一个...

weixin_42511512的博客 288

jsp与java的关系_浅谈servlet与jsp的关系

servlet是用java语言编写的,是一个java类。主要功能是用来接受、处理客户端的请求,并把处理结果返回到客户端显示。Jsp是servlet发展后期的产物。在没有jsp之前,servlet利用输出流动态生成整个HTML页面,输出内容包括每一个HTML标签和每个在HTML页面中出现的内容。HTML文件包含大量标签和大量静态文本及格式等,以及所有的表现逻辑,包括布局、色彩及图像等。这些内容都必须...

weixin_35589806的博客 219

Java - JSP和Servlet是什么关系?

分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。Servl...

了解➔熟悉➔掌握➔精通 3万+

jsp与java的关系_jsp和Servlet关系

一、什么是servletservlet本质就是一个Java接口,Servlet运行在web容器当中(如docker镜像运行在docker容器当中)二、什么是web容器在JSP技术 推出后,管理和运行Servlet/JSP的容器也称为Web容器。web容器中包含servlet容器。web容器有很多(tomcat,Jboss,weblogic)等三、什么是jspJSP是动态网页技术,出现在Servle...

weixin_39653405的博客 418

java中jsp网页的容器_JSP学习 —— 开篇:JSP,servlet容器,Tomcat,servlet容器之间的关系...

JSP(JAVA SERVER PAGE)的缩写,其本身就是servlet的简化,是一种动态网页标准,其特点是在HTML代码中嵌入JAVA代码,JSP标签或用户标签来生成网页。至于它为什么会出现,主要原因在于早期的servlet技术在编写代码时经常通篇性的写一大堆HTML标签,静态文本及文本格式等表现逻辑,其开发效率非常之低下;为了解决这种情况,便随之出现了JSP,其静态部分(包括表现逻辑,如图片...

weixin_35735088的博客 242

java jsp servelet_java Web jsp和servlet的关系

JSP在本质上就是SERVLET,但是两者的创建方式不一样Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观JSP由HTML代码和JSP标签构成,可以方便地编写动态网页因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页简单的说,SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语...

weixin_39868248的博客 129

Java Web笔记 – JSP的工作原理介绍 Servlet与JSP之间的转换关系

1、生成页面的流程: 浏览器(客户端) --> 服务器(Tomcat) --> Servlet --> HTML 浏览器发送请求给服务器,服务器解析请求,并找到相应的Servlet,处理请求,并返回相应的HTML格式的数据返回给客户端,让客户端的浏览器解析并渲染成页面。 JSP:Java Server Pages Java服务器端页面 2、JSP页面: 在HTML页面中加入Jav

My Heart Will Go On 816

java Web jsp和servlet的关系

java Web jsp和servlet的关系 JSP在本质上就是SERVLET,但是两者的创建方式不一样Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观JSP由HTML代码和JSP标签构成,可以方便地编写动态网页因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页...

这里将承载一段最初的记忆 362
上一篇: 我的触摸屏驱动源代码
下一篇: U-Boot的移植之(一)基础篇:添加新的目标板定义
slippercat
博客等级 码龄24年 19粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值