当前位置:文档之家› linux下可执行文件分析

linux下可执行文件分析

linux下可执行文件分析
linux下可执行文件分析

本文讨论了UNIX/LINUX 平台下三种主要的可执行文件格式:a.out(assembler and link editor output 汇编器和链接编辑器的输出)、COFF(Common Object File Format 通用对象文件格式)、ELF(Executable and Linking Format 可执行和链接格式)。首先是对可执行文件格式的一个综述,并通过描述ELF 文件加载过程以揭示可执行文件内容与加载运行操作之间的关系。随后依此讨论了此三种文件格式,并着重讨论ELF 文件的动态连接机制,其间也穿插了对各种文件格式优缺点的评价。最后对三种可执行文件格式有一个简单总结,并提出作者对可文件格式评价的一些感想。

可执行文件格式综述

相对于其它文件类型,可执行文件可能是一个操作系统中最重要的文件类型,因为它们是完成操作的真正执行者。可执行文件的大小、运行速度、资源占用情况以及可扩展性、可移植性等与文件格式的定义和文件加载过程紧密相关。研究可执行文件的格式对编写高性能程序和一些黑客技术的运用都是非常有意义的。

不管何种可执行文件格式,一些基本的要素是必须的,显而易见的,文件中应包含代码和数据。因为文件可能引用外部文件定义的符号(变量和函数),因此重定位信息和符号信息也是需要的。一些辅助信息是可选的,如调试信息、硬件信息等。基本上任意一种可执行文件格式都是按区间保存上述信息,称为段(Segment)或节(Section)。不同的文件格式中段和节的含义可能有细微区别,但根据上下文关系可以很清楚的理解,这不是关键问题。最后,可执行文件通常都有一个文件头部以描述本文件的总体结构。相对可执行文件有三个重要的概念:编译(compile)、连接(link,也可称为链接、联接)、加载(load)。源程序文件被编译成目标文件,多个目标文件被连接成一个最终的可执行文件,可执行文件被加载到内存中运行。因为本文重点是讨论可执行文件格式,因此加载过程也相对重点讨论。下面是LINUX平台下ELF文件加载过程的一个简单描述。

1:内核首先读ELF文件的头部,然后根据头部的数据指示分别读入各种数据结构,找到标记为可加载(loadable)的段,并调用函数mmap()把段内容加载到内存中。在加载之前,内核把段的标记直接传递给mmap(),段的标记指示该段在内存中是否可读、可写,可执行。显然,文本段是只读可执行,而数据段是可读可写。这种方式是利用了现代操作系统和处理器对内存的保护功能。著名的Shellcode(参考资料17)的编写

技巧则是突破此保护功能的一个实际例子。

2:内核分析出ELF文件标记为PT_INTERP 的段中所对应的动态连接器名称,并加载动态连接器。现代LINUX 系统的动态连接器通常是/lib/ld-linux.so.2,相关细节在后面有详细描述。

3:内核在新进程的堆栈中设置一些标记-值对,以指示动态连接器的相关操作。

4:内核把控制传递给动态连接器。

5:动态连接器检查程序对外部文件(共享库)的依赖性,并在需要时对其进行加载。6:动态连接器对程序的外部引用进行重定位,通俗的讲,就是告诉程序其引用的外部变量/函数的地址,此地址位于共享库被加载在内存的区间内。动态连接还有一个延迟(Lazy)定位的特性,即只在"真正"需要引用符号时才重定位,这对提高程序运行效率有极大帮助。

7:动态连接器执行在ELF文件中标记为 .init 的节的代码,进行程序运行的初始化。在早期系统中,初始化代码对应函数_init(void)(函数名强制固定),在现代系统中,则对应形式为

void

__attribute((constructor))

init_function(void)

{

……

}

其中函数名为任意。

8:动态连接器把控制传递给程序,从ELF 文件头部中定义的程序进入点开始执行。在a.out 格式和ELF格式中,程序进入点的值是显式存在的,在COFF 格式中则是由规范隐含定义。

从上面的描述可以看出,加载文件最重要的是完成两件事情:加载程序段和数据段到内存;进行外部定义符号的重定位。重定位是程序连接中一个重要概念。我们知道,一个可执行程序通常是由一个含有main() 的主程序文件、若干目标文件、若干共享库(Shared Libraries)组成。(注:采用一些特别的技巧,也可编写没有main 函数的程序,请参阅参考资料2)一个 C 程序可能引用共享库定义的变量或函数,换句话说就是程序运行时必须知道这些变量/函数的地址。在静态连接中,程序所有需要使用的外部定义都完全包含在可执行程序中,而动态连接则只在可执行文件中设置相关外部定义的一些引用信息,真正的重定位是在程序运行之时。静态连接方式有两个大问题:如果

库中变量或函数有任何变化都必须重新编译连接程序;如果多个程序引用同样的变量/函数,则此变量/函数会在文件/内存中出现多次,浪费硬盘/内存空间。比较两种连接方式生成的可执行文件的大小,可以看出有明显的区别。

回页首

a.out 文件格式分析

a.out 格式在不同的机器平台和不同的UNIX 操作系统上有轻微的不同,例如在MC680x0 平台上有6 个section。下面我们讨论的是最"标准"的格式。

a.out 文件包含7 个section,格式如下:

exec header(执行头部,也可理解为文件头部)

text segment(文本段)

data segment(数据段)

text relocations(文本重定位段)

data relocations(数据重定位段)

symbol table(符号表)

string table(字符串表)

执行头部的数据结构:

struct exec {

unsigned long a_midmag; /* 魔数和其它信息*/

unsigned long a_text; /* 文本段的长度*/

unsigned long a_data; /* 数据段的长度*/

unsigned long a_bss; /* BSS段的长度*/

unsigned long a_syms; /* 符号表的长度*/

unsigned long a_entry; /* 程序进入点*/

unsigned long a_trsize; /* 文本重定位表的长度*/

unsigned long a_drsize; /* 数据重定位表的长度*/

};

文件头部主要描述了各个section 的长度,比较重要的字段是a_entry(程序进入点),代表了系统在加载程序并初试化各种环境后开始执行程序代码的入口。这个字段在后面讨论的ELF 文件头部中也有出现。由a.out 格式和头部数据结构我们可以看出,a.out 的格式非常紧凑,只包含了程序运行所必须的信息(文本、数据、BSS),而且每个section 的顺序是固定的。这种结构缺乏扩展性,如不能包含"现代"可执行文件中常见的调试信息,最初的UNIX 黑客对a.out 文件调试使用的工具是adb,而adb 是一种机器语言调试器!

a.out 文件中包含符号表和两个重定位表,这三个表的内容在连接目标文件以生成可执行文件时起作用。在最终可执行的 a.out 文件中,这三个表的长度都为0。a.out 文件在连接时就把所有外部定义包含在可执行程序中,如果从程序设计的角度来看,这是一种硬编码方式,或者可称为模块之间是强藕和的。在后面的讨论中,我们将会具体看到ELF格式和动态连接机制是如何对此进行改进的。

a.out 是早期UNIX系统使用的可执行文件格式,由AT&T 设计,现在基本上已被ELF 文件格式代替。a.out 的设计比较简单,但其设计思想明显的被后续的可执行文件格式所继承和发扬。可以参阅参考资料16和阅读参考资料15源代码加深对a.out 格式的理解。参考资料12讨论了如何在"现代"的红帽LINUX运行 a.out 格式文件。

回页首

COFF 文件格式分析

COFF 格式比a.out 格式要复杂一些,最重要的是包含一个节段表(section table),因此除了 .text,.data,和 .bss 区段以外,还可以包含其它的区段。另外也多了一个可选的头部,不同的操作系统可一对此头部做特定的定义。

