【嵌入式Linux学习七步曲之第四篇 Linux内核移植】GDB和BDI2000调试PPC Linux内核

本文主要是介绍【嵌入式Linux学习七步曲之第四篇 Linux内核移植】GDB和BDI2000调试PPC Linux内核,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

GDB和BDI2000调试PPC Linux内核

 

Sailor_forever  sailing_9806@163.com 转载请注明
http://blog.csdn.net/sailor_8318/archive/2009/11/10/4795287.aspx

 

【摘要】本文以MPC8270处理器和Linux2.6.15.5内核为例,讲述了用BDI2000和GDB联合调试Linux内核的基本方法。给出了BDI配置脚本,内核的配置选项及调试的具体步骤,为在PPC处理器上移植Linux内核奠定了基础。
【关键字】BDI2000  GDB  MPC8270  Linux MMU  PTBASE    SIGSTOP
 
目录

BDI2000配置脚本    1
内核修改及配置    3
编译选项    3
内核配置    4
内核调试    8
GDB script    8
未开启MMU    9
开启MMU    11


BDI2000配置脚本
; bdiGDB configuration file for PUC8270/80 Board and PPCBoot and MirrorBit-Flash
; Hard Reset Configuration Word = 0x0A07B000 (RSTCONF=LOW)
; ---------------------------------------------------------
;
[INIT]
; init core register
WREG    MSR             0x00000000
;      ;clear MSR

;IMMR : internal space @ 0xFFF00000 decided by ISB of Hard Reset Configuration Word
WM32    0xFFF10004    0xFFFFFFC3    ;SYPCR: disable watchdog
WM32    0xFFF10C80    0x00000001    ;SCCR : normal operation
WM32    0xFFF10024    0x00040000    ;BCR init
WM32    0xFFF10000    0x00000000    ;SIUMCR init
WM32    0xFFF10C94    0x00000000      ;RMR init

; init memory controller
; Flash S29GL128M10, 16MB, 100ns
WM32    0xFFF10104    0xFF001846    ;OR0: Data buffer dis., 6 w.s., ext.hold on read, tc=8*15ns=120ns
WM32    0xFFF10100    0x00001001    ;BR0: Flash @0x00000000, 16bit, no parity,default value for low boot memory mode instead of the eventual value

[TARGET]
CPUTYPE     8270        ;the CPU type (603EV,750,8240,8260)
JTAGCLOCK   0          ;use 16.6 MHz JTAG clock
BOOTADDR    0x00000100  ;boot address used for start-up break, decided by CIP of Hard Reset Configuration Word
BDIMODE     AGENT         ;the BDI working mode (LOADONLY | AGENT | GATEWAY)
BREAKMODE   HARD          ;SOFT or HARD, HARD uses PPC hardware breakpoints
STEPMODE    HWBP       ;TRACE or HWBP, HWPB uses a hardware breakpoint
MMU         XLAT       ;In order to support Linux kernel debugging when MMU is on, the BDI translates effective (virtual) to physical addresses.
PTBASE      0xf0       ;the physical memory address where the BDI looks for the virtual address of the array with the two page table pointers, setup in the kernel
;VECTOR      NOCATCH      ;catch unhandled exceptions
;DCACHE      NOFLUSH    ;data cache flushing (FLUSH | NOFLUSH)
POWERUP     3000        ;start delay after power-up detected in ms
REGLIST     ALL         ;select registers needed init to transfer to CPU
STARTUP         RESET  ;for programming
;STARTUP        RUN    ;for gdb debug
WAKEUP            1000
;MEMDELAY        2000


[HOST]
IP          150.236.68.171   ;tftp sever ip where to get download image
LOAD        MANUAL      ;load code MANUAL or AUTO after reset
DEBUGPORT   2001        ;network port for gdb debug
PROMPT      PUC8270>    ;new prompt for Telnet
DUMP        E:/temp/dump.bin

[FLASH]
CHIPTYPE    MIRRORX16   ;Flash type: AMD MirrorBit
CHIPSIZE    0x1000000   ;The size of one flash chip in bytes (e.g. S29GL128M10 = 0x1000000)
BUSWIDTH    16          ;The width of the flash memory bus in bits (8 | 16 | 32 | 64)
FILE        pboot.bin   ;default file to program, here is TMN PBOOT
FORMAT      BIN
;WORKSPACE   0xFFF00000    ;workspace in int. target RAM for fast download; download is stable without this, but fast with it.

