当前位置: 首页 > wzjs >正文

织梦网站联系我们的地图怎么做北京搜索引擎关键词优化

织梦网站联系我们的地图怎么做,北京搜索引擎关键词优化,在线做效果图的网站,哈尔滨网站如何制作目录 概要 一、汇编分析 1.1、go tool compile 1.2、dlv debug 二、栈布局与函数栈帧 概要 函数栈帧是指函数在被调用时,为该函数在栈上分配的一块内存区域(一般是连续的),主要用于保存函数的上下文信息,包括参数&…

目录

概要

一、汇编分析

1.1、go tool compile

1.2、dlv debug

 二、栈布局与函数栈帧


概要

函数栈帧是指函数在被调用时,为该函数在栈上分配的一块内存区域(一般是连续的),主要用于保存函数的上下文信息,包括参数,返回值,局部变量,寄存器值(ebp/rbp)等信息

调试的服务器信息:Centos Linux 7 ,CPU AMD x86_64,Go version 1.24

在go源码runtime/stack.go文件中可以看到go给出的在x86 CPU下栈布局:

// (x86)
// +------------------+
// | args from caller |
// +------------------+ <- frame->argp
// |  return address  |
// +------------------+
// |  caller's BP (*) | (*) if framepointer_enabled && varp > sp
// +------------------+ <- frame->varp
// |     locals       |
// +------------------+
// |  args to callee  |
// +------------------+ <- frame->sp

我们结合一下案例,结合汇编进行逐步分析:

 1 package main23 func main() {4     x,y:=int64(1),int64(2)5     x=add(x,y)6 }78 func add(a, b int64) int64 {9      c:=a+b10     q:=int64(5)11     p:=sub(c, q)12     return p13 }1415 func sub(x, y int64) int64{16     return x -y17 }

前置知识:

x86寄存器

x86汇编指令

go汇编
 

一、汇编分析

我们通过go tool compile和dlv两个工具结合来看。

1.1、go tool compile