COFF 文件格式如下:

File Header(文件头部)

Optional Header(可选文件头部)

Section 1 Header(节头部)

………

Section n Header(节头部)

Raw Data for Section 1(节数据)

Raw Data for Section n(节数据)

Relocation Info for Sect. 1(节重定位数据)

Relocation Info for Sect. n(节重定位数据)

Line Numbers for Sect. 1(节行号数据)

Line Numbers for Sect. n(节行号数据)

Symbol table(符号表)

String table(字符串表)

文件头部的数据结构:

struct filehdr

{

unsigned short f_magic; /* 魔数*/

unsigned short f_nscns; /* 节个数*/

long f_timdat; /* 文件建立时间*/

long f_symptr; /* 符号表相对文件的偏移量*/

long f_nsyms; /* 符号表条目个数*/

unsigned short f_opthdr; /* 可选头部长度*/

unsigned short f_flags; /* 标志*/

};

COFF 文件头部中魔数与其它两种格式的意义不太一样,它是表示针对的机器类型,例如0x014c 相对于I386 平台,而0x268 相对于Motorola 68000系列等。当COFF 文件为可执行文件时,字段f_flags 的值为F_EXEC(0X00002),同时也表示此文件没有未解析的符号,换句话说,也就是重定位在连接时就已经完成。由此也可以看出,原始的COFF 格式不支持动态连接。为了解决这个问题以及增加一些新的特性,

一些操作系统对COFF 格式进行了扩展。Microsoft 设计了名为PE(Portable Executable)的文件格式,主要扩展是在COFF 文件头部之上增加了一些专用头部,具体细节请参阅参考资料18,某些UNIX 系统也对COFF 格式进行了扩展,如XCOFF(extended common object file format)格式,支持动态连接,请参阅参考资料5。

紧接文件头部的是可选头部,COFF 文件格式规范中规定可选头部的长度可以为0,但在LINUX 系统下可选头部是必须存在的。下面是LINUX 下可选头部的数据结构:

typedef struct

{

char magic[2]; /* 魔数*/

char vstamp[2]; /* 版本号*/

char tsize[4]; /* 文本段长度*/

char dsize[4]; /* 已初始化数据段长度*/

char bsize[4]; /* 未初始化数据段长度*/

char entry[4]; /* 程序进入点*/

char text_start[4]; /* 文本段基地址*/

char data_start[4]; /* 数据段基地址*/

}

COFF_AOUTHDR;

字段magic 为0413 时表示COFF 文件是可执行的,注意到可选头部中显式定义了程序进入点,标准的COFF 文件没有明确的定义程序进入点的值,通常是从 .text 节开始执行,但这种设计并不好。

前面我们提到,COFF 格式比 a.out 格式多了一个节段表,一个节头条目描述一个节数据的细节,因此COFF 格式能包含更多的节,或者说可以根据实际需要,增加特定的节,具体表现在COFF 格式本身的定义以及稍早提及的COFF 格式扩展。我个人认为,节段表的出现可能是COFF 格式相对 a.out 格式最大的进步。下面我们将简单描述COFF 文件中节的数据结构,因为节的意义更多体现在程序的编译和连接上,所以本文不对其做更多的描述。此外,ELF 格式和COFF格式对节的定义非常相似,在随后的ELF 格式分析中,我们将省略相关讨论。

struct COFF_scnhdr

{

char s_name[8]; /* 节名称*/

char s_paddr[4]; /* 物理地址*/

char s_vaddr[4]; /* 虚拟地址*/

char s_size[4]; /* 节长度*/

char s_scnptr[4]; /* 节数据相对文件的偏移量*/

char s_relptr[4]; /* 节重定位信息偏移量*/

char s_lnnoptr[4]; /* 节行信息偏移量*/

char s_nreloc[2]; /* 节重定位条目数*/

char s_nlnno[2]; /* 节行信息条目数*/

char s_flags[4]; /* 段标记*/

};

有一点需要注意:LINUX系统中头文件coff.h中对字段s_paddr的注释是"physical address",但似乎应该理解为"节被加载到内存中所占用的空间长度"。字段s_flags标记该节的类型,如文本段、数据段、BSS段等。在COFF的节中也出现了行信息,行信息描述了二进制代码与源代码的行号之间的对映关系,在调试时很有用。

参考资料19是一份对COFF格式详细描述的中文资料,更详细的内容请参阅参考资料20。

回页首

ELF文件格式分析

ELF文件有三种类型:可重定位文件:也就是通常称的目标文件,后缀为.o。共享文件:也就是通常称的库文件,后缀为.so。可执行文件:本文主要讨论的文件格式,总的来说,可执行文件的格式与上述两种文件的格式之间的区别主要在于观察的角度不同:一种称为连接视图(Linking View),一种称为执行视图(Execution View)。

首先看看ELF文件的总体布局:

ELF header(ELF头部)

Program header table(程序头表)

Segment1(段1)

Segment2(段2)

………

Sengmentn(段n)

Setion header table(节头表,可选)

段由若干个节(Section)构成,节头表对每一个节的信息有相关描述。对可执行程序而言,节头表是可选的。参考资料1中作者谈到把节头表的所有数据全部设置为0,程序也能正确运行!ELF头部是一个关于本文件的路线图(road map),从总体上描述文件的结构。下面是ELF头部的数据结构:

typedef struct

{

unsigned char e_ident[EI_NIDENT]; /* 魔数和相关信息*/

Elf32_Half e_type; /* 目标文件类型*/

Elf32_Half e_machine; /* 硬件体系*/

Elf32_Word e_version; /* 目标文件版本*/

Elf32_Addr e_entry; /* 程序进入点*/

Elf32_Off e_phoff; /* 程序头部偏移量*/

Elf32_Off e_shoff; /* 节头部偏移量*/

Elf32_Word e_flags; /* 处理器特定标志*/

Elf32_Half e_ehsize; /* ELF头部长度*/

Elf32_Half e_phentsize; /* 程序头部中一个条目的长度*/

Elf32_Half e_phnum; /* 程序头部条目个数*/

Elf32_Half e_shentsize; /* 节头部中一个条目的长度*/

Elf32_Half e_shnum; /* 节头部条目个数*/

Elf32_Half e_shstrndx; /* 节头部字符表索引*/

} Elf32_Ehdr;

下面我们对ELF头表中一些重要的字段作出相关说明,完整的ELF定义请参阅参考资料6和参考资料7。

e_ident[0]-e_ident[3]包含了ELF文件的魔数,依次是0x7f、'E'、'L'、'F'。注意,任何一个ELF文件必须包含此魔数。参考资料3中讨论了利用程序、工具、/Proc文件系统等多种查看ELF魔数的方法。e_ident[4]表示硬件系统的位数,1代表32位,2代表64位。e_ident[5]表示数据编码方式,1代表小印第安排序(最大有意义的字节占有最低的地址),2代表大印第安排序(最大有意义的字节占有最高的地址)。e_ident[6]指定ELF头部的版本,当前必须为1。e_ident[7]到e_ident[14]是填充符,通常是0。ELF格式规范中定义这几个字节是被忽略的,但实际上是这几个字节完全可以可被利用。如病毒Lin/Glaurung.676/666(参考资料1)设置e_ident[7]为0x21,表示本文件已被感染;或者存放可执行代码(参考资料2)。ELF头部中大多数字段都是对子头部数据的描述,其意义相对比较简单。值得注意的是某些病毒可能修改字段e_entry(程序进入点)的值,以指向病毒代码,例如上面提到的病毒Lin/Glaurung.676/666。