;default for erase command, total size should be larger the size of download image
ERASE       0x00000000  ;erase sector (128K) of flash
ERASE       0x00020000  ;erase sector (128K) of flash
ERASE       0x00040000  ;erase sector (128K) of flash
ERASE       0x00060000  ;erase sector (128K) of flash
ERASE       0x00080000  ;erase sector (128K) of flash
ERASE       0x000a0000  ;erase sector (128K) of flash

[REGS]
DMM1        0xFFF00000  ;IMMR base
FILE        $reg8280.def
 
配置文件的规则可以参考http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx的4.9.1    BDI configuration file for debug章节
对于Linux内核打开MMU后的调试,需要在配置文件中打开相关配置选项:MMU和PTBASE
MMU    XLAT在MMU打开之后,BDI访问内存时,会根据BAT、内核和用户页表及默认的映射关系(KERNELBASE + 0x0FFFFFFF) to
0x00000000...0x0FFFFFFF将虚拟地址转换为物理地址。MMU未打开时,将不进行地址转换,此时因为符号表中的地址为虚拟地址,因此需要重定位符号表

PTBASE是存储内核页表和用户页表首地址的指针,关系如下:
PTBASE (physical address) ->
    PTE pointer pointer(virtual address) ->
           PTE kernel pointer (virtual address)
           PTE user pointer (virtual address)

PTBASE是物理地址,可以设置为MMU打开之后内核不再访问的一块内存中的任意地址,0-0x100在复位向量之下,启动之后不再使用。因此PTBASE可以设置为0-0x100之间的任意值,但是需要和 内核匹配,此值一般为0xf0。
 

内核修改及配置
编译选项
为了能够调试内核,需要生成各种调试信息,这可以通过更改内核根目录下面的Makefile实现
-g GDB调试必备的编译参数
-O 优化选项,Linux的优化级别一般为O2,会根据性能及代码大小进行相关优化,优化后的代码和C源代码会有不一致的情况,不便于单步调试
-fomit-frame-pointer 优化函数栈回溯,当有此选项时,函数调用时可能会把参数放在寄存器中,不便于查看函数调用时的参数传递关系,因此应该去掉。可以添加-fno-omit-frame-pointer选项
-gdwarf-2 可以在GDB中看出相关宏定义。正常情况下,宏定义在编译时会进行替换,添加-gdwarf-2可以生成相关宏信息。

上述编译选项可以通过CONFIG_DEBUG_INFO来统一控制,更改如下:
ifdef CONFIG_DEBUG_INFO
CFLAGS        += -O
else
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
CFLAGS        += -Os
else
CFLAGS        += -O2
endif
endif
#sailing cancel optimisation 20091103.

ifdef CONFIG_DEBUG_INFO
CFLAGS        += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
else
ifdef CONFIG_FRAME_POINTER
CFLAGS        += -fno-omit-frame-pointer $(call cc-option,-fno-optimize-sibling-calls,)
else
CFLAGS        += -fomit-frame-pointer
endif
endif
#sailing add stack trace 20091103

ifdef CONFIG_DEBUG_INFO
CFLAGS        += -g -gdwarf-2
endif
#sailing add -gdwarf-2 20091103

内核配置
CONFIG_DEBUG_INFO宏又是从哪里来的呢?它其实是从内核的配置文件.config里面来的。
make ARCH=ppc CROSS_COMPILE=ppc_82xx-  menuconfig

Kernel hacking  --->
[*] Kernel debugging
 [*] Include BDI-2000 user context switcher    

在2.6.15.5内核中,打开Kernel debugging选项后将自动打开Compile the kernel with debug info

保存后,以上选项会在根目录下的.config文件中生成如下配置选项:
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_MAGIC_SYSRQ is not set
CONFIG_LOG_BUF_SHIFT=14
CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_RCU_TORTURE_TEST is not set
CONFIG_WANT_EXTRA_DEBUG_INFORMATION=y
CONFIG_KGDB=y
CONFIG_KGDB_CONSOLE=y
CONFIG_KGDB_ONLY_MODULES=y
# CONFIG_XMON is not set
CONFIG_BDI_SWITCH=y


CONFIG_BDI_SWITCH选项是配合BDI2000的MMU和PTBASE参数的。其将自动在PTBASE所指向的物理内存处保存内核页表和用户页表的首地址。

arch/ppc/kernel/head.S
/* Load up the kernel context */
2:    bl    load_up_mmu

#ifdef CONFIG_BDI_SWITCH
    /* Add helper information for the Abatron bdiGDB debugger.
     * We do this here because we know the mmu is disabled, and
     * will be enabled for real in just a few instructions.
     */
    lis    r5, abatron_pteptrs@h
    ori    r5, r5, abatron_pteptrs@l
    stw    r5, 0xf0(r0)    /* This must match your Abatron config */
    lis    r6, swapper_pg_dir@h
    ori    r6, r6, swapper_pg_dir@l
    tophys(r5, r5)
    stw    r6, 0(r5)
