《嵌入式Linux开发实用教程》——1.4 映像文件的生成和运行

简介:

本节书摘来自异步社区《嵌入式Linux开发实用教程》一书中的第1章,第1.4节,作者 朱兆祺,李强,袁晋蓉,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.4 映像文件的生成和运行

德国罕见的科学大师莱布尼茨,在他的手迹里留下这么一句话:“1与0,一切数字的神奇渊源。这是造物的秘密美妙的典范,因为,一切无非都来自上帝。”二进制0和1两个简单的数字,构造了神奇的计算机世界,对人类的生产活动和社会活动产生了极其重要的影响,并以强大的生命力飞速发展。在嵌入式系统移植过程中,不管文件数量多么庞大,经过编译工具的层层处理后,最终生成一个可以加载到存储器内执行的二进制映像文件(.bin)。本节内容将会探讨映像文件的生成过程,以及它在存储设备的不同位置对程序运行产生的影响,为本书后文嵌入式系统的移植打下坚定的基础。

1.4.1 编译过程

GNU提供的编译工具包括汇编器as、C编译器gcc、C++编译器g++、链接器ld、二进制转换工具objcopy和反汇编的工具objdump等。它们被称作GNU编译器集合,支持多种计算机体系类型。基于ARM平台的工具分别为arm-linux-gcc、arm-linux-g++、arm-linux-ld、arm-linux-objcopy和arm-linux-objdump。arm-linux交叉编译工具链的制作方法已经详细介绍过了,编译程序直接使用前面制作好的工具链。

GNU编译器的功能非常强大,程序可以用C文件、汇编文件编写,甚至是二者的混合。如图1.3所示是程序编译的大体流程,源文件经过预处理器、汇编器、编译器、链接器处理后生成可执行文件,再由二进制转换工具转换为可用于烧写到Flash的二进制文件,同时为了调试的方便还可以用反汇编工具生成反汇编文件。图中双向箭头的含义是,当gcc增加一些参数时可以相互调用汇编器和链接器进行工作。例如输入命令行“gcc –O main.c”后,直接就得到可执行文件a.out(elf)。


screenshot

程序编译大体上可以分为编译和链接两个步骤:把源文件处理成中间目标文件.o(linux)、obj(windows)的动作称为编译;把编译形成的中间目标文件以及它们所需要的库函数.a(linux)、lib(windows)链接在一起的动作称为链接。现用一个简单的test工程来分析程序的编译流程。麻雀虽小,五脏俱全,它由启动程序start.S、应用程序main.c、链接脚本test.lds和Makefile四个文件构成。test工程中的程序通过操作单板上的LED灯的状态来判定程序的运行结果,它除了用于理论研究之外,没有其他的实用价值。

1.编译

在编译阶段,编译器会检查程序的语法、函数与变量的声明情况等。如果检查到程序的语法有错误,编译器立即停止编译,并给出错误提示。如果程序调用的函数、变量没有声明原型,编译器只会抛出一个警告,继续编译生成中间目标文件,待到链接阶段进一步确定调用的变量、函数是否存在。

start.S文件的内容如程序清单1.1所示,文件中的_start函数,为程序能够在C语言环境下运行做了最低限度的初始化:将S3C6410处理外设端口的地址范围告知ARM内核,关闭看门狗,清除bss段,初始化栈。初始化工作完毕后,跳转到main()。start.S是用汇编语言编写的代码文件,文件中定义了一个WATCHDOG宏,用于寄存器的赋值。在汇编文件中出现#define宏定义语句,对于初学者可能会有些迷惑。

程序清单1.1 start.S中汇编代码

/*
 *   This is a part of the test project 
 *   Author: LiQiang Date: 2013/04/01
 *   Licensed under the GPL-2 or later.
 */
.globl _start
_start:

  #define REG32 0x70000000
  ldr r0, =REG32
  orr r0, r0, #0x13
  mcr p15,0,r0,c15,c2,4  

  /*关闭看门狗*/
  #define WATCHDOG 0x7E004000
  ldr r0, =WATCHDOG
  mov r1, #0
  str r1, [r0]

clean_bss:
   ldr r0, =bss_start
   ldr r1, =bss_end
   mov r3, #0
   cmp r0, r1
   beq clean_done