一个实际可执行文件的文件头部形式如下:(利用命令readelf)

ELF Header:

Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

Class: ELF32

Data: 2's complement, little endian

Version: 1 (current)

OS/ABI: UNIX - System V

ABI Version: 0

Type: EXEC (Executable file)

Machine: Intel 80386

Version: 0x1

Entry point address: 0x80483cc

Start of program headers: 52 (bytes into file)

Start of section headers: 14936 (bytes into file)

Flags: 0x0

Size of this header: 52 (bytes)

Size of program headers: 32 (bytes)

Number of program headers: 6

Size of section headers: 40 (bytes)

Number of section headers: 34

Section header string table index: 31

紧接ELF头部的是程序头表,它是一个结构数组,包含了ELF头表中字段e_phnum定义的条目,结构描述一个段或其他系统准备执行该程序所需要的信息。

typedef struct {

Elf32_Word p_type; /* 段类型*/

Elf32_Off p_offset; /* 段位置相对于文件开始处的偏移量*/

Elf32_Addr p_vaddr; /* 段在内存中的地址*/

Elf32_Addr p_paddr; /* 段的物理地址*/

Elf32_Word p_filesz; /* 段在文件中的长度*/

Elf32_Word p_memsz; /* 段在内存中的长度*/

Elf32_Word p_flags; /* 段的标记*/

Elf32_Word p_align; /* 段在内存中对齐标记*/

} Elf32_Phdr;

在详细讨论可执行文件程序头表之前,首先查看一个实际文件的输出:

Program Headers:

Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align

PHDR 0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4

INTERP 0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R 0x1

[Requesting program interpreter: /lib/ld-linux.so.2]

LOAD 0x000000 0x08048000 0x08048000 0x00684 0x00684 R E 0x1000

LOAD 0x000684 0x08049684 0x08049684 0x00118 0x00130 RW 0x1000

DYNAMIC 0x000690 0x08049690 0x08049690 0x000c8 0x000c8 RW 0x4

NOTE 0x000108 0x08048108 0x08048108 0x00020 0x00020 R 0x4

Section to Segment mapping:

Segment Sections...

00

01 .interp

02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .ro data .eh_frame

03 .data .dynamic .ctors .dtors .jcr .got .bss

04 .dynamic

05 .note.ABI-tag

Section Headers:

[Nr] Name Type Addr Off Size ES Flg Lk Inf Al

[ 0] NULL 00000000 000000 000000 00 0 0 0

[ 1] .interp PROGBITS 080480f4 0000f4 000013 00 A 0 0 1

[ 2] .note.ABI-tag NOTE 08048108 000108 000020 00 A 0 0 4

[ 3] .hash HASH 08048128 000128 000040 04 A 4 0 4

[ 4] .dynsym DYNSYM 08048168 000168 0000b0 10 A 5 1 4

[ 5] .dynstr STRTAB 08048218 000218 00007b 00 A 0 0 1

[ 6] .gnu.version VERSYM 08048294 000294 000016 02 A 4 0 2

[ 7] .gnu.version_r VERNEED 080482ac 0002ac 000030 00 A 5 1 4

[ 8] .rel.dyn REL 080482dc 0002dc 000008 08 A 4 0 4

[ 9] .rel.plt REL 080482e4 0002e4 000040 08 A 4 b 4

[10] .init PROGBITS 08048324 000324 000017 00 AX 0 0 4

[11] .plt PROGBITS 0804833c 00033c 000090 04 AX 0 0 4

[12] .text PROGBITS 080483cc 0003cc 0001f8 00 AX 0 0 4

[13] .fini PROGBITS 080485c4 0005c4 00001b 00 AX 0 0 4

[14] .rodata PROGBITS 080485e0 0005e0 00009f 00 A 0 0 32

[15] .eh_frame PROGBITS 08048680 000680 000004 00 A 0 0 4

[16] .data PROGBITS 08049684 000684 00000c 00 WA 0 0 4

[17] .dynamic DYNAMIC 08049690 000690 0000c8 08 WA 5 0 4

[18] .ctors PROGBITS 08049758 000758 000008 00 WA 0 0 4

[19] .dtors PROGBITS 08049760 000760 000008 00 WA 0 0 4

[20] .jcr PROGBITS 08049768 000768 000004 00 WA 0 0 4

[21] .got PROGBITS 0804976c 00076c 000030 04 WA 0 0 4

[22] .bss NOBITS 0804979c 00079c 000018 00 WA 0 0 4

[23] .comment PROGBITS 00000000 00079c 000132 00 0 0 1

[24] .debug_aranges PROGBITS 00000000 0008d0 000098 00 0 0 8

[25] .debug_pubnames PROGBITS 00000000 000968 000040 00 0 0 1

[26] .debug_info PROGBITS 00000000 0009a8 001cc6 00 0 0 1

[27] .debug_abbrev PROGBITS 00000000 00266e 0002cc 00 0 0 1

[28] .debug_line PROGBITS 00000000 00293a 0003dc 00 0 0 1

[29] .debug_frame PROGBITS 00000000 002d18 000048 00 0 0 4

[30] .debug_str PROGBITS 00000000 002d60 000bcd 01 MS 0 0 1

[31] .shstrtab STRTAB 00000000 00392d 00012b 00 0 0 1

[32] .symtab SYMTAB 00000000 003fa8 000740 10 33 56 4

[33] .strtab STRTAB 00000000 0046e8 000467 00 0 0 1

对一个ELF可执行程序而言,一个基本的段是标记p_type为PT_INTERP的段,它表明了运行此程序所需要的程序解释器(/lib/ld-linux.so.2),实际上也就是动态连接器(dynamic linker)。最重要的段是标记p_type为PT_LOAD的段,它表明了为运行程序而需要加载到内存的数据。查看上面实际输入,可以看见有两个可LOAD段,第一个为只读可执行(FLg为R E),第二个为可读可写(Flg为RW)。段1包含了文本节.text,注意到ELF文件头部中程序进入点的值为0x80483cc,正好是指向节.text在内存中的地址。段二包含了数据节.data,此数据节中数据是可读可写的,相对的只读数据节.rodata包含在段1中。ELF格式可以比COFF格式包含更多的调试信息,如上面所列出的形式为.debug_xxx的节。在I386平台LINUX系统下,用命令file查看一个ELF 可执行程序的可能输出是:a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped。

ELF文件中包含了动态连接器的全路径,内核定位"正确"的动态连接器在内存中的地址是"正确"运行可执行文件的保证,参考资料13讨论了如何通过查找动态连接器在内存中的地址以达到颠覆(Subversiver)动态连接机制的方法。

最后我们讨论ELF文件的动态连接机制。每一个外部定义的符号在全局偏移表(Global Offset Table GOT)中有相应的条目,如果符号是函数则在过程连接表(Procedure Linkage Table PLT)中也有相应的条目,且一个PLT条目对应一个GOT条目。对外部定义函数解析可能是整个ELF文件规范中最复杂的,下面是函数符号解析过程的一个描述。

1:代码中调用外部函数func,语句形式为call 0xaabbccdd,地址0xaabbccdd实际上就是符号func在PLT表中对应的条目地址(假设地址为标号.PLT2)。

2:PLT表的形式如下

.PLT0: pushl 4(%ebx) /* GOT表的地址保存在寄存器ebx中*/

jmp *8(%ebx)

nop; nop

nop; nop

.PLT1: jmp *name1@GOT(%ebx)

pushl $offset

jmp .PLT0@PC

.PLT2: jmp *func@GOT(%ebx)