#endif /* CONFIG_BDI_SWITCH */

swapper_pg_dir为内核页表首地址,r0为内核首地址的虚拟地址,r0+0xf0经过tophys后将转换为物理地址的0xf0,在此处存储swapper_pg_dir

在arch/ppc/kernel/head.S的最后通过abatron_pteptrs保留了8个字节的空间给BDI2000用于保存2个页表指针,如下:

/* Room for two PTE pointers, usually the kernel and current user pointers
 * to their respective root page table.
 */
abatron_pteptrs:
    .space    8

/*
 * Set up the segment registers for a new context.
 */
_GLOBAL(set_context)
    mulli    r3,r3,897    /* multiply context by skew factor */
    rlwinm    r3,r3,4,8,27    /* VSID = (context & 0xfffff) << 4 */
    addis    r3,r3,0x6000    /* Set Ks, Ku bits */
    li    r0,NUM_USER_SEGMENTS
    mtctr    r0

#ifdef CONFIG_BDI_SWITCH
    /* Context switch the PTE pointer for the Abatron BDI2000.
     * The PGDIR is passed as second argument.
     */
    lis    r5, KERNELBASE@h
    lwz    r5, 0xf0(r5)
    stw    r4, 0x4(r5)
#endif
进程切换时将当前进程的页表首地址存储在虚拟地址【KERNELBASE + 0xf0】指向的地址 + 4的偏移处,实际上就是abatron_pteptrs + 4对应的虚拟地址处。

内核调试
在内核开启MMU之前,采用的是物理地址,而符号表中的地址为虚拟地址,因此需要对符号表进行重定位,调试分两个阶段
相关规则可以参考
http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx
4.9.2    Debugging of U-Boot before relocation
同时可以利用脚本来加载符号,可参考
http://blog.csdn.net/sailor_8318/archive/2009/10/30/4745793.aspx
4.9.5    GDB script

GDB script
启动GDB时会自动调用内核根目录下的脚本.gdbinit,示例如下:
target remote 150.236.68.212:2001

define add-symbol_mmu_on
        d b
        symbol-file
        add-symbol-file vmlinux 0xC0000000
        b start_kernel
end

define reset
        d b
        symbol-file
        add-symbol-file vmlinux 0
        b __start
end

未开启MMU

PUC8270> 表示BDI2000的命令行窗口
cnbjc0052 sailing/linux-2.6.15.5>表示开发主机
(gdb) 表示是开发主机上的GDB的调试窗口中
=> 表示目标板的命令行

启动开发板
PUC8270>go
开发板启动后等待用户输入
=>
。。。。
Err:   serial
U-Boot relocated to 03fb7000
Net:   FCC1 ETHERNET
=>
停止板子的运行
PUC8270>halt
    Target CPU        : MPC8280/8220/5200 (Zeppo)
    Target state      : debug mode
    Debug entry cause : COP halt
    Current PC        : 0x03feb514
    Current CR        : 0x22000008
    Current MSR       : 0x0000a002
    Current LR        : 0x03fd540c
在__start处设置断点,注意需要先halt,否则断点未能真正设置生效
PUC8270>bi 0xc
Breakpoint identification is 1
重新使开发板运行
PUC8270>go
启动Linux内核
=> boot
*  kernel: cmdline image address = 0x00400000
## Booting kernel from Legacy Image at 00400000 ...
   Image Name:   Linux-2.6.19
   Created:      2009-11-03   8:09:41 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1285009 Bytes =  1.2 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   kernel data at 0x00400040, len = 0x00139b91 (1285009)
   Uncompressing Kernel Image ... OK
   kernel loaded at 0x00000000, end = 0x002cf086
## Current stack ends at 0x03f96530
## cmdline at 0x007fff00 ... 0x007fff00
## kernel board info at 0x007ffeb0
bd address  = 0x03F96AB6
。。。。。。。。
baudrate    =  19200 bps
## No init Ramdisk
   ramdisk start = 0x00000000, ramdisk end = 0x00000000
## initrd_high = 0xffffffff, copy_to_ram = 1
   ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## Transferring control to Linux (at address 00000000) ...
   Booting using board info...

开发板在0xc断点处停止
-    TARGET: stopped
清除断点,因为8270只支持一个硬件断点,否则后面无法单步调试
PUC8270>ci
连接开发板
cnbjc0052 sailing/linux-2.6.19> ppc_82xx-gdb vmlinux
GNU gdb Red Hat Linux (6.7-1rh)
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=ppc-linux".
The target architecture is set automatically (currently powerpc:603)
..
0x00000000 in ?? ()
调用reset宏,更新符号表
(gdb) reset
add symbol table from file "vmlinux" at
        .text_addr = 0x0