[root@test gofunc]# go tool compile -S -N -l main.go
main.main STEXT size=66 args=0x0 locals=0x28 funcid=0x0 align=0x00x0000 00000 (/home/gofunc/main.go:3) TEXT    main.main(SB), ABIInternal, $40-00x0000 00000 (/home/gofunc/main.go:3) CMPQ    SP, 16(R14)0x0004 00004 (/home/gofunc/main.go:3) PCDATA  $0, $-2 //GC相关0x0004 00004 (/home/gofunc/main.go:3) JLS     580x0006 00006 (/home/gofunc/main.go:3) PCDATA  $0, $-1 //GC相关0x0006 00006 (/home/gofunc/main.go:3) PUSHQ   BP0x0007 00007 (/home/gofunc/main.go:3) MOVQ    SP, BP0x000a 00010 (/home/gofunc/main.go:3) SUBQ    $32, SP0x000e 00014 (/home/gofunc/main.go:3) FUNCDATA        $0, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB) //GC相关0x000e 00014 (/home/gofunc/main.go:3) FUNCDATA        $1, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB) //GC相关0x000e 00014 (/home/gofunc/main.go:4) MOVQ    $1, main.x+24(SP)0x0017 00023 (/home/gofunc/main.go:4) MOVQ    $2, main.y+16(SP)0x0020 00032 (/home/gofunc/main.go:5) MOVL    $1, AX0x0025 00037 (/home/gofunc/main.go:5) MOVL    $2, BX0x002a 00042 (/home/gofunc/main.go:5) PCDATA  $1, $00x002a 00042 (/home/gofunc/main.go:5) CALL    main.add(SB)0x002f 00047 (/home/gofunc/main.go:5) MOVQ    AX, main.x+24(SP)0x0034 00052 (/home/gofunc/main.go:6) ADDQ    $32, SP0x0038 00056 (/home/gofunc/main.go:6) POPQ    BP0x0039 00057 (/home/gofunc/main.go:6) RET0x003a 00058 (/home/gofunc/main.go:6) NOP0x003a 00058 (/home/gofunc/main.go:3) PCDATA  $1, $-10x003a 00058 (/home/gofunc/main.go:3) PCDATA  $0, $-20x003a 00058 (/home/gofunc/main.go:3) CALL    runtime.morestack_noctxt(SB)0x003f 00063 (/home/gofunc/main.go:3) PCDATA  $0, $-10x003f 00063 (/home/gofunc/main.go:3) NOP0x0040 00064 (/home/gofunc/main.go:3) JMP     00x0000 49 3b 66 10 76 34 55 48 89 e5 48 83 ec 20 48 c7  I;f.v4UH..H.. H.0x0010 44 24 18 01 00 00 00 48 c7 44 24 10 02 00 00 00  D$.....H.D$.....0x0020 b8 01 00 00 00 bb 02 00 00 00 e8 00 00 00 00 48  ...............H0x0030 89 44 24 18 48 83 c4 20 5d c3 e8 00 00 00 00 90  .D$.H.. ].......0x0040 eb be                                            ..rel 43+4 t=R_CALL main.add+0rel 59+4 t=R_CALL runtime.morestack_noctxt+0
main.add STEXT size=103 args=0x10 locals=0x38 funcid=0x0 align=0x00x0000 00000 (/home/gofunc/main.go:8) TEXT    main.add(SB), ABIInternal, $56-160x0000 00000 (/home/gofunc/main.go:8) CMPQ    SP, 16(R14)0x0004 00004 (/home/gofunc/main.go:8) PCDATA  $0, $-20x0004 00004 (/home/gofunc/main.go:8) JLS     760x0006 00006 (/home/gofunc/main.go:8) PCDATA  $0, $-10x0006 00006 (/home/gofunc/main.go:8) PUSHQ   BP0x0007 00007 (/home/gofunc/main.go:8) MOVQ    SP, BP0x000a 00010 (/home/gofunc/main.go:8) SUBQ    $48, SP0x000e 00014 (/home/gofunc/main.go:8) FUNCDATA        $0, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB)0x000e 00014 (/home/gofunc/main.go:8) FUNCDATA        $1, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB)0x000e 00014 (/home/gofunc/main.go:8) FUNCDATA        $5, main.add.arginfo1(SB)0x000e 00014 (/home/gofunc/main.go:8) MOVQ    AX, main.a+64(SP)0x0013 00019 (/home/gofunc/main.go:8) MOVQ    BX, main.b+72(SP)0x0018 00024 (/home/gofunc/main.go:8) MOVQ    $0, main.~r0+16(SP)0x0021 00033 (/home/gofunc/main.go:9) ADDQ    BX, AX0x0024 00036 (/home/gofunc/main.go:9) MOVQ    AX, main.c+40(SP)0x0029 00041 (/home/gofunc/main.go:10)        MOVQ    $5, main.q+24(SP)0x0032 00050 (/home/gofunc/main.go:11)        MOVL    $5, BX0x0037 00055 (/home/gofunc/main.go:11)        PCDATA  $1, $00x0037 00055 (/home/gofunc/main.go:11)        CALL    main.sub(SB)0x003c 00060 (/home/gofunc/main.go:11)        MOVQ    AX, main.p+32(SP)0x0041 00065 (/home/gofunc/main.go:12)        MOVQ    AX, main.~r0+16(SP)0x0046 00070 (/home/gofunc/main.go:12)        ADDQ    $48, SP0x004a 00074 (/home/gofunc/main.go:12)        POPQ    BP0x004b 00075 (/home/gofunc/main.go:12)        RET0x004c 00076 (/home/gofunc/main.go:12)        NOP0x004c 00076 (/home/gofunc/main.go:8) PCDATA  $1, $-10x004c 00076 (/home/gofunc/main.go:8) PCDATA  $0, $-20x004c 00076 (/home/gofunc/main.go:8) MOVQ    AX, 8(SP)0x0051 00081 (/home/gofunc/main.go:8) MOVQ    BX, 16(SP)0x0056 00086 (/home/gofunc/main.go:8) CALL    runtime.morestack_noctxt(SB)0x005b 00091 (/home/gofunc/main.go:8) PCDATA  $0, $-10x005b 00091 (/home/gofunc/main.go:8) MOVQ    8(SP), AX0x0060 00096 (/home/gofunc/main.go:8) MOVQ    16(SP), BX0x0065 00101 (/home/gofunc/main.go:8) JMP     00x0000 49 3b 66 10 76 54 55 48 89 e5 48 83 ec 28 48 89  I;f.vTUH..H..(H.0x0010 44 24 38 48 89 5c 24 40 48 c7 44 24 10 00 00 00  D$8H.\$@H.D$....0x0020 00 48 8d 0c 18 48 89 4c 24 20 48 c7 44 24 18 05  .H...H.L$ H.D$..0x0030 00 00 00 b8 4b 00 00 00 bb 05 00 00 00 0f 1f 00  ....K...........0x0040 e8 00 00 00 00 48 89 44 24 18 48 8b 44 24 20 48  .....H.D$.H.D$ H0x0050 89 44 24 10 48 83 c4 28 5d c3 48 89 44 24 08 48  .D$.H..(].H.D$.H0x0060 89 5c 24 10 e8 00 00 00 00 48 8b 44 24 08 48 8b  .\$......H.D$.H.0x0070 5c 24 10 eb 8b                                   \$...rel 65+4 t=R_CALL main.sub+0rel 101+4 t=R_CALL runtime.morestack_noctxt+0
main.sub STEXT nosplit size=39 args=0x10 locals=0x10 funcid=0x0 align=0x00x0000 00000 (/home/gofunc/main.go:15)        TEXT    main.sub(SB), NOSPLIT|ABIInternal, $16-160x0000 00000 (/home/gofunc/main.go:15)        PUSHQ   BP0x0001 00001 (/home/gofunc/main.go:15)        MOVQ    SP, BP0x0004 00004 (/home/gofunc/main.go:15)        SUBQ    $8, SP0x0008 00008 (/home/gofunc/main.go:15)        FUNCDATA        $0, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB)0x0008 00008 (/home/gofunc/main.go:15)        FUNCDATA        $1, gclocals·FzY36IO2mY0y4dZ1+Izd/w==(SB)0x0008 00008 (/home/gofunc/main.go:15)        FUNCDATA        $5, main.sub.arginfo1(SB)0x0008 00008 (/home/gofunc/main.go:15)        MOVQ    AX, main.x+24(SP)0x000d 00013 (/home/gofunc/main.go:15)        MOVQ    BX, main.y+32(SP)0x0012 00018 (/home/gofunc/main.go:15)        MOVQ    $0, main.~r0(SP)0x001a 00026 (/home/gofunc/main.go:16)        SUBQ    BX, AX0x001d 00029 (/home/gofunc/main.go:16)        MOVQ    AX, main.~r0(SP)0x0021 00033 (/home/gofunc/main.go:16)        ADDQ    $8, SP0x0025 00037 (/home/gofunc/main.go:16)        POPQ    BP0x0026 00038 (/home/gofunc/main.go:16)        RET0x0000 55 48 89 e5 48 83 ec 08 48 89 44 24 18 48 89 5c  UH..H...H.D$.H.\0x0010 24 20 48 c7 04 24 00 00 00 00 48 29 d8 48 89 04  $ H..$....H).H..0x0020 24 48 83 c4 08 5d c3                             $H...].
go:cuinfo.producer.<unlinkable> SDWARFCUINFO dupok size=00x0000 2d 4e 20 2d 6c 20 72 65 67 61 62 69              -N -l regabi
go:cuinfo.packagename.main SDWARFCUINFO dupok size=00x0000 6d 61 69 6e                                      main
main..inittask SNOPTRDATA size=80x0000 00 00 00 00 00 00 00 00                          ........
gclocals·FzY36IO2mY0y4dZ1+Izd/w== SRODATA dupok size=80x0000 01 00 00 00 00 00 00 00                          ........
main.add.arginfo1 SRODATA static dupok size=50x0000 00 08 08 08 ff                                   .....
main.sub.arginfo1 SRODATA static dupok size=50x0000 00 08 08 08 ff                                   .....