pushl $offset

jmp .PLT0@PC

3:查看标号.PLT2的语句,实际上是跳转到符号func在GOT表中对应的条目。

4:在符号没有重定位前,GOT表中此符号对应的地址为标号.PLT2的下一条语句,即是pushl $offset,其中$offset是符号func的重定位偏移量。注意到这是一个二次跳转。

5:在符号func的重定位偏移量压栈后,控制跳到PLT表的第一条目,把GOT[1]的内容压栈,并跳转到GOT[2]对应的地址。

6:GOT[2]对应的实际上是动态符号解析函数的代码,在对符号func的地址解析后,会把func在内存中的地址设置到GOT表中此符号对应的条目中。

7:当第二次调用此符号时,GOT表中对应的条目已经包含了此符号的地址,就可直接调用而不需要利用PLT表进行跳转。

动态连接是比较复杂的,但为了获得灵活性的代价通常就是复杂性。其最终目的是把GOT表中条目的值修改为符号的真实地址,这也可解释节.got包含在可读可写段中。动态连接是一个非常重要的进步,这意味着库文件可以被升级、移动到其他目录等等而不需要重新编译程序(当然,这不意味库可以任意修改,如函数入参的个数、数据类型应保持兼容性)。从很大程度上说,动态连接机制是ELF格式代替a.out格式的决定性原因。如果说面对对象的编程本质是面对接口(interface)的编程,那么动态连接机制则是这种思想的地一个非常典型的应用,具体的讲,动态连接机制与设计模式中的桥接(BRIDGE)方法比较类似,而它的LAZY特性则与代理(PROXY)方法非常相似。动态连接操作的细节描述请参阅参考资料8,9,10,11。通过阅读命令readelf、objdump 的源代码以及参考资料14中所提及的相关软件源代码,可以对ELF文件的格式有更彻底的了解。

回页首

总结

不同时期的可执行文件格式深刻的反映了技术进步的过程,技术进步通常是针对解决存在的问题和适应新的环境。早期的UNIX系统使用a.out格式,随着操作系统和硬件系统的进步,a.out格式的局限性越来越明显。新的可执行文件格式COFF在UNIX System VR3中出现,COFF格式相对a.out格式最大变化是多了一个节头表(section head table),能够在包含基础的文本段、数据段、BSS段之外包含更多的段,但是COFF 对动态连接和C++程序的支持仍然比较困难。为了解决上述问题,UNIX系统实验室(UNIX SYSTEM Laboratories USL) 开发出ELF文件格式,它被作为应用程序二进制接口(Application binary Interface ABI)的一部分,其目的是替代传统的a.out格式。例如,ELF文件格式中引入初始化段.init和结束段.fini(分别对应构造函数和析构函数)则主要是为了支持C++程序。1994年6月ELF格式出现在LINUX系统上,现在ELF格式作为UNIX/LINUX最主要的可执行文件格式。当然我们完全有理由相信,在将来还会有新的可执行文件格式出现。

上述三种可执行文件格式都很好的体现了设计思想中分层的概念,由一个总的头部刻画了文件的基本要素,再由若干子头部/条目刻画了文件的若干细节。比较一下可执行文件格式和以太数据包中以太头、IP头、TCP头的设计,我想我们能很好的感受分层这一重要的设计思想。参考资料21从全局的角度讨论了各种文件的格式,并提出一个比较夸张的结论:Everything Is Byte!

最后的题外话:大多数资料中对a.out格式的评价较低,常见的词语有黑暗年代(dark ages)、丑陋(ugly)等等,当然,从现代的观点来看,的确是比较简单,但是如果没有曾经的简单何来今天的精巧?正如我们今天可以评价石器时代的技术是ugly,那么将来的人们也可以嘲讽今天的技术是非常ugly。我想我们也许应该用更平和的心态来对曾经的技术有一个公正的评价。

linux下编译C语言

GCC 支持了许多不同的语言,包括C、C++、Ada、Fortran、Objective C,Perl、Python 和Ruby,甚至还有Java。 Linux 内核和许多其他自由软件以及开放源码应用程序都是用 C 语言编写并使用GCC 编译的。 编译C++程序: -c 只编译不连接 g++ file1 -c -o file1.o g++ file2 -c -o file2.o g++ file1.o file.o -o exec g++ -c a.cpp 编译 g++ -o a a.o 生成可执行文件 也可以g++ -o a a.cpp直接生成可执行文件。 1. 编译单个源文件 为了进行测试,你可以创建“Hello World”程序: #include #include int main(int argc, char **argv) { printf(“Hello world!n”); exit(0); } 使用如下命令编译并测试这个代码: # gcc -o hello hello.c

# ./hello Hello wordl! 在默认情况下产生的可执行程序名为a.out,但你通常可以通过gcc 的“-o”选项来指定自己的可执行程序名称。 2. 编译多个源文件 源文件message.c包含一个简单的消息打印函数: #include void goodbye_world(void) { printf(“Goodbye, world!n”); } 使用gcc的“-c”标记来编译支持库代码: # gcc -c message.c 这一过程的输出结果是一个名为message.o的文件,它包含适合连接到一个较大程序的已编译目标代码。 创建一个简单的示例程序,它包含一个调用goodbye_world的main函数 #include void goodbye_world(void): int main(int argc, char **argv) { goodbye_world(); exit(0); }

Linux下Makefile简单教程

目录 一:Makefile基本规则 1.1示例 1.2 隐式规则 1.3 伪目标 1.4 搜索源文件 二:变量 2.1使用变量定义变量值 2.2追加变量 三:条件判断 四:函数

Linux下Makefile总结 ——一步 MakeFile可以看做是一种简单的编程语言,其诞生的本质目的是实现自动化编译。 以Linux下gcc-c编译器为例,编译一个c语言程序需要经过以下几个步骤: 1.将c语言源程序预处理,生成.i文件; 2.预处理后的.i语言编译成汇编语言,生成.s文件; 3.汇编语言经过汇编,生成目标文件.o文件; 4.将各个模块的.o文件链接起来,生成一个可执行程序文件。 我们知道,在Visual C++6.0中,可以新建一个工程,在一个工程当中能够包含若干个c语言文件,则编译的时候直接编译整个工程便可。Linux下无法为多个c语言文件新建工程,但可以通过MakeFile实现它们的整合编译。 如上gcc-c编译步骤,如果使用Makefile则过程为: .C文件——>.o文件——>可执行文件 当然,Makefile中也加入了自己的设置变量方法与集成了一些函数,能够更有效地方便用户使用。 /**************************分隔符********************************/

一:Makefile基本规则 1.1示例 target ... : prerequisites ... command ... ... target也就是一个目标文件,可以是Object File,也可以是执行文件。prerequisites就是,要生成那个target所需要的文件或是目标。command也就是make需要执行的命令。(任意的Shell命令) 为了方便理解,我们来看一个示例: /*Makefile示例*/ edit : main.o kbd.o command.o display.o / insert.o search.o files.o utils.o gcc -o edit main.o kbd.o command.o display.o / insert.o search.o files.o utils.o main.o : main.c defs.h #生成main.o gcc -c main.c

怎样执行在Linux上运行应用程序