clean_loop:
   str r3, [r0], #4
   cmp r0, r1  
   bne clean_loop    
clean_done:  

   /* 初始化栈 S3C6410 8K的SRAM映射到0地址处*/
   ldr sp, =8*1024
   bl main 
halt:
   b halt

事实上,汇编文件有“.S”和“.s”两种后缀,在以“.s”为后缀的汇编文件中,程序完全是由纯粹的汇编代码编写的。所谓的纯粹是相对以“.S”为后缀的汇编文件而言的,由于现代汇编工具引入了预处理的概念,允许在汇编代码(.S)中使用预处理命令。预处理命令以符号“#”开头,包括宏定义、文件包含和条件编译。在U-Boot和Linux内核源码中,这种编程方式运用非常广泛。

main.c文件内容如程序清单1.2所示,main.c中的main函数是运行完_start函数的跳转点。main()中首先定义了一个静态局部变量,初值为12,然后配置S3C6410处理器的GPM端口为输出、下拉模式,并将GPM端口低四位对应的管脚设为高电平(LED驱动管脚的电平为高时,LED熄灭)。最后判断flag是否等于12,如果等于就点亮LED,否则不点亮。从程序上看,这个判断语句好像多此一举、莫名其妙,因为flag期间并没有做任何改变。其实,这个变量是为讲解程序的运行地址和加载地址的概念而定义的,它与程序运行的位置有关。

程序清单1.2 main.c文件内容

/*
 * This is a part of the test project 
 *   Author: LiQiang Date: 2013/04/01
 *   Licensed under the GPL-2 or later.
 */
#define GPMCON *((volatile unsigned long*)0x7F008820)
#define GPMDAT *((volatile unsigned long*)0x7F008824)
#define GPMPUD *((volatile unsigned long*)0x7F008828)
int main()
{
   static int flag = 12;
   GPMCON = 0x1111; /* 输出模式 */ 
   GPMPUD = 0x55;  /* 使能下拉 */
   GPMDAT = 0x0f;  /* 关闭LED */
   if(12 == flag)
     GPMDAT = 0x00; 
   else 
     GPMDAT = 0x0f;
   while(1);
   return 0;
}

将上面两个源码文件处理成中间目标文件,分别输入如下命令行:

arm-linux-gcc -o mian.o main.c –c
arm-linux-gcc -o start.o start.S –c

得到main.o、Start.o两个中间目标文件,供链接器使用。

2.链接

链接是汇编阶段生成的中间目标文件,相互查找自己所需要的函数与变量,重定向数据,完成符号解析的过程。包括对所有目标文件进行重定位、建立符号引用规则,同时为变量、函数等分配运行地址。函数与变量可能来源于其他中间文件或者库文件,如果没有找到所需的实现,链接器立即停止链接,给处错误提示。

利用一个链接脚本(.lds后缀)来指导链接器工作。控制输出节(Outpat section)在映像文件中的布局。fortest.lds是一个简单的链接脚本,指示了程序的运行地址(又称链接地址)为0x5000_0000以及text段、data段和bss段在映像文件中的空间排布顺序。fortest.lds文件的内容如下:

ENTRY(_start)
SECTIONS
{
    . = 0x50000000;
    . = ALIGN(4);
    .text : {
        start.o (.text)
        * (.text)
    }

    .data : {
        * (.data)
    }

    bss_start = .;
    .bss : {
        * (.bss)
    }
    bss_end = .;
}

(1)text段代码段(text segment),通常是用来存放程序执行代码的内存区域。这块区域的大小在程序编译时就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量。

(2)data段数据段(data segment),数据段是存放已经初始化不为0的静态变量的内存区域,静态变量包括全局变量和局部变量,它们与程序有着相同的生存期。

(3)bss段bss segment,bbs段与data段类似,也是存放的静态变量的内存区域。与data段不同的是,bbs段存放的是没有初始化或者初始化为0的静态变量,并且bbs段不在生成的可执行二进制文件内。bss_start表示这块内存区域的起始地址,bss_end表示结束地址,它们由编译系统计算得到。未初始化的静态变量默认为0,因此程序开始执行的时候,在bss_start到bss_end内存中存储的数据都必须是0。

(4)其他段,上面3个段是编译系统预定义的段名,用户还能通过.section伪操作自定义段,在后面的移植过程中会发现,Linux内核源码中为了合理地排布数据实现特定的功能,定义了各种各样的段。