我们对add函数汇编结果的前两行最分析(其他的内容用dlv分析,结果更直观):

1) 函数头信息 (STEXT)

main.add STEXT size=103 args=0x10 locals=0x38 funcid=0x0 align=0x0

  • ​**STEXT**
    表示该段代码属于程序的代码段(Text Segment),用于存放可执行指令。
  • ​**size=117**
    函数内容编译后的机器码总大小为 103 字节。
  • ​**args=0x10**
    参数和返回值总大小为 16 字节(对应两个 int64 类型,8+8)。
  • ​**locals=0x38**
    局部变量和临时存储空间占用 56 字节(包含寄存器值、栈扩展保留空间等)。
  • ​**funcid=0x0**
    函数标识符,0 表示普通函数(非闭包或方法)。
  • ​**align=0x0**
    函数入口地址对齐方式,0 表示使用默认对齐(通常为 16 字节)。

2)函数定义 (TEXT)

0x0000 00000 (/home/gofunc/main.go:8) TEXT    main.add(SB), ABIInternal, $56-16

  • ​**main.add(SB)**
    定义函数 main.addSB 是虚拟的静态基址寄存器,表示符号的全局地址。
  • ​**ABIInternal**
    使用 Go 1.17+ 的内部调用约定(Internal ABI),通过寄存器传递参数,提升性能。
  • ​**$56-16**
    • 56:函数栈帧总大小(单位字节)。
    • 16:参数和返回值的总大小(由调用者在栈上分配)。