如何执行在Linux上运行的应用程序 关键字:Linux 先决条件 要充分理解本文,必须具备Windows 环境下桌面应用程序的工作经验,我认为读者对如何使用Linux 桌面有一个基本的了解。使用一个运行的Linux 计算来机探讨本文的概念和示例是很有帮助的。 概述 有时候第一次在Linux 上运行一个应用程序需要一点额外工作。有些应用程序,比如服务器服务,可能无法安装为服务,因此您需要从命令行启动这些应用程序。对于启动这些应用程序的用户帐户而言,需要在应用程序文件中设置执行许可标志(x)。 运行用户空间应用程序 Linux 在内核空间或用户空间运行进程。用户空间是操作系统的区域,应用程序通常在此运行。简单地说,每个用户帐户有其自己的用户空间,应用程序在这个领域内运行。 默认情况下,只有root 用户有权访问内核空间。root 用户是Linux 中的超级用户,相当于Windows 中的管理员帐户。在root 用户帐户下运行应用程序可能会引起安全风险,是不可取的。 很多服务器服务需要root 权限启动服务。然而,服务启动后,root 帐户通常会将其移至服务帐户。严格地说,Linux 中的服务帐户才是标准的用户帐户。主要区别是服务帐户仅用于运行一个服务,而不是为任何实际登录的用户准备的。 设置权限 您可以使用chmod 命令在一个文件中设置执行权限。在Linux 中,umask 设置通常用来防止下载的文件被执行,也有充分的理由相信,因为它有助于维护Linux 计算机的安全性。 大多数Linux 发行版具有一个值为022 的umask 设置,这意味着,默认情况下一个新文件权限设置为644.权限的数字表示形式采用读(4)、写(2)、执行(1) 的格式。因此,默认权限为644 的应用程序下载意味着文件所有者有读写权限,而组用户和其他用户只有读权限。 例如,为每个人赋予一个文件的执行权限,使用chmod a+x 命令。a 表示所有人,加号(+) 表示添加,而x 表示执行。同样地,如果应用程序是一个服务器服务,您应该确保只有授权帐户才有权执行此服务。 如果一个应用程序能够在标准用户帐户权限下运行,但只有特定组中的用户才需要使用它,您可以将该组所有者权限设置为可执行,然后将这些用户添加到该组中。 更具体地说,您可以在一个可执行文件中设置访问控制列表(ACL) 权限,赋予特定用户或组权限来运行该应用程序。使用setfacl 实用工具设置ACL 权限。 对于这些需要以root 用户启动进程的应用程序,比如服务器服务,您有几个选择。总结了允许用户执行需要root 权限的服务器服务的各种选项。 选项描述 作为root 用户不推荐用于服务器服务。当用户已经知道root 密码而且应用程序泄露不是首要关注问题时,可用于应用程序。 SetUID 由于安全问题,不推荐使用。SetUID 允许标准用户以另一个用户方式,比如root 用户,执行一个文件。 sudo 很常用,并且被认为是一个很好的实践。sudo 授予一个用户或组成员权限以执行可能额外需要root 权限的文件。该用户不需要知道root 密码。 带有文件权限的标准用户帐户在一个文件上为用户所有者、组所有者或其他人(所有人)

怎么在linux操作系统上安装可执行的软件

仅以RedHat Linux 为参照,包括但不限于其他版本的Linux都要遵循此方法,以下是说明: 先来看看Linux软件扩展名。软件后缀为.rpm最初是Red Hat Linux提供的一种包封装格式,现在许多Linux发行版本都使用;后缀为.deb是Debain Linux 提供的一种包封装格式;后缀为.tar.gz、tar.Z、tar.bz2或.tgz是使用Unix 系统打包工具tar打包的;后缀为.bin的一般是一些商业软件。通过扩展名可以了解软件格式,进而了解软件安装。 RPM格式软件包的安装 1.简介 几乎所有的Linux发行版本都使用某种形式的软件包管理安装、更新和卸载软件。与直接从源代码安装相比,软件包管理易于安装和卸载;易于更新已安装的软件包;易于保护配置文件;易于跟踪已安装文件。 RPM全称是Red Hat Package Manager(Red Hat包管理器)。RPM本质上就是一个包,包含可以立即在特定机器体系结构上安装和运行的Linux软件。RPM 示意图见图1。 大多数Linux RPM软件包的命名有一定的规律,它遵循名称-版本-修正版-类型-MYsoftware-1.2 -1.i386.rpm 。 2.安装RPM包软件 #rpm -ivh MYsoftware-1.2 -1.i386.rpm RPM命令主要参数: -i 安装软件。 -t 测试安装,不是真的安装。 -p 显示安装进度。 -f 忽略任何错误。 -U 升级安装。 -v 检测套件是否正确安装。 3.卸载软件 #rpm -e 软件名 需要说明的是,上面代码中使用的是软件名,而不是软件包名。例如,要卸载software-1.2.-1.i386.rpm这个包时,应执行: #rpm -e software 4.强行卸载RPM包

如何在linux中自由执行python程序

可执行的Python程序 这部分内容只对Linux/Unix用户适用,不过Windows用户可能也对程序的第一行比较好奇。首先我们需要通过chmod命令,给程序可执行的许可,然后运行程序。 chmod命令用来改变文件的模式,给系统中所有用户这个源文件的执行许可。然后我们可以直接通过指定源文件的位置来执行程序。我们使用./来指示程序位于当前目录。 为了更加有趣一些,你可以把你的文件名改成仅仅helloworld,然后运行./helloworld。这样,这个程序仍然可以工作,因为系统知道它必须用源文件第一行指定的那个解释器来运行程序。 只要知道程序的确切位置,你现在就可以运行程序了——但是如果你希望你的程序能够从各个位置运行呢?那样的话,你可以把你的程序保存在PATH环境变量中的目录之一。每当你运行任何程序,系统会查找列在PATH环境变量中的各个目录。然后运行那个程序。你只要简单地把这个源文件复制到PATH所列目录之一就可以使你的程序在任何位置都可用了。

我们能够用echo命令来显示PATH变量,用$给变量名加前缀以向shell 表示我们需要这个变量的值。我们看到/home/swaroop/bin 是PATH变量中的目录之一。swaroop是我的系统中使用的用户名。通常,在你的系统中也会有一个相似的目录。你也可以把你选择的目录添加到PATH变量中去——这可以通过运行PATH=$PATH:/home/swaroop/mydir完成,其中“/home/swaroop/mydir”是我想要添加到PATH变量中的目录。 当你想要在任何时间、任何地方运行你的程序的时候,这个方法十分有用。它就好像创造你自己的指令,如同cd或其他Linux终端或DOS提示符命令那样。

linux下编写c源程序并编译运行

姓名:雨田河南大学rjxy 班级:XXXX 实验二Linux基本操作 实验二Linux基本操作 编写c源程序并用编译运行 【需求】 ◆在当前目录下创建新文件t.c,用vi编辑器一段简单代码,代码要求在屏幕上输出 文字“Hello Linux!”; ◆用gcc编译t.c文件,并运行,查看输出结果,若结果错误,请根据提示修改;【系统及软件环境】 操作系统:Virtualbox,Fedora 13 【实验配置文件及命令】 1.配置文件: 2.命令:touch、rpm、gcc、./等