在宿主机上输入以下命令行,完成中间的目标文件的链接和可执行二进制文件的格式转换。

arm-linux-ld –T test.lds -o test.elf start.o main.o
arm-linux-objcopy -O binary test.elf test.bin
arm-linux-objdump -D test.elf > test.dis

如图1.4所示是使用arm-linux-objcopy格式转换工具得到的二进制文件test.bin的内容,这些内容是处理器能够识别的机器码,我们往往难以直接阅读、理解它们的含义。使用arm-linux-objdump工具生成便于我们阅读的反汇编文件test.dis。


screenshot

对比二进制文件test.bin的内容,耐心细致地分析反汇编文件,如程序清单1.3所示,可以提炼出大量的信息。

程序清单1.3 text.dis文件内容

50000000 <_start>: /* 代码段起始位置程序的运行地址为0x5000_0000*/
50000000:  e3a00207   mov  r0, #1879048192  ; 0x70000000
50000004:  e3800013   orr  r0, r0, #19  ; 0x13
50000008:  ee0f0f92   mcr  15, 0, r0, cr15, cr2, {4}
5000000c:  e59f0030   ldr  r0, [pc, #48]  ; 50000044 <halt+0x4>
50000010:  e3a01000   mov  r1, #0  ; 0x0
50000014:  e5801000   str  r1, [r0]

50000018 <clean_bss>: /* 清除bss段 */
50000018:  e59f0028   ldr  r0, [pc, #40]  ; 50000048 <halt+0x8>
5000001c:  e59f1028   ldr  r1, [pc, #40]  ; 5000004c <halt+0xc>
50000020:  e3a03000   mov  r3, #0  ; 0x0
50000024:  e1500001   cmp  r0, r1
50000028:  0a000002   beq  50000038 <clean_done>

5000002c <clean_loop>:
5000002c:  e4803004   str  r3, [r0], #4
50000030:  e1500001   cmp  r0, r1
50000034:  1afffffc   bne  5000002c <clean_loop>

50000038 <clean_done>:
50000038:  e3a0da02   mov  sp, #8192  ; 0x2000 /* 初始化sp */
5000003c:  eb000003   bl  50000050 <main> /* 跳转至mian() */

50000040 <halt>:
50000040:  eafffffe   b  50000040 <halt>
50000044:  7e004000   .word  0x7e004000
50000048:  500000e0   .word  0x500000e0
5000004c:  500000e0   .word  0x500000e0

50000050 <main>: /* main()*/
50000050:  e52db004   push  {fp}    ; (str fp, [sp, #-4]!)
50000054:  e28db000   add  fp, sp, #0  ; 0x0
50000058:  e3a0247f   mov  r2, #2130706432  ; 0x7f000000
5000005c:  e2822b22   add  r2, r2, #34816  ; 0x8800
50000060:  e2822020   add  r2, r2, #32  ; 0x20
50000064:  e3a03c11   mov  r3, #4352  ; 0x1100
50000068:  e2833011   add  r3, r3, #17  ; 0x11
5000006c:  e5823000   str  r3, [r2] /* GPMCON = 0x1111 */
50000070:  e3a0347f   mov  r3, #2130706432  ; 0x7f000000
50000074:  e2833b22   add  r3, r3, #34816  ; 0x8800
50000078:  e2833028   add  r3, r3, #40  ; 0x28
5000007c:  e3a02055   mov  r2, #85  ; 0x55
50000080:  e5832000   str  r2, [r3] /* GPMPUD = 0x55 */
50000084:  e3a0347f   mov  r3, #2130706432  ; 0x7f000000
50000088:  e2833b22   add  r3, r3, #34816  ; 0x8800
5000008c:  e2833024   add  r3, r3, #36  ; 0x24
50000090:  e3a0200f   mov  r2, #15  ; 0xf
50000094:  e5832000   str  r2, [r3] /* GPMDAT = 0x0f */
50000098:  e59f3038   ldr  r3, [pc, #56]  ; 500000d8 <main+0x88> /* 读取flag变量存储地址 */
5000009c:  e5933000   ldr  r3, [r3]/* 读取flag变量的值 */
500000a0:  e353000c   cmp  r3, #12  ; 0xc
500000a4:  1a000005   bne  500000c0 <main+0x70>
500000a8:  e3a0347f   mov  r3, #2130706432  ; 0x7f000000
500000ac:  e2833b22   add  r3, r3, #34816  ; 0x8800
500000b0:  e2833024   add  r3, r3, #36  ; 0x24
500000b4:  e3a02000   mov  r2, #0  ; 0x0
500000b8:  e5832000   str  r2, [r3]
500000bc:  ea000004   b  500000d4 <main+0x84>
500000c0:  e3a0347f   mov  r3, #2130706432  ; 0x7f000000
500000c4:  e2833b22   add  r3, r3, #34816  ; 0x8800
500000c8:  e2833024   add  r3, r3, #36  ; 0x24
500000cc:  e3a0200f   mov  r2, #15  ; 0xf
500000d0:  e5832000   str  r2, [r3]
500000d4:  eafffffe   b  500000d4 <main+0x84>
500000d8:  500000dc   .word  0x500000dc
Disassembly of section .data:

500000dc <flag.1245>: /* flag变量的地址为0x5000_00dc,值为12 */
500000dc:  0000000c   .word  0x0000000c

从test.dis反汇编文件中可知,test.bin包含了代码段和数据段,并没有包含bss段。我们知道,bbs内存区域的数据初始值全部为零,区域的起始位置和结束位置在程序编译的时候预知。很容易想到在程序开始运行时,执行一小段代码将这个区域的数据全部清零即可,没必要在test.bin包含全为0的bss段。编译器的这种机制有效地减小了镜像文件的大小,节约了磁盘容量。

main()函数的核心功能是验证flag变量是否等于12,现在追踪下这个操作的实现过程。要想读取flag的值,必须知道它的存储位置,首先执行指令“ldrr3, [pc, #56]”得到flag变量的地址(指针)。pc与56相加合成一个地址,它是相对pc偏移56产生的。pc+56地址处存放了flag变量的指针0x5000_00dc,读取出来存放到r3寄存器。然后执行指令“ldrr3, [r3]”,将内存0x5000_00dc地址处的值读出,这个值就是flag,并覆盖r3寄存器。最后,判断r3寄存器是否等于12。flag变量的地址在链接阶段已经被分配好了,固定在0x5000_00dc处,但是从代码中,我们没有找到对flag变量赋初值的语句,尽管在main函数已经用C语句“flag = 12”对它赋初值。

现提供一个验证程序效果的简单方法:将S3C6410处理器设置为SD卡启动方式,使用SD_Writer软件将test.bin烧写至SD卡中,然后将SD卡插入单板的卡槽,复位启动即可。实际上,启动的时候test.bin被加载到内部SRAM中,SRAM映射到0地址处。这个简单方法可以用来验证一些裸板程序,方法实现的原理和SD_Writer软件用法现在不展开讨论,目前只要会使用即可。复位后,LED并没有点亮。

如果每次编译都要重复输入编译命令,操作起来很麻烦,为此test工程中建立了一个Makefile文件,内容如下:

test.bin: start.o main.o  
   arm-linux-ld -T fortest.lds -o test.elf start.o main.o  
   arm-linux-objcopy -O binary test.elf test.bin  
   arm-linux-objdump -D test.elf > test.dis

start.o : start.S  
   arm-linux-gcc -o start.o start.S -c
main.o : main.c  
   arm-linux-gcc -o main.o main.c -c

clean:  
   rm *.o test.*

当将链接脚本中的运行地址修改为0时,进入test目录,输入“make clean”命令清除旧的文件,再输入“make”重新编译程序,验证新生成的test.bin文件的效果,发现LED全部点亮,产生这个现象的原因在下一个小节讲述。

1.4.2 代码搬运

当程序执行时,必须把代码搬运到链接时所指定的运行地址空间,以保证程序在执行过程中对变量、函数等符号的正确引用。在带有操作系统的嵌入式系统中,这个过程由操作系统负责完成。而在裸机环境下,镜像文件的运行地址由程序员根据具体平台指定,加载地址又与处理器的设计密切相关。通常情况下,启动代码最先执行一段位置无关码,这段代码实现程序从加载地址到运行地址的重定位,或者将程序从外部存储介质直接拷贝至其运行地址。

1.位置无关码

位置无关码必须具有位置无关的跳转、位置无关的常量访问等特点,不能访问静态变量,都是相对pc的偏移量来进行函数的跳转或者常量的访问。在ARM 体系中,使用相对跳转指令b/bl实现程序跳转。指令中所跳转的目标地址用基于当前PC的偏移量来表示,与链接时分配给地址标号的绝对地址值无关,因而代码可以在任何位置正确的跳转,实现位置无关性。

使用ldr伪指令将一个常量读取到非pc的其他通用寄存器中,可实现位置无关的常量访问。例如:

ldr r0, =WATCHDOG

如果使用ldr伪指令将一个函数标号读取到pc,这是一条与位置有关的跳转指令,执行的结果是跳转到函数的运行地址处。

2.运行地址与加载地址

试想一下,当系统上电复位的时候,如果test.bin刚好位于0x5000_0000地址(flag的初值12位于0x5000_00dc),PC指向0x5000_0000地址,那么这段代码按照上述flag变量的读取步骤,能够准确无误地得到结果。但是,如果test.bin位于0地址(flag的初值12位于0xdc,LED不亮时的情况),PC指向0地址,程序依然从0x5000_00dc地址读取flag变量,实际上它的初值位于0xdc。这时从C语言的角度看,出现一个flag不等于它的初值的现象(期间没有改变flag)。出现错误的原因是在程序中使用了位置相关的变量,但运行地址与加载地址不一致(加载地址为0,运行地址为0x5000_0000)。由此,能够容易理解运行地址和加载地址的含义:

加载地址是系统上电启动时,程序被加载到可直接执行的存储器的地址,也就是程序在RAM或者Flash ROM中的地址。因为有些存储介质只能用来存储数据不能执行程序,例如SD卡和NAND Flash等,必须把程序从这些存储介质加载到可以执行的地址处。运行地址就是程序在链接时候确定的地址,比如fortest.lds链接脚本指定了程序的运行地址为0x5000_0000,那么链接器在为变量、函数等分配地址的时候就会以0x5000_0000作为参考。当加载地址和运行地址不相等时,必须使用与位置无关码把程序代码从它的加载地址搬运至运行地址,然后使用“ldr pc, =label”指令跳转到运行地址处执行。

1.4.3 混合编程

在嵌入式系统底层编程中,C语言和汇编两种编程语言的使用最广泛。C语言开发的程序具有可读性高,容易修改、移植和开发周期短等特点。但是,C语言在一些场合很难或无法实现特定的功能:底层程序需要直接与CPU内核打交道,一些特殊的指令在C语言中并没有对应的成分,例如关闭看门狗、中断的使能等;被系统频繁调用的代码段,对代码的执行效率要求严格的时候。事实上,CPU体系结构并不一致,没有对内部寄存器操作的通用指令。汇编语言与CPU的类型密切相关,提供的助记符指令能够方便直接地访问硬件,但要求开发人员对CPU的体系结构十分熟悉。在早期的微处理器中,由于处理器速度、存储空间等硬件条件的限制,开发人员不得不选用汇编语言开发程序。随着微处理器的发展,这些问题已经得到很好的解决。如果依然完全使用汇编语言编写程序,工作量会非常大,系统很难维护升级。大多数情况下,充分结合两种语言的特点,彼此相互调用,以约定规则传递参数,共享数据。

1.汇编函数与C语言函数相互调用

C程序函数与汇编函数相互调用时必须严格遵循ATPCS(ARMThumb Procedure Call Standard)。函数间约定R0、R1和R2为传入参数,函数的返回值放在R0中。GNU ARM编译环境中,在汇编程序中要使用.global伪操作声明改汇编程序为全局的函数,可被外部函数调用。在C程序中要被汇编程序调用的C函数,同样需要用关键字extern声明。

程序清单1.4是从archarmcpuarm1176start.S文件(U-Boot)中截取的代码片段,relocate_code函数用于重定位代码。它在C程序中,通过relocate_code(addr_sp, id, addr)被调用。变量addr_sp、id和addr分别通过寄存器R0、R1和R3传递给汇编程序,实现了C函数和汇编函数数据的共享。

程序清单1.4 代码重定位函数

.globl  relocate_code
relocate_code:  
   mov  r4, r0  /* save addr_sp */  
   mov  r5, r1  /* save addr of gd */  
   mov  r6, r2  /* save addr of destination */

   /* Set up the stack  */
stack_setup:  
   mov  sp, r4

   adr  r0, _start  
   cmp  r0, r6  
   beq  clear_bss    /* skip relocation */  
   mov  r1, r6      /* r1 <- scratch for copy_loop */  
   ldr  r3, _bss_start_ofs  
   add  r2, r0, r3    /* r2 <- source end address    */
……

2.C语言内嵌汇编

当需要在C语言程序中内嵌汇编代码时,可以使用gcc提供的asm语句功能。

程序清单1.5是从Linux源码文件arch/arm/include/asm/atomic.h截取的一段代码,本节内容不分析函数的具体实现。对于初学者,这段代码看起来晦涩难懂,因为这不是标准C所定义的形式,而是gcc对C语言扩充的asm功能语句,用以在C语言程序中嵌入汇编代码。

程序清单1.5 整数原子加操作的实现

/*
 * ARMv6 UP and SMP safe atomic ops. We use load exclusive and
 * store exclusive to ensure that these are atomic. We may loop
 * to ensure that the update happens.
 */
static inline void atomic_add(int i, atomic_t *v)
{  
   unsigned long tmp;  
   int result;

   __asm__ __volatile__("@ atomic_add\n"
"1:  ldrex  %0, [%3]\n"
"  add  %0, %0, %4\n"
"  strex  %1, %0, [%3]\n"
"  teq  %1, #0\n"
"  bne  1b"  
   : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)  
   : "r" (&v->counter), "Ir" (i)  
   : "cc");
}

asm语句最常用的格式为:

__asm __ __volatile__(" inst1 op1, op2, . \n" 
" inst2 op1, op2, . \n"      /* 指令部分必选*/
   ...
" instN op1, op2, . \n" 
   : output_operands  /* 输出操作数可选 */
   : input_operands    /* 输入操作数可选 */
   : clobbered_operands  /* 损坏描述部分可选*/
);

它由4个部分组成:指令部分、输出部分、输入部分和损坏描述部分。各部分使用“:”隔开,指令部分必不可少,其他3部分可选。但是如果使用了后面的部分,而前面部分为空,也需要用“:”分隔,相应部分内容为空。asm表示汇编语句的起始,volatile是一个可选项,加上它可以防止编译器优化时对汇编语句删除、移动。

指令部分,指令之间使用“n”(也可以使用“;”或者“nt”)分隔。嵌入汇编指令的格式与标准汇编指令的格式大体相同,嵌入汇编指令的操作数使用占位符“%”预留位置,用以引用C语言程序中的变量。操作数占位符的数量取决于CPU中通用寄存器的总数量,占位符的格式为%0,%1,……,%n。

输出、输入部分,这两部分用于描述操作数,不同的操作数描述语句之间用逗号分隔,每个操作数描述符由限定字符串和C语言表达式组成,当限定字符串中带有“=”时表示该操作数为输出操作数。限定字符串用于指示编译器如何处理C语言表达式与指令操作数之间的关系,限定字符串中的限定字母有很多种,有些是通用的,有些跟特定的体系相关。在程序清单1.5中:result、tmp和v->counter是输出操作数,分别赋给%0、%1和%2;v->counter和i是输入操作数,分别赋给%3和%4。其中,“r”表示把变量放入通用寄存器中;“I”表示0-31之间的常数。

相关文章
|
2天前
|
Linux API C语言
C语言读写BMP文件-EasyBmp【 linux 平台】
**EasyBmp** 是一个49KB的轻量级C++图像处理库,专注于BMP格式,提供简单易用的API。它的特点是小巧、开源、易于理解和高度定制。通过示例代码展示了如何轻松读取、缩放和保存BMP图像。适合需要高效处理BMP图像的开发者。
|
3天前
|
Linux Shell
蓝易云 - Linux下查看文件和文件夹大小
这两个命令是查看Linux系统中文件和文件夹大小的基本工具,可以根据需要进行组合使用。
5 0
|
3天前
|
应用服务中间件 Linux nginx
蓝易云 - Linux使用pid文件结束nginx
以上就是使用pid文件结束nginx进程的方法。
5 0
|
4天前
|
缓存 Linux Windows
初识Linux操作系统(根目录下的重要文件)(命令提示符的含义)
Linux系统基于&quot;一切皆文件&quot;的理念,重要文件分布在如/root(root用户目录)、/home(普通用户目录)、/etc(应用配置)、/dev(设备文件)、/boot(内核及启动文件)、/proc(动态系统信息)、/lib64(库文件)、/opt(软件存放)、/tmp(临时文件)。&quot;[root@localhost ~]#&quot;代表管理员在root目录,&quot;$&quot;代表普通用户。创建新用户用`useradd`命令。调节终端字体大小:Ctrl+Shift++增大,Ctrl+减号缩小。绝对路径从根目录开始,相对路径从当前目录开始。
|
4天前
|
Web App开发 安全 Linux
FFmpeg开发笔记(二十六)Linux环境安装ZLMediaKit实现视频推流
《FFmpeg开发实战》书中介绍轻量级流媒体服务器MediaMTX,但其功能有限,不适合生产环境。推荐使用国产开源的ZLMediaKit,它支持多种流媒体协议和音视频编码标准。以下是华为欧拉系统下编译安装ZLMediaKit和FFmpeg的步骤,包括更新依赖、下载源码、配置、编译、安装以及启动MediaServer服务。此外,还提供了通过FFmpeg进行RTSP和RTMP推流,并使用VLC播放器拉流的示例。
19 3
FFmpeg开发笔记(二十六)Linux环境安装ZLMediaKit实现视频推流
|
5天前
|
编解码 Linux
FFmpeg开发笔记(二十五)Linux环境给FFmpeg集成libwebp
《FFmpeg开发实战》书中指导如何在Linux环境下为FFmpeg集成libwebp以支持WebP图片编解码。首先,从GitHub下载libwebp源码,解压后通过`libtoolize`,`autogen.sh`,`configure`,`make -j4`和`make install`步骤安装。接着,在FFmpeg源码目录中重新配置并添加`--enable-libwebp`选项,然后进行`make clean`,`make -j4`和`make install`以编译安装FFmpeg。最后,验证FFmpeg版本信息确认libwebp已启用。
15 1
FFmpeg开发笔记(二十五)Linux环境给FFmpeg集成libwebp
|
1天前
|
Linux 数据处理 数据安全/隐私保护
Linux中的groups命令:管理用户组信息的利器
`groups`命令在Linux中用于显示用户所属的用户组,帮助管理员进行权限管理。它读取`/etc/group`和`/etc/passwd`文件获取信息,特点是简单直观,支持多用户组。命令参数如`-a`显示主组,`-g`显示主组ID,`-n`以数字形式显示,`-r`显示实际组。在实际应用中,结合其他命令可进行权限分析和定制输出。注意权限问题及用户组可能随系统变化。
|
1天前
|
关系型数据库 Linux 数据处理
深入了解Linux命令gprof:数据处理和分析利器
gprof是Linux下的一款命令行工具,用于分析程序性能,找出代码瓶颈。它通过分析函数调用和执行时间,提供函数级别的性能报告和图形化展示。使用gprof需在编译时添加`-pg`选项,然后运行程序并用gprof生成报告。注意覆盖所有执行路径,并可与其他性能工具结合使用,以优化代码性能。
|
1天前
|
安全 算法 Linux
探索Linux命令gpgv2:安全通信与数据验证的利器
`gpgv2`是GPG的签名验证工具,用于确保文件完整性和来源真实性。基于公钥密码学,支持多种加密算法和OpenPGP标准。通过`--verify`等参数验证签名,例如`gpgv2 --verify signature_file file`。重要注意事项包括保护私钥、定期更新密钥、验证签名来源及使用强密码。在数据安全场景中,`gpgv2`是保障信息安全的利器。
|
1天前
|
安全 Linux 数据处理
深入探索Linux中的gpgsplit命令
`gpgsplit`是GPG套件的一部分,用于分割大型加密文件或合并加密的OpenPGP消息。它支持ASCII armored和二进制格式,提供按字节数、行数或消息数分割的灵活性,并可合并消息。在处理大型加密文件、安全管理及数据传输中发挥作用。使用时注意保护私钥、备份数据、正确指定格式,并遵循安全实践。示例:使用`--split 10M`将大文件按10MB分割,`cat`多个消息文件并用`gpgsplit --output`合并。