PS:这里可能会疑惑为什么传参和返回值怎么才16字节,不应该34字节吗?这是因为go编译器编译时让返回值复用一个参数的内存了。

1.2、dlv debug

 go汇编,汇编指令解析放在每一行最后。

[root@test gofunc]# dlv debug main.go
Type 'help' for list of commands.
(dlv) b main.go:3
Breakpoint 1 set at 0x470aea for main.main() ./main.go:3
(dlv) b main.go:8
Breakpoint 2 set at 0x470b4a for main.add() ./main.go:8
(dlv) b main.go:15
Breakpoint 3 set at 0x470bc4 for main.sub() ./main.go:15
(dlv) c
> [Breakpoint 1] main.main() ./main.go:3 (hits goroutine(1):1 total:1) (PC: 0x470aea)1: package main2:
=>   3: func main() {4:     x,y:=int64(1),int64(2)5:     x=add(x,y)6: }7:8: func add(a, b int64) int64 {
(dlv) disass
TEXT main.main(SB) /home/gofunc/main.gomain.go:3       0x470ae0        493b6610                cmp rsp, qword ptr [r14+0x10] //比较栈顶(rsp)和协程栈预警值(g.stackguard0)大小,小于则要栈扩容main.go:3       0x470ae4        7634                    jbe 0x470b1a//小于成立,则跳到0x470b9a指令处,进行栈扩容main.go:3       0x470ae6        55                      push rbp //入栈main.go:3       0x470ae7        4889e5                  mov rbp, rsp
=>      main.go:3       0x470aea*       4883ec20                sub rsp, 0x20//SP寄存器值减去32,表示栈顶向下减小32字节,即为main函数申请32字节的栈内存保存其上下文main.go:4       0x470aee        48c744241801000000      mov qword ptr [rsp+0x18], 0x1 //设置x变量的值,占用8字节,即其占用rsp(栈顶)向上[0x18,0x20]之间的内存,第24到32字节之间main.go:4       0x470af7        48c744241002000000      mov qword ptr [rsp+0x10], 0x2  //设置y变量的值,占用8字节,即其占用rsp(栈顶)向上[0x10,0x18]之间的内存,第16到24字节之间main.go:5       0x470b00        b801000000              mov eax, 0x1 //设置AX寄存器值为1,即调用add的参数xmain.go:5       0x470b05        bb02000000              mov ebx, 0x2 //设置BX寄存器值为2,即调用add的参数ymain.go:5       0x470b0a        e831000000              call $main.add //调用add函数main.go:5       0x470b0f        4889442418              mov qword ptr [rsp+0x18], rax //将add返回值从AX寄存器中取出来,赋值给x变量main.go:6       0x470b14        4883c420                add rsp, 0x20//SP寄存器值加上32,表示栈顶向上增加32字节,即将main函数申请32字节的栈内存归还main.go:6       0x470b18        5d                      pop rbp //出栈main.go:6       0x470b19        c3                      retmain.go:3       0x470b1a        e8c1adffff              call $runtime.morestack_noctxtmain.go:3       0x470b1f        90                      nopmain.go:3       0x470b20        ebbe                    jmp $main.main
(dlv) c
> [Breakpoint 2] main.add() ./main.go:8 (hits goroutine(1):1 total:1) (PC: 0x470b4a)3: func main() {4:     x,y:=int64(1),int64(2)5:     x=add(x,y)6: }7:
=>   8: func add(a, b int64) int64 {9:     c:=a+b10:     q:=int64(5)11:     p:=sub(c, q)12:     return p13: }
(dlv) disass
TEXT main.add(SB) /home/gofunc/main.gomain.go:8       0x470b40        493b6610                cmp rsp, qword ptr [r14+0x10]  main.go:8       0x470b44        7654                    jbe 0x470b9a main.go:8       0x470b46        55                      push rbp //将BP寄存器的值入栈,此时BP寄存器存的是main函数栈帧起始位置的地址,此时SP值等于SP值减去8字节,用于存储BP寄存器的值main.go:8       0x470b47        4889e5                  mov rbp, rsp//将SP寄存器值赋给BP寄存器,作为add函数栈帧的栈底
=>      main.go:8       0x470b4a*       4883ec28                sub rsp, 0x30//SP寄存器值减去48,表示栈顶向下减少48字节,即为add函数申请48字节的栈内存保存其上下文main.go:8       0x470b4e        4889442438              mov qword ptr [rsp+0x40], rax //从AX寄存器取参数x的值,放到rsp+0x38地址处,使用的是main函数的栈main.go:8       0x470b53        48895c2440              mov qword ptr [rsp+0x48], rbx//从BX寄存器取参数y的值,放到rsp+0x40地址处,使用的是main函数的栈main.go:8       0x470b38        48c744241000000000      mov qword ptr [rsp+0x10], 0x0 //令rsp+0x10地址处的值为0,即rsp+[0x10,0x18]之间的内存处存储的值清空main.go:9       0x470b41        4801d8                  add rax, rbx//令AX寄存器值等于AX寄存器值+BX寄存器值,即c=a+b的操作main.go:9       0x470b44        4889442428              mov qword ptr [rsp+0x28], rax //将AX寄存器值放到rsp+0x28地址处main.go:10      0x470b6a        48c744241805000000      mov qword ptr [rsp+0x18], 0x5 //令rsp+0x18地址处值为5,即q:=int64(5)main.go:11      0x470b78        bb05000000              mov ebx, 0x5//令AX寄存器值等与0x5(5),为调用sub函数第二个参数main.go:11      0x470b7d        0f1f00                  nop dword ptr [rax], eaxmain.go:11      0x470b80        e83b000000              call $main.sub //调用sub本函数main.go:11      0x470b5c        4889442420              mov qword ptr [rsp+0x20], rax //将sub函数返回结果放到rsp+0x20地址处main.go:12      0x470b8f        4889442410              mov qword ptr [rsp+0x10], rax //令rsp+0x10地址处的值等与AX寄存器的值(莫名其妙的操作,大神知道这个操作做啥吗,评论区见)main.go:12      0x470b94        4883c428                add rsp, 0x30//SP寄存器值加上48,表示栈顶向上增加48字节,即将add函数申请48字节的栈内存归还main.go:12      0x470b98        5d                      pop rbp //出栈,即将栈顶值(此时其值是main函数栈帧起始位置的地址)设置给BP寄存器main.go:12      0x470b99        c3                      ret//返回到main函数调用add函数处继续执行main.go:8       0x470b9a        4889442408              mov qword ptr [rsp+0x8], rax //栈扩容前保存下AX寄存器的值,即参数a,因为栈扩容会覆盖AX寄存器的值main.go:8       0x470b9f        48895c2410              mov qword ptr [rsp+0x10], rbx//栈扩容前保存下BX寄存器的值,即参数b,因为栈扩容会覆盖BX寄存器的值main.go:8       0x470ba4        e837adffff              call $runtime.morestack_noctxt  //栈扩容main.go:8       0x470ba9        488b442408              mov rax, qword ptr [rsp+0x8] //扩容后重新设置参数a到AX寄存器main.go:8       0x470bae        488b5c2410              mov rbx, qword ptr [rsp+0x10] //扩容后重新设置参数b到BX寄存器main.go:8       0x470bb3        eb8b                    jmp $main.add //扩容后重新跳到add函数代码段(不是调用add函数),继续执行add函数
(dlv) c
> [Breakpoint 3] main.sub() ./main.go:15 (hits goroutine(1):1 total:1) (PC: 0x470bc4)10:     q:=int64(5)11:     q=sub(75, q)12:     return c13: }14:
=>  15: func sub(x, y int64) int64{16:   return x -y17: }
(dlv) disass
TEXT main.sub(SB) /home/gofunc/main.gomain.go:15      0x470bc0        55                      push rbpmain.go:15      0x470bc1        4889e5                  mov rbp, rsp
=>      main.go:15      0x470bc4*       4883ec08                sub rsp, 0x8//申请8字节栈内存main.go:15      0x470bc8        4889442418              mov qword ptr [rsp+0x18], raxmain.go:15      0x470bcd        48895c2420              mov qword ptr [rsp+0x20], rbxmain.go:15      0x470bd2        48c7042400000000        mov qword ptr [rsp], 0x0//令rsp地址处的值为0main.go:16      0x470bda        4829d8                  sub rax, rbx //计算x-y的值并将结果存到AX寄存器中,这样add函数从AX寄存器取sub函数返回值main.go:16      0x470bdd        48890424                mov qword ptr [rsp], rax//令rsp地址处的值为AX寄存器的值,也就是x-y的值main.go:16      0x470be1        4883c408                add rsp, 0x8 //归还8字节栈内存main.go:16      0x470be5        5d                      pop rbp//出栈,BP寄存器值恢复到add函数栈帧起始位置地址main.go:16      0x470be6        c3                      ret