进入Linux操作系统,应用程序-> 系统工具-> 终端,输入命令:su 输入密码切换到root超级用户。 1.在当前目录建立一个新的目录test:$ mkdir test 在test目录下建立文件t.c :$touch t.c 3编辑程序源代码:vi t.c 首先按下键盘的“i”键,字符界面下方出现“insert”提示字符,此时输入以下代码: #include "stdio.h" int main() { printf("Hello Linux!\n"); return 0; } 4 保存退出:先按下“Esc”键,然后按下“shift”和“:”键,界面上出现冒号,然后输入“xq!”或者“x”对代码保存退出。 5 由于系统默认没有安装C语言编译程序,下面进行安装gcc 程序; 此处不再赘述,以下引用实验指导书: 1.gcc的安装 (1)查看gcc是否安装 rpm –q gcc (2)指定安装源 在“系统-分配光驱”里选择“Fedora-13-i386-DVD.iso” (3)查看安装源挂载位置 df命令,可查看到虚拟光驱挂载点 返回结果为:/media/Fedora 13 i386 DVD (4)使用安装源 安装的文件为RPM安装包,所在位置为安装光盘中的“Packages”目录下,可用“cd”命令进入此目录 cd /media/ Fedora 13 i386 DVD/Packages ★由于“Fedora 13 i386 DVD”名字中有空格,若直接输入,则会提示找不到此目录,可用“tab”键自动补全 【方法】cd /media/F)/P() 则可返回如下结果: cd /media/Fedora\ 13\ i386 \DVD\ /Packages (5)查看当前目录下是否有gcc安装包

linux命令--RUN

linux命令 一、linux各种版本 linux命令是对Linux系统进行管理的命令。对于Linux系统来说,无论是中央处理器、内存、磁盘驱动器、键盘、鼠标,还是用户等都是文件,Linux系统管理的命令是它正常运行的核心,与之前的DOS命令类似。linux命令在系统中有两种类型:内置Shell命令和Linux命令。 二、Linux基本操作命令 首先介绍一个名词“控制台(console)”,它就是我们通常见到的使用字符操作界面的人机接口,例如dos。我们说控制台命令,就是指通过字符界面输入的可以操作系统的命令,例如dos命令就是控制台命令。我们现在要了解的是基于Linux操作系统的基本控制台命令。有一点一定要注意,和dos命令不同的是,Linux的命令(也包括文件名等等)对大小写是敏感的,也就是说,如果你输入的命令大小写不对的话,系统是不会做出你期望的响应的。 2.1 模式切换 1、由字符到图型#startx或#init 5 2、由图形到字符#logout或init 3 3、注销#logout或exit或ctrl+d 4、关机#poweroff或init 0或shutdown now或 halt -p 5、重启#reboot或init 6或shutdown -r now 2.2 获得帮助 #help提供内部命令的帮助#man或info提供外部命令的帮助。 如果你的英文足够好,那完全可以不靠任何人就精通linux,只要你会用man。Man实际上就是察看指令用法的help,学习任何一种UNIX类的操作系统最重要的就是学会使用man这个辅助命令。man是manual(手册)的缩写字,它的说明非常的详细,但是因为它都是英文,看起来非常的头痛。建议大家需要的时候再去看man,平常吗,记得一些基本用法就可以了。 2.3 ls ls [list]

(完整word版)Linux操作系统(下)复习题

复习题 一、填空题 1、Linux操作系统是Unix 操作系统的一个克隆版本。 2、Linux的命令运行环境昌Shell,它是一种命令解释器,在用户和操作系统之间提供了一个交互接口。 3、Linux系统中有三个基本的文件类型:普通文件、目录文件和设备文件。 4、Linux系统通过目录将系统中所有的文件分级、分层组织在一起,形成了Linux文件系统的树型层次结构。 5、在Linux系统中建立新目录的命令是mkdir 。 6、Linux编程可分为Shell 编程和高级语言编程。 7、Linux系统提供了许多文本编辑程序,比较常用的有vi 和emacs 等。 8、要使用make,必须编写一个叫Makefile 的文件。 9、autoconf 是一个用于生成可以自动配置软件源代码包以适应多种类Unix系统的shell 脚本的工具。 10、Automake 是一个从文件Makefile.am中自动生成Makefile.in文件的工具。 11、Linux的文件是个简单的字节序列。 12、对于Linux而言,所有对设备和文件的操作都使用文件描述符。 13、调用open 函数可以打开或创建一个文件。 14、设置文件的存取权限,分为属主、组用户和其他用户三类。每类分为读、写和执行权限。 15、第一个进程都有一个目录与之相连,它称为当前工作目录,或简单地称为工作目录。 16、当打开一个流时,标准输入输出函数返回一个FILE结构的指针。 17、在三个流是在执行程序时自动打开的。它们是标准输入、标准输出和标准错误输出。 18、有3种类型的无格式I/O 函数可用来读写流,它们是字符I/O函数、行I/O函数和块I/O函数。 19、每一个流对象内部都保持着两个指示器:一个是错误指示器,当读写文件出错时该指示器被设置;另一个为文件结束指示器,当遇到文件尾时该指示器被设置。 20、流有3种不同的缓冲类型,它们是全缓冲、行缓冲和无缓冲。 21、进程在其生存期内可能处于三种基本状态:运行态、就绪态、等待态。 22、为了让Linux来管理系统中的进程,每个进程用一个task_struct 数据结构来表示。 23、在Linux系统中,进程有两种运行模式:用户模式和系统模式。 24、创建一个新进程的唯一方法是由某个已存在的进程调用fork 或vfork 函数,被创建的新进程称为子进程,已存在的进程称为父进程。 25、系统中的每个进程都有唯一的非负整数作为标识,它被称为进程标识号。 26、进程间通信有如下一些目的:数据传输、共享数据、通知事件、资源共享、进程控制. 27、Linux支持Unix System v中的三种进程间通信机制,它们是:消息队列、信号量、共享内存。

linux操作系统考试题库完整

1.在Linux目录结构中目录中的文件是普通用户可以使用的可使用的可执行文件的目录是(B)19 A./sbin B./bin C./usr D./lib 2.在Linux目录结构中Linux的内核及引导程序所需要的文件位于(B)目录 A/bin B./boot C./root D./proc 3.在Linux目录结构中用来存放系统配置文件(D)目录 A./lib B./dev C./proc D./etc 4.Linux三种特殊权限中仅用于目录文件的权限是(C)22 A.SUID B.SGID C.黏滞位 D.都可以 5.Linux三种权限中允许进入目录的权限(C) 22 A.r-可读 B.w-可写 C.x-可执行 D.都不是 6.下列脚本文件最先自动执行的是(A)24 A./etc/rc.local B./etc/profile C.~/.bashrc D.~/.bash_logout 7.下面通配符可匹配多个任意字符的通配符是(A)29 A.* B. ? C.[abcde ] D.[!a-e] 8.输出重定向符号右边的文件已经存在,不会覆盖文件而是追加的定向符是(B) A.> B.> > C. 2> D.&> 9.表示用户家目录的是(B) A./home (所有用户家目录的父目录,+用户名才是用户家目录) B.~ C. . D. .. 10.可删除非空目录及其子目录的命令是(B) A. touch B.rm -r C.mkdir D.rmdir空目录 11. 是强引用的符号(A) A. 单引号 B.反引号 C.双引号 D.中括号 12.可显示隐藏文件的ls命令形式选项是(A) A. ls -a B. ls -d C.ls -h D .ls -l 13.可通过接受标准输入作为输出内容来创建简单文件的命令是(B)42 A.touch B.cat C.vi D. gedit 14.不带有编辑功能但有翻页、按行滚动、搜索功能的命令是(B)43 A.cat B.more和less C.head 和tail D vi 15.wc命令可统计文件的行数、字数和字节数。在下列格式中只显示文件的字数的是(C)51 A.wc B. wc -c C. wc -w D wc -l 16.可实现文件重命名的命令是(B)52 A.cp B. mv C. ln D rename

Linux下面使用命令如何运行.sh文件方法有哪些

Linux下面使用命令如何运行.sh文件方法有哪些 Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。那么在Linux上怎么运行.sh文件呢?一种是直接./加上文件名.sh,还有一种是接sh 加上文件名.sh的解决绝办法,需要的朋友可以参考下 本文介绍Linux下面用命令如何运行.sh文件的方法,有两种方法: 一、直接./加上文件名.sh,如运行hello.sh为./hello.sh 【hello.sh必须有x权限】