Reading symbols from /home/sailing/ linux-2.6.15.5/vmlinux...done.
(gdb)

115             .globl  __start
116     __start:
117             mr      r31,r3                  /* save parameters */
118             mr      r30,r4
119             mr      r29,r5
120             mr      r28,r6

当需要调试MMU开启之后的代码,使用下述宏即可
(gdb) add-symbol_mmu_on


开启MMU
PUC8270>go
开发板启动后等待用户输入
=>
。。。。
Err:   serial
U-Boot relocated to 03fb7000
Net:   FCC1 ETHERNET
=>
停止板子的运行
PUC8270>halt
    Target CPU        : MPC8280/8220/5200 (Zeppo)
    Target state      : debug mode
    Debug entry cause : COP halt
    Current PC        : 0x03feb514
    Current CR        : 0x22000008
    Current MSR       : 0x0000a002
    Current LR        : 0x03fd540c
获得MMU开启之后start_kernel的首地址
cnbjc0052 sailing/linux-2.6.15.5> cat System.map | grep start_kernel
c02b2610 T start_kernel
在start_kernel处设置断点,注意需要先halt,否则断点未能真正设置生效
PUC8270>bi 0xc02b2610
Breakpoint identification is 1

重新使开发板运行
PUC8270>go
启动Linux内核
=> boot
*  kernel: cmdline image address = 0x00400000
## Booting kernel from Legacy Image at 00400000 ...
   Image Name:   Linux-2.6.19
   Created:      2009-11-03   8:09:41 UTC
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    1285009 Bytes =  1.2 MB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   kernel data at 0x00400040, len = 0x00139b91 (1285009)
   Uncompressing Kernel Image ... OK
   kernel loaded at 0x00000000, end = 0x002cf086
## Current stack ends at 0x03f96530
## cmdline at 0x007fff00 ... 0x007fff00
## kernel board info at 0x007ffeb0
bd address  = 0x03F96AB6
。。。。。。。。
baudrate    =  19200 bps
## No init Ramdisk
   ramdisk start = 0x00000000, ramdisk end = 0x00000000
## initrd_high = 0xffffffff, copy_to_ram = 1
   ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## Transferring control to Linux (at address 00000000) ...
   Booting using board info...

开发板在start_kernel断点处停止
-    TARGET: stopped
清除断点,因为8270只支持一个硬件断点,否则后面无法单步调试
PUC8270>ci
连接开发板

cnbjc0052 sailing/linux-2.6.19> ppc_82xx-gdb vmlinux
GNU gdb Red Hat Linux (6.7-1rh)
Copyright (C) 2007 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=ppc-linux".
The target architecture is set automatically (currently powerpc:603)
..
0xc02b2610 in ?? ()
start_kernel () at init/main.c:479
479     {
(gdb) l 479
474     void __init __attribute__((weak)) smp_setup_processor_id(void)
475     {
476     }
477
478     asmlinkage void __init start_kernel(void)
479     {
480             char * command_line;
481             extern struct kernel_param __start___param[], __stop___param[];
482
483             smp_setup_processor_id();
(gdb)

便可开始内核的调试了,大部分板级的初始化代码将在这之后运行。

若BDI2000的配置脚本中没有MMU和PTBASE选项或者CONFIG_BDI_SWITCH未打开,则在MMU开启之后,使能定时器中断后,将出现异常,无法单步调试。现象如下:
491             time_init();
(gdb)

Program received signal SIGSTOP, Stopped (signal).
time_init () at arch/ppc/kernel/time.c:322
322                             sec = ppc_md.get_rtc_time();
(gdb) n
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)
322                             sec = ppc_md.get_rtc_time();
(gdb)
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c244 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc01bd3fc in time_init () at arch/ppc/kernel/time.c:323
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
time_init () at arch/ppc/kernel/time.c:321
321                             elapsed += stamp - old_stamp;
(gdb)
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc000c248 in tqm8260_get_rtc_time () at arch/ppc/platforms/tqm8260_setup.c:36
36      }
(gdb)

Program received signal SIGSTOP, Stopped (signal).
0xc01bd3fc in time_init () at arch/ppc/kernel/time.c:323
323                     } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);

这篇关于【嵌入式Linux学习七步曲之第四篇 Linux内核移植】GDB和BDI2000调试PPC Linux内核的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/284512

相关文章

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与