通过观察三个函数的汇编,可以看到【栈的申请与归还操作】和【函数栈帧的恢复】:
栈申请:

        push rbp //入栈操作等价于【sub rsp 0x8;  mov rsp, rbp】,即申请8字节栈内存用于保存caller的函数栈帧起始位置地址
        mov rbp, rsp //保存callee的函数栈帧起始位置地址
        sub rsp, 0x28 //栈申请

栈归还:

        add rsp, 0x28  //栈归还
        pop rbp //出栈操作等价于【mov rbp, rsp; add rsp 0x8】,恢复caller的函数栈帧起始位置地址

 二、栈布局与函数栈帧

通过第一章对示例的汇编进行分析,我们可以画出其栈布局,假设main函数被调用时的起始地址是ox3e8(1000),那么我们可以得到下图:

 可以看到整体还是符合go源码给出的栈布局的:

// (x86)
// +------------------+
// | args from caller |
// +------------------+ <- frame->argp
// |  return address  |
// +------------------+
// |  caller's BP (*) | (*) if framepointer_enabled && varp > sp (暂存调用者函数栈帧起始位置地址)
// +------------------+ <- frame->varp (函数栈帧起始位置地址 BP寄存器)
// |     locals       |
// +------------------+
// |  args to callee  |
// +------------------+ <- frame->sp (栈顶,SP寄存器)

 一个函数栈帧由BP寄存器和SP寄存器确定。