二、直接sh 加上文件名.sh,如运行hello.sh为sh hello.sh 【hello.sh可以没有x权限】 方法一:绝对路径执行.sh文件 1、下面三种方法都可以, 复制内容到剪贴板 ./home/test/shell/hello.sh /home/test/shell/hello.sh sh /home/test/shell/hello.sh 方法二:当前目录执行.sh文件 1、cd到.sh文件所在目录 比如以hello.sh文件为例,如 2、给.sh文件添加x执行权限

比如以hello.sh文件为例,chmod u+x hello.sh, 3、./执行.sh文件 比如以hello.sh文件为例,./hello.sh 即可执行hello.sh 文件, 4、sh 执行.sh文件 以hello.sh文件为例,sh hello.sh即可执行hello.sh文件 补充:Linux基本命令 1.ls命令: 格式::ls [选项] [目录或文件] 功能:对于目录,列出该目录下的所有子目录与文件;对于文件,列出文件名以及其他信息。 常用选项:

linux下可执行文件分析

本文讨论了UNIX/LINUX 平台下三种主要的可执行文件格式:a.out(assembler and link editor output 汇编器和链接编辑器的输出)、COFF(Common Object File Format 通用对象文件格式)、ELF(Executable and Linking Format 可执行和链接格式)。首先是对可执行文件格式的一个综述,并通过描述ELF 文件加载过程以揭示可执行文件内容与加载运行操作之间的关系。随后依此讨论了此三种文件格式,并着重讨论ELF 文件的动态连接机制,其间也穿插了对各种文件格式优缺点的评价。最后对三种可执行文件格式有一个简单总结,并提出作者对可文件格式评价的一些感想。 可执行文件格式综述 相对于其它文件类型,可执行文件可能是一个操作系统中最重要的文件类型,因为它们是完成操作的真正执行者。可执行文件的大小、运行速度、资源占用情况以及可扩展性、可移植性等与文件格式的定义和文件加载过程紧密相关。研究可执行文件的格式对编写高性能程序和一些黑客技术的运用都是非常有意义的。 不管何种可执行文件格式,一些基本的要素是必须的,显而易见的,文件中应包含代码和数据。因为文件可能引用外部文件定义的符号(变量和函数),因此重定位信息和符号信息也是需要的。一些辅助信息是可选的,如调试信息、硬件信息等。基本上任意一种可执行文件格式都是按区间保存上述信息,称为段(Segment)或节(Section)。不同的文件格式中段和节的含义可能有细微区别,但根据上下文关系可以很清楚的理解,这不是关键问题。最后,可执行文件通常都有一个文件头部以描述本文件的总体结构。相对可执行文件有三个重要的概念:编译(compile)、连接(link,也可称为链接、联接)、加载(load)。源程序文件被编译成目标文件,多个目标文件被连接成一个最终的可执行文件,可执行文件被加载到内存中运行。因为本文重点是讨论可执行文件格式,因此加载过程也相对重点讨论。下面是LINUX平台下ELF文件加载过程的一个简单描述。 1:内核首先读ELF文件的头部,然后根据头部的数据指示分别读入各种数据结构,找到标记为可加载(loadable)的段,并调用函数mmap()把段内容加载到内存中。在加载之前,内核把段的标记直接传递给mmap(),段的标记指示该段在内存中是否可读、可写,可执行。显然,文本段是只读可执行,而数据段是可读可写。这种方式是利用了现代操作系统和处理器对内存的保护功能。著名的Shellcode(参考资料17)的编写 技巧则是突破此保护功能的一个实际例子。

从Linux程序中执行shell(程序、脚本)并获得输出结果

Contents 1. 前言 2. 使用临时文件 3. 使用匿名管道 4. 使用popen 5. 小结 1. 前言 Unix界有一句名言:“一行shell脚本胜过万行C程序”,虽然这句话有些夸张,但不可否认的是,借助脚本确实能够极大的简化一些编程工作。比如实现一个ping程序来测试网络的连通性,实现ping函数需要写上200~300行代码,为什么不能直接调用系统的ping命令呢?通常在程序中通过system函数来调用shell命令。但是,system函数仅返回命令是否执行成功,而我们可能需要获得shell命令在控制台上输出的结果。例如,执行外部命令ping后,如果执行失败,我们希望得到ping的返回信息。 2. 使用临时文件 首先想到的方法就是将命令输出重定向到一个临时文件,在我们的应用程序中读取这个临时文件,获得外部命令执行结果,代码如下所示: #define CMD_STR_LEN 1024 int mysystem(char* cmdstring, char* tmpfile) { char cmd_string[CMD_STR_LEN]; tmpnam(tmpfile); sprintf(cmd_string, "%s > %s", cmdstring, tmpfile); return system(cmd_string); } 这种使用使用了临时文件作为应用程序和外部命令之间的联系桥梁,在应用程序中需要读取文件,然后再删除该临时文件,比较繁琐,优点是实现简单,容易理解。有没有不借助临时文件的方法呢? 3. 使用匿名管道 在<>一书中给出了一种通过匿名管道方式将程序结果输出到分页程序的例子,因此想到,我们也可以通过管道来将外部命令的结果同应用程序连接起来。方法就是fork一个子进程,并创建一个匿名管道,在子进程中执行shell命令,并将其标准输出dup 到匿名管道的输入端,父进程从管道中读取,即可获得shell 命令的输出,代码如下: int mysystem(char* cmdstring, char* buf, int len) { int fd[2]; pid_t pid;

LINUX下python程序的运行

LINUX下python程序的运行 当你学习一种新的编程语言的时候,你编写运行的第一个程序通常都是“Hello World”程序,这已经成为一种传统了。在你运行“Hello World”程序的时候,它所做的事只是说声:“Hello World”。正如提出“Hello World”程序的Simon Cozens[1]所说:“它是编程之神的传统咒语,可以帮助你更好的学习语言。” 启动你选择的编辑器,输入下面这段程序,然后把它保存为helloworld.py。 例3.2 使用源文件 #!/usr/bin/python # Filename : helloworld.py print 'Hello World' (源文件:code/helloworld.py) 为了运行这个程序,请打开shell(Linux终端或者DOS提示符),然后键入命令python helloworld.py。如果你使用IDLE,请使用菜单Edit->Run Script 或者使用键盘快捷方式Ctrl-F5。输出如下所示。 输出 $ python helloworld.py Hello World 如果你得到的输出与上面所示的一样,那么恭喜!——你已经成功地运行了你的第一个Python程序。 万一你得到一个错误,那么请确保你键入的程序准确无误,然后再运行一下程序。注意Python是大小写敏感的,即print与Print不一样——注意前一个是小写p而后一个是大写P。另外,确保在每一行的开始字符前没有空格或者制表符——我们将在后面讨论为什么这点是重要的。 它如何工作 让我们思考一下这个程序的前两行。它们被称作注释——任何在#符号右面的内容都是注释。注释主要作为提供给程序读者的笔记。 Python至少应当有第一行那样的特殊形式的注释。它被称作组织行——源文件的头两个字符是#!,后面跟着一个程序。这行告诉你的Linux/Unix系统当你执行你的程序的时候,它应该运行哪个解释器。这会在下一节做详细解释。注

Linux系统下各文件目录的含义

随着Linux的不断发展,越来越多的人开始使用Linux,对于那些刚刚接触的人来说,恐怕最先感到困惑的就是那些“不明不白”的目录了。如果想熟练使用Linux,让Linux听命于自己,就必须掌握这些目录,下面就以Xteam公司的最新产品——XteamLinux 4.0为例,介绍一下在该系统下的目录。 /bin bin是Binary的缩写。这个目录存放着最经常使用的命令。 /boot这里存放的是启动Linux时使用的一些核心文件,包括一些链接文件以及镜像文件。 /dev dev是Device(设备)的缩写。该目录下存放的是Linux的外部设备,在Linux中访问设备的方式和访问文件的方式是相同的。 /etc这个目录用来存放所有的系统管理所需要的配置文件和子目录。 /home用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。 /lib这个目录里存放着系统最基本的动态链接共享库,其作用类似于Windows里的DLL文件。几乎所有的应用程序都需要用到这些共享库。 /lost+found这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。 /mnt在这里面中有四个目录,系统提供这些目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在/mnt/cdrom上,然后进入该目录就可以查看光驱里的内容了。 /proc这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器: echo 1 >; /proc/sys/net/ipv4/icmp_echo_ignore_all。 /root该目录为系统管理员,也称作超级权限者的用户主目录。 /sbin s就是Super User的意思,这里存放的是系统管理员使用的系统管理程序。 /tmp这个目录是用来存放一些临时文件的。 我们要用到的很多应用程序和文件几乎都存放在usr目录下。具体来说: /usr/X11R6存放X-Windows的目录; /usr/games存放着XteamLinux自带的小游戏;

linux可执行文件格式

Linux可执行文件格式 ?Elf 也就是“Executable and Linking Format.” ?Elf 起源于Unix,经改进应用于FreeBSD和Linux等现有类Unix操作系统。 ?微软的PE格式也学习了ELF格式的优点。 ?ELF文档服务于在不同的操作系统上目标文件的创建或者执行文件的开发。它分以下三个部分: ?“目标文件”描述了ELF目标文件格式三种主要的类型。 ?“程序装载和动态连接”描述了目标文件的信息和系统在创建运行时程序的行为。 ?“C 语言库”列出了所有包含在libsys中的符号、标准的ANSIC和libc的运行程序,还有libc运行程序所需的全局的数据符号。 三种主要类型: ?一个可重定位文件(relocatable file)保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享文件。 ?一个可执行文件(executable file)保存着一个用来执行的程序,该文件指出了exec(BA_OS)如何来创建程序进程映象。 ?一个共享目标文件(shared object file)保存着代码和合适的数据,用来被下面的两个链接器链接。第一个是链接编辑器,可以和其他的重定位和共享目标文件来创建另一个目标文件。第二个是动态链接器,联合一个可执行文件和其他的共享目标文件来创建一个进程映象。 ELF头 ?#define EI_NIDENT 16 ?typedef struct { ?unsigned char e_ident[EI_NIDENT]; ?Elf32_Half e_type;

?Elf32_Half e_machine; ?Elf32_Word e_version; ?Elf32_Addr e_entry; ?Elf32_Off e_phoff; ?Elf32_Off e_shoff; ?Elf32_Word e_flags; ?Elf32_Half e_ehsize; ?Elf32_Half e_phentsize; ?Elf32_Half e_phnum; ?Elf32_Half e_shentsize; ?Elf32_Half e_shnum; ?Elf32_Half e_shstrndx; ?} Elf32_Ehdr; 节 ?一个目标文件的节头表可以让我们定位所有的节。节头表是一个Elf32_Shdr结构的数组。一个节头表的索引是这个数组的下标。 ?ELF头结构中的e_shoff成员给出了节头表的偏移量(从文件开始计数)。 ?e_shnum告诉我们节头表中包含了多少个表项; ?e_shentsize 给出了每个表项的长度。某些节头表索引是保留的,这些索引在目标文

Linux下 .sh 文件语法

linux 下.sh 文件语法 介绍: 1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。 当编写脚本完成时,如果要执行该脚本,还必须使其可执行。 要使编写脚本可执行: 编译chmod +x filename 这样才能用./filename 来运行 2 注释 在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。我们真诚地建议您在程序中使用注释。 如果您使用了注释,那么即使相当长的时间内没有使用该脚本,您也能在很短的时间内明白该脚本的作用及工作原理。 3 变量 在其他编程语言中您必须使用变量。在shell编程中,所有的变量都由字符串组成,并且您不需要对变量进行声明。要赋值给一个变量,您可以这样写: #!/bin/sh #对变量赋值: a=”hello world” # 现在打印变量a的内容: echo “A is:” echo $a 有时候变量名很容易与其他文字混淆,比如: num=2 echo “this is the $numnd” 这并不会打印出”this is the 2nd”,而仅仅打印”this is the “,因为shell会去搜索变量numnd的值,但是这个变量时没有值的。可以使用花括号来告诉shell我们要打印的是num变量: num=2 echo “this is the ${num}nd” 这将打印:this is the 2nd 4 环境变量 由export关键字处理过的变量叫做环境变量。我们不对环境变量进行讨论,因为通常情况下仅仅在登录脚本中使用环境变量。 5 Shell命令和流程控制 在shell脚本中可以使用三类命令: 1)Unix 命令: 虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。 常用命令语法及功能

在Linux下修改文件及文件夹权限的命令详解

先来了解一下文件属性, 在shell环境里输入:ls -l 可以查看当前目录文件。如: drwxr-xr-x 2 nsf users 1024 12-10 17:37 下载文件备份 分别对应的是: 文件属性连接数文件拥有者所属群组文件大小文件修改时间文件名 这里r是可读,w可写,x 可执行,其中文件属性分为四段,---- --- --- 10个位置 例如: d rwx r-x r-x 第一个字符指定了文件类型。在通常意义上,一个目录也是一个文件。如果第一个字符是横线,表示是一个非目录的文件。如果是d,表示是一个目录。 第二段是文件拥有者的属性, 第三段是文件所属群组的属性, 第四段是对于其它用户的属性, 如上面文件夹“下载文件备份” 的访问权限,表示文件夹“下载文件备份” 是一个目录文件;文件夹“下载文件备份” 的属主有读写可执行权限;与文件夹“下载文件备份” 属主同组的用户只有读和可执行权限;其他用户也有读和可执行权限。 确定了一个文件的访问权限后,用户可以利用Linux系统提供的chmod命令来重新设定不同的访问权限。也可以利用chown命令来更改某个文件或目录的所有者。利用chgrp命令来更改某个文件或目录的用户组。 chmod 命令 功能:chmod命令是非常重要的,用于改变文件或目录的访问权限.用户用它控制文件或目录的访问权限. 语法:该命令有两种用法。一种是包含字母和操作符表达式的文字设定法;另一种是包含数字的数字设定法。 1. 文字设定法 chmod [who] [+ | - | =] [mode] 文件名? 参数: 操作对象who可是下述字母中的任一个或者它们的组合: u 表示“用户(user)”,即文件或目录的所有者。 g 表示“同组(group)用户”,即与文件属主有相同组ID的所有用户。 o 表示“其他(others)用户”。 a 表示“所有(all)用户”。它是系统默认值。 操作符号可以是: + 添加某个权限。 - 取消某个权限。 = 赋予给定权限并取消其他所有权限(如果有的话)。 设置mode所表示的权限可用下述字母的任意组合: r 可读。 w 可写。 x 可执行。 X 只有目标文件对某些用户是可执行的或该目标文件是目录时才追加x 属性。 s 在文件执行时把进程的属主或组ID置为该文件的文件属主。方式“u+s”设置文件的用户ID位,“g+s”设置组ID位。

相关主题
文本预览
相关文档 最新文档