C中函数栈帧是逐步扩张的(每定义一个变量就扩张一次), 但是go里面函数栈帧的扩张是一次性分配一大块(直接将栈指针移动到所需最大栈空间的位置,即栈顶),然后通过栈顶指针加偏移量这种相对寻址方式使用函数栈帧。

函数栈帧大小在编译期就可以确定!!! 对于栈消耗较大的函数,编译器会在函数头部插入检测代码,如果发现需要进行“栈扩容”,就会调用runtime.morestack相关函数重新分配一块足够大的栈空间, 将原来的数据拷过来并释放原来的空间。

http://www.dtcms.com/wzjs/181743.html

相关文章:

  • flowplayer wordpress海南seo
  • 医药网站文案编辑是怎么做的seo推广外包
  • 自己怎么做专属自己的网站网络营销活动策划方案
  • 山东通app官网下载二维码上海官网seo
  • 华耀建设网站竞价推广托管
  • 电子商务网站建设试卷与答案亚洲卫星电视网参数表
  • 成都公司网站seo山东seo首页关键词优化
  • 网站的url是什么百度广告语
  • 深圳专业网站建设公司排名郑州网络seo公司
  • 网站后台管理密码忘了扬州整站seo
  • 建设部网站撤销注册资质的都是公职人员吗搜索率最高的关键词
  • 国外对旅游网站建设的现状深圳网络推广渠道
  • 新疆建设云网站投诉电话新闻头条今日要闻最新
  • 复制网站 dedecmsseo工具优化软件
  • wordpress侧边栏滚动seo诊断工具网站
  • 网站做优化有用吗电商代运营一般收多少服务费
  • 百度站长工具有哪些成全在线观看免费高清动漫
  • wordpress产品图片怎么改sem对seo的影响有哪些
  • 咸宁市做网站网站设计方案模板
  • 策划公司排名seo引擎搜索入口
  • 商城网站源文件下载西地那非片的功能主治
  • 医院网站域名备案seo首页关键词优化
  • 婚恋公司销售好做吗seo黑帽多久入门
  • 郑州信息网平台无锡百度关键词优化
  • 找网络公司做的网站可以出售吗域名138查询网
  • 新疆住房建设部官方网站seo外包靠谱
  • 网站服务做推广公司网站怎么弄
  • 东坑东莞网站建设上优化seo
  • 网站建设开票写什么关键词广告
  • 企业做网站有发展么关键词优化seo费用