Linux C编程一站式学习 记录


Linux C编程一站式学习


正文

《Linux C编程一站式学习》 是宋劲杉写的一本关系C语言学习的好书,充分考虑到了学以致用的环境Linux。 看了好多C语言的书,包括谭浩强的那本,但还是无法下手编程,不能解决实际操作问题,另外感觉只是翻书, 始终不了解C代码是怎么操作计算机底层的,查来查去说道要想知道C语言是怎样与计算机底层交互的, 就需要去把这几本书翻一遍:编译原理、操作系统、计算机组成原理、数据结构、数据库、网络、软件工程 等。 这里面哪一本不是砖头厚?听名字都要后退3步。所以一直想能找到一本领进门的书,这本书可以使人更透彻的学习。 偶然逛知乎碰到了《Linux C编程一站式学习》,翻了几天,被这本书打动了,这就是我一直在找的那本书, 入门浅显、知识点覆盖全面、知其然还能知其所以然,而且还是开源的。这里就是一些知识点记录一下,以备查阅翻看。

gdb http://akaedu.github.io/book/ch10.html

Linux 家族历史:

计算机内核运行模式:

c运算符优先级

  1. 标识符、 常量、 字符串 和 用()括号套起来的表达式 是组成表达式的最基本单元,在运算中做操作数,优先级最高。

  2. 后缀运算符,包括数组取下标[] 、 函数调用() 、 结构体取成员. 、 指向结构体的指针取成员->后缀自增++后缀自减--。 如果一个操作数后面有多个后缀,按照离操作数从近到远的顺序(也就是从左到右)依次计算, 比如 a.name++ ,先算 a.name ,再++,这里的 .name 应该看成 a 的一个后缀,而不是把 . 看成双目运算符。

  3. 单目运算符,包括++前缀自增--前缀自减sizeof 、 类型转换()、 取地址运算& 、 指针间接寻址*正号+负号-按位取反~逻辑非!。 如果一个操作数前面有多个前缀,按照离操作数从近到远的顺序(也就是从右到左)依次计算,比如 !~a ,先算 ~a ,再求!。

  4. 乘*、除/、模%运算符。 这三个运算符是左结合的。

  5. 加+、减-运算符。左结合。

  6. 移位运算符«和»。左结合。

  7. 关系运算符< > <= >=。左结合。

  8. 相等性运算符==和!=。左结合。

  9. 按位与&。左结合。

  10. 按位异或^。左结合。

  11. 按位或|。左结合。

  12. 逻辑与&&。左结合。

  13. 逻辑或||。左结合。

  14. 条件运算符:?。在第 2 节 “if/else语句”讲过Dangling-else问题,条件运算符也有类似的问题。 例如 a ? b : c ? d : e 是看成 (a ? b : c) ? d : e 还是 a ? b : (c ? d : e) 呢?C语言规定是后者。

  15. 赋值 = 和 各种复合赋值*= /= %= += -= <<= >>= &= ^= |=。 在双目运算符中只有赋值和复合赋值是右结合的。

  16. 逗号运算符 。 左结合。

指向指针的指针

指针可以指向复合类型。

指针也可以指向指针自己,看一个例子:

#include <stdio.h>

int main(void)
{
    int i;
    int *pi = &i;
    int **ppi = &pi;
    printf("   &i=%p\n   pi=%p\n  &pi=%p\n  ppi=%p\n *ppi=%p\n", &i, pi, &pi, ppi, *ppi);
   
    return 0;
}

输出:

   &i=0x7ffdcce13ed4
   pi=0x7ffdcce13ed4
  &pi=0x7ffdcce13ec8
  ppi=0x7ffdcce13ec8
 *ppi=0x7ffdcce13ed4

可以看出,* 只是一个标示,说明这个变量保存的是另一个标量的地址,如ppi保存的是pi的地址,*ppi意思是读取ppi保存的值也就是pi地址上的保存的值,即i的地址。

可以看出*(&i) = i , 分析下**ppi**ppi = *(*ppi) = *(*(&pi)) = *pi = *(&i) = i,从里面也看到了*ppi = *(&pi) = pi = &i

指向数组的指针

先看一下 typedef 的使用。

C标准规定 size_t 是一种无符号整型,编译器可以用 typedef 做一个类型声明:

typedef unsigned long size_t;

那么 size_t 就代表 unsigned long 型。不同平台的编译器可能会根据自己平台的具体情况定义 size_t 所代表的类型, 比如有的平台定义为 unsigned long 型,有的平台定义为 unsigned long long 型,C标准规定 size_t 这个名字就是为了隐藏这些细节, 使代码具有可移植性。

所以注意不要把 size_t 类型和它所代表的真实类型混用,例如:

unsigned long x;
size_t y;
x = y;

如果在一种ILP32平台上定义 size_t 代表 unsigned long long 型,这段代码把 y 赋给 x 时就把高位截掉了,结果可能是错的。

typedef 这个关键字用于给某种类型起个新名字,比如上面的 typedef 声明可以这么看:去掉 typedef 就成了一个变量声明 unsigned long size_t; , size_t 是一个变量名,类型是 unsigned long ,那么加上 typedef 之后, size_t 就是一个类型名,就代表 unsigned long 类型。 再举个例子:

typedef char array_t[10];
array_t a;

这相当于声明 char a[10]; 。 类型名也遵循标识符的命名规则,并且通常加个 _t 后缀表示Type。

定义一个指向数组的指针,该数组有10个int元素:

int (*a)[10];

和指针数组的定义int *a[10]; 相比,仅仅多了一个()括号。如何记住和区分这两种定义呢? 我们可以认为[]*有更高的优先级,如果a先和*结合则表示a是一个指针,如果a先和[]结合则表示a是一个数组。

int *a[10]; 这个定义可以拆成两句:

typedef int *t;
t a[10];

t代表int *类型,a则是由这种类型的元素组成的数组。

int (*a)[10]; 这个定义也可以拆成两句:

typedef int t[10];
t *a;

t代表由10个int组成的数组类型,a则是指向这种类型的指针。

现在看指向数组的指针如何使用:

int a[10];
int (*pa)[10] = &a;

a是一个数组,在&a这个表达式中,数组名做左值,取整个数组的首地址赋给指针pa。注意,&a[0]表示数组a的首元素的首地址,而&a表示数组a的首地址, 显然这两个地址的数值相同,但这两个表达式的类型是两种不同的指针类型,前者&a[0]的类型是int *,而后者&a的类型是int (*)[10]*pa就表示pa所指向的数组a,所以取数组的a[0]元素可以用表达式(*pa)[0]。 注意到*pa可以写成pa[0], 所以(*pa)[0]这个表达式也可以改写成pa[0][0],pa就像一个二维数组的名字,它表示什么含义呢?下面把pa和二维数组放在一起做个分析。

int a[5][10];int (*pa)[10]; 之间的关系同样类似于int a[10];int *pa; 之间的关系:a是由一种元素组成的数组,pa则是指向这种元素的指针。 所以,如果pa指向a的首元素:

int a[5][10];
int (*pa)[10] = &a[0];

pa[0]a[0] 取的是同一个元素,唯一比原来复杂的地方在于这个元素是由10个int组成的数组,而不是基本类型。 这样,我们可以把pa当成二维数组名来使用,pa[1][2]a[1][2] 取的也是同一个元素,而且pa比a用起来更灵活,数组名不支持赋值、自增等运算, 而指针可以支持,pa++使pa跳过二维数组的一行(40个字节),指向a[1]的首地址。

或者换个角度想一下,数组的本质就是指针,一维数组 int a【10】; int *pa = &a[0];, 数组名做右值时自动转换成指向首元素的指针, int *pa = &a[0]; => int *pa = a;,可以看出pa就是a,*pa = *a = *(&a[0]) = a[0], 取a[2]的值有 a[2] = *(pa + 2) = *(&a[0] + 2) = *(a + 2)a[2]pa[2] 本质上是一样的,都是通过指针间接寻址访问元素。

一维数组用指针可以运算,如果二维数组每一行的第1个位置都有指针指向,那二维数组运算起来会方便很多,而这个正是上面说的指向数组的指针, 指针加1就是移到下一行的第1个位置。三维数组也类似。

链表

linkedlist.h

/* linkedlist.h */
#ifndef LINKEDLIST_H
#define LINKEDLIST_H

typedef struct node *link;
struct node {
    unsigned char item;
    link next;
};

link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void delete(link p);
void traverse(void (*visit)(link));
void destroy(void);
void push(link p);
link pop(void);

#endif

linkedlist.c

/* linkedlist.c */
#include <stdlib.h>
#include "linkedlist.h"

static link head = NULL;

link make_node(unsigned char item)
{
    link p = malloc(sizeof *p);
    p->item = item;
    p->next = NULL;
    return p;
}

void free_node(link p)
{
    free(p);
}

link search(unsigned char key)
{
    link p;
    for (p = head; p; p = p->next)
        if (p->item == key)
            return p;
    return NULL;
}

void insert(link p)
{
    p->next = head;
    head = p;
}

void delete(link p)
{
    link pre;
    if (p == head) {
        head = p->next;
        return;
    }
    for (pre = head; pre; pre = pre->next)
        if (pre->next == p) {
            pre->next = p->next;
            return;
        }
}

/* delete方法改进 */
void delete(link p)
{
    link *pnext;
    for (pnext = &head; *pnext; pnext = &(*pnext)->next)
        if (*pnext == p) {
            *pnext = p->next;
            return;
        }
}

void traverse(void (*visit)(link))
{
    link p;
    for (p = head; p; p = p->next)
        visit(p);
}

void destroy(void)
{
    link q, p = head;
    head = NULL;
    while (p) {
        q = p;
        p = p->next;
        free_node(q);
    }
}

void push(link p)
{
    insert(p);
}

link pop(void)
{
    if (head == NULL)
        return NULL;
    else {
        link p = head;
        head = head->next;
        return p;
    }
}

main.c

/* main.c */
#include <stdio.h>
#include "linkedlist.h"

void print_item(link p)
{
    printf("%d\n", p->item); 
}

int main(void)
{
    link p = make_node(10);
    insert(p);
    p = make_node(5);
    insert(p);
    p = make_node(90);
    insert(p);
    p = search(5);
    delete(p);
    free_node(p);
    traverse(print_item);
    destroy();

    p = make_node(100);
    push(p);
    p = make_node(200);
    push(p);
    p = make_node(250);
    push(p);
    while (p = pop()) {
        print_item(p);
        free_node(p);
    }

    return 0;
}
双向链表

doublylinkedlist.h

/* doublylinkedlist.h */
#ifndef DOUBLYLINKEDLIST_H
#define DOUBLYLINKEDLIST_H

typedef struct node *link;
struct node {
    unsigned char item;
    link prev, next;
};

link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void delete(link p);
void traverse(void (*visit)(link));
void destroy(void);
void enqueue(link p);
link dequeue(void);

#endif

doublylinkedlist.c

/* doublylinkedlist.c */
#include <stdlib.h>
#include "doublylinkedlist.h"

struct node tailsentinel;
struct node headsentinel = {0, NULL, &tailsentinel};
struct node tailsentinel = {0, &headsentinel, NULL};

static link head = &headsentinel;
static link tail = &tailsentinel;

link make_node(unsigned char item)
{
    link p = malloc(sizeof *p);
    p->item = item;
    p->prev = p->next = NULL;
    return p;
}

void free_node(link p)
{
    free(p);
}

link search(unsigned char key)
{
    link p;
    for (p = head->next; p != tail; p = p->next)
        if (p->item == key)
            return p;
    return NULL;
}

void insert(link p)
{
    p->next = head->next;
    head->next->prev = p;
    head->next = p;
    p->prev = head;
}

void delete(link p)
{
    p->prev->next = p->next;
    p->next->prev = p->prev;
}

void traverse(void (*visit)(link))
{
    link p;
    for (p = head->next; p != tail; p = p->next)
        visit(p);
}

void destroy(void)
{
    link q, p = head->next;
    head->next = tail;
    tail->prev = head;
    while (p != tail) {
        q = p;
        p = p->next;
        free_node(q);
    }
}

void enqueue(link p)
{
    insert(p);
}

link dequeue(void)
{
    if (tail->prev == head)
        return NULL;
    else {
        link p = tail->prev;
        delete(p);
        return p;
    }
}

main.c

/* main.c */
#include <stdio.h>
#include "doublylinkedlist.h"

void print_item(link p)
{
    printf("%d\n", p->item); 
}

int main(void)
{
    link p = make_node(10);
    insert(p);
    p = make_node(5);
    insert(p);
    p = make_node(90);
    insert(p);
    p = search(5);
    delete(p);
    free_node(p);
    traverse(print_item);
    destroy();

    p = make_node(100);
    enqueue(p);
    p = make_node(200);
    enqueue(p);
    p = make_node(250);
    enqueue(p);
    while (p = dequeue()) {
        print_item(p);
        free_node(p);
    }

    return 0;
}

二叉树

binarytree.h

/* binarytree.h */
#ifndef BINARYTREE_H
#define BINARYTREE_H

typedef struct node *link;
struct node {
     unsigned char item;
     link l, r;
};

link init(unsigned char VLR[], unsigned char LVR[], int n);
void pre_order(link t, void (*visit)(link));
void in_order(link t, void (*visit)(link));
void post_order(link t, void (*visit)(link));
int count(link t);
int depth(link t);
void destroy(link t);

#endif

binarytree.c

/* binarytree.c */
#include <stdlib.h>
#include "binarytree.h"

static link make_node(unsigned char item)
{
    link p = malloc(sizeof *p);
    p->item = item;
    p->l = p->r = NULL;
    return p;
}

static void free_node(link p)
{
    free(p);
}

link init(unsigned char VLR[], unsigned char LVR[], int n)
{
    link t;
    int k;
    if (n <= 0)
        return NULL;
    for (k = 0; VLR[0] != LVR[k]; k++);
    t = make_node(VLR[0]);
    t->l = init(VLR+1, LVR, k);
    t->r = init(VLR+1+k, LVR+1+k, n-k-1);
    return t;
}

void pre_order(link t, void (*visit)(link))
{
    if (!t)
        return;
    visit(t);
    pre_order(t->l, visit);
    pre_order(t->r, visit);
}

void in_order(link t, void (*visit)(link))
{
    if (!t)
        return;
    in_order(t->l, visit);
    visit(t);
    in_order(t->r, visit);
}

void post_order(link t, void (*visit)(link))
{
    if (!t)
        return;
    post_order(t->l, visit);
    post_order(t->r, visit);
    visit(t);
}

int count(link t)
{
    if (!t)
        return 0;
    return 1 + count(t->l) + count(t->r);
}

int depth(link t)
{
    int dl, dr;
    if (!t)
        return 0;
    dl = depth(t->l);
    dr = depth(t->r);
    return 1 + (dl > dr ? dl : dr);
}

void destroy(link t)
{
    post_order(t, free_node);
}

main.c

/* main.c */
#include <stdio.h>
#include "binarytree.h"

void print_item(link p)
{
    printf("%d", p->item);
}

int main()
{
    unsigned char pre_seq[] = { 4, 2, 1, 3, 6, 5, 7 };
    unsigned char in_seq[] = { 1, 2, 3, 4, 5, 6, 7 };
    link root = init(pre_seq, in_seq, 7);
    pre_order(root, print_item);
    putchar('\n');
    in_order(root, print_item);
    putchar('\n');
    post_order(root, print_item);
    putchar('\n');
    printf("count=%d depth=%d\n", count(root), depth(root));
    destroy(root);
    return 0;
}
排序二叉树

排序二叉树(BST,Binary Search Tree)具有这样的性质:对于二叉树中的任意节点,如果它有左子树或右子树, 则该节点的数据成员大于左子树所有节点的数据成员,且小于右子树所有节点的数据成员。

bst.h

/* bst.h */
#ifndef BST_H
#define BST_H

typedef struct node *link;
struct node {
     unsigned char item;
     link l, r;
};

link search(link t, unsigned char key);
link insert(link t, unsigned char key);
link delete(link t, unsigned char key);
void print_tree(link t);

#endif

bst.c

/* bst.c */
#include <stdlib.h>
#include <stdio.h>
#include "bst.h"

static link make_node(unsigned char item)
{
    link p = malloc(sizeof *p);
    p->item = item;
    p->l = p->r = NULL;
    return p;
}

static void free_node(link p)
{
    free(p);
}

link search(link t, unsigned char key)
{
    if (!t)
        return NULL;
    if (t->item > key)
        return search(t->l, key);
    if (t->item < key)
        return search(t->r, key);
    /* if (t->item == key) */
    return t;
}

link insert(link t, unsigned char key)
{
    if (!t)
        return make_node(key);
    if (t->item > key) /* insert to left subtree */
        t->l = insert(t->l, key);
    else /* if (t->item <= key), insert to right subtree */
        t->r = insert(t->r, key);
    return t;
}

link delete(link t, unsigned char key)
{
    link p;
    if (!t)
        return NULL;
    if (t->item > key) /* delete from left subtree */
        t->l = delete(t->l, key);
    else if (t->item < key) /* delete from right subtree */
        t->r = delete(t->r, key);
    else { /* if (t->item == key) */
        if (t->l == NULL && t->r == NULL) { /* if t is leaf node */
            free_node(t);
            t = NULL;
        } else if (t->l) { /* if t has left subtree */
            /* replace t with the rightmost node in left subtree */
            for (p = t->l; p->r; p = p->r);
            t->item = p->item;
            t->l = delete(t->l, t->item);
        } else { /* if t has right subtree */
            /* replace t with the leftmost node in right subtree */
            for (p = t->r; p->l; p = p->l);
            t->item = p->item;
            t->r = delete(t->r, t->item);
        }
    }
    return t;
}

void print_tree(link t)
{
    if (t) {
        printf("(");
        printf("%d", t->item);
        print_tree(t->l);
        print_tree(t->r);
        printf(")");
    } else
        printf("()");
}

main.c

/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "bst.h"

#define RANGE 100
#define N 6

void print_item(link p)
{
    printf("%d", p->item);
}

int main()
{
    int i, key;
    link root = NULL;
    srand(time(NULL));
    for (i = 0; i < N; i++)
        root = insert(root, rand() % RANGE);
    printf("\t\\tree");
    print_tree(root);
    printf("\n\n");
    while (root) {
        key = rand() % RANGE;
        if (search(root, key)) {
            printf("delete %d in tree\n", key);
            root = delete(root, key);
            printf("\t\\tree");
            print_tree(root);
            printf("\n\n");
        }
    }
}

TCP/iP协议基础

网络七层协议:

常用设备 常用协议
7 应用层   HTTP、 FTP、 SMTP、 TELNET、 NFS
6 表示层 URL加密、 口令加密、 图片编解码 等  
5 会话层    
4 传输层 进程、 端口 TCP、 UDP
3 网络层 路由器、 多层交换机、 防火墙 IP
2 数据链路层 网卡、 网桥、 二层交换机 等 ARP、 RARP
1 物理层 集线器、 中继器、 网线 等 Rj45、 802.3

注意,虽然IP、ARP和RARP数据报都需要以太网驱动程序来封装成帧,但是从功能上划分,ARP和RARP属于链路层,IP属于网络层。 虽然ICMP、IGMP、TCP、UDP的数据都需要IP协议来封装成数据报,但是从功能上划分,ICMP、IGMP与IP同属于网络层,TCP和UDP属于传输层。

ARP数据报格式

ARP协议:获得目的主机的硬件地址。

看个例子:

请求帧:

以太网首部(14字节)
0000:      ff ff ff ff ff ff 00 05 5d 61 58 a8        08 06
ARP帧(28字节)
0000:                                           00 01
0010:         08 00           06      04       00 01        00 05 5d 61 58 a8          c0 a8 00 37
0020:         00 00 00 00 00 00         c0 a8 00 02
填充位(18字节)
0020:                               00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00

以太网首部:目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4, op为0x0001表示请求目的主机的MAC地址,源主机MAC地址为00:05:5d:61:58:a8,源主机IP地址为c0 a8 00 37(192.168.0.55), 目的主机MAC地址全0待填写,目的主机IP地址为c0 a8 00 02(192.168.0.2)。 由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充位,填充位的内容没有定义,与具体实现相关。

应答帧:

以太网首部
0000:       00 05 5d 61 58 a8 00 05 5d a1 b8 40        08 06
ARP帧
0000:                                           00 01
0010:         08 00           06        04      00 02          00 05 5d a1 b8 40       c0 a8 00 02
0020:         00 05 5d 61 58 a8         c0 a8 00 37
填充位
0020:                               00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00

以太网首部:目的主机的MAC地址是00:05:5d:61:58:a8,源主机的MAC地址是00:05:5d:a1:b8:40,上层协议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4, op为0x0002表示应答,源主机MAC地址为00:05:5d:a1:b8:40,源主机IP地址为c0 a8 00 02(192.168.0.2), 目的主机MAC地址为00:05:5d:61:58:a8,目的主机IP地址为c0 a8 00 37(192.168.0.55)。

汇编程序

一个简单示例:

 #PURPOSE: Simple program that exits and returns a
#status code back to the Linux kernel
#
#INPUT:none
#
#OUTPUT: returns a status code. This can be viewed
#by typing
#
#echo $?
#
#after running the program
#
#VARIABLES:
#%eax holds the system call number
#%ebx holds the return status
#
.section .data
.section .text
.globl _start
_start:
movl $1, %eax # this is the linux kernel command
# number (system call) for exiting
# a program
movl $4, %ebx # this is the status number we will
# return to the operating system.
# Change this around and it will
# return different things to
# echo $?
int $0x80 # this wakes up the kernel to run
# the exit command

找到最大值示例:

max.s:

#PURPOSE: This program finds the maximum number of a
#set of data items.
#
#VARIABLES: The registers have the following uses:
#
# %edi - Holds the index of the data item being examined
# %ebx - Largest data item found
# %eax - Current data item
#
# The following memory locations are used:
#
# data_items - contains the item data. A 0 is used
# to terminate the data
#
.section .data
data_items: #These are the data items
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi
# move 0 into the index register
movl data_items(,%edi,4), %eax # load the first byte of data
movl %eax, %ebx
# since this is the first item, %eax is
# the biggest
start_loop:
# start loop
cmpl $0, %eax
# check to see if we've hit the end
je loop_exit
incl %edi
# load next value
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
# compare values
jle start_loop
# jump to loop beginning if the new
# one isn't bigger
movl %eax, %ebx
# move the value as the largest
jmp start_loop
# jump to loop beginning
loop_exit:
# %ebx is the status code for the _exit system call
# and it already has the maximum number
movl $1, %eax
#1 is the _exit() syscall
int $0x80

用汇编器(Assembler) as 把汇编程序中的助记符翻译成机器指令:

as max.s -o max.o

用链接器(Linker,或Link Editor) ld 把目标文件 max.o 链接成可执行文件 max:

ld max.o -o max

程序运行:

./max

用特殊变量 $? 得到上一条命令的退出状态:

echo $?

看一下目标文件max.o的ELF Header和Section Header Table:

ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x0
  程序头起点:              0 (bytes into file)
  Start of section headers:          504 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       0 (字节)
  Number of program headers:         0
  节头大小:         64 (字节)
  节头数量:         8
  字符串表索引节头: 7

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040
       000000000000002d  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000190
       0000000000000030  0000000000000018   I       5     1     8
  [ 3] .data             PROGBITS         0000000000000000  0000006d
       0000000000000038  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000000  000000a5
       0000000000000000  0000000000000000  WA       0     0     1
  [ 5] .symtab           SYMTAB           0000000000000000  000000a8
       00000000000000c0  0000000000000018           6     7     8
  [ 6] .strtab           STRTAB           0000000000000000  00000168
       0000000000000028  0000000000000000           0     0     1
  [ 7] .shstrtab         STRTAB           0000000000000000  000001c0
       0000000000000031  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

本文件中没有程序头。

重定位节 '.rela.text' 位于偏移量 0x190 含有 2 个条目:
  偏移量          信息           类型           符号值        符号名称 + 加数
000000000009  00020000000b R_X86_64_32S      0000000000000000 .data + 0
00000000001a  00020000000b R_X86_64_32S      0000000000000000 .data + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.symtab' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    3 data_items
     5: 000000000000000f     0 NOTYPE  LOCAL  DEFAULT    1 start_loop
     6: 0000000000000026     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit
     7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    1 _start

No version information found in this file.

机器指令反汇编(Disassemble)看一下:

objdump -d max.o

max.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:    bf 00 00 00 00           mov    $0x0,%edi
   5:    67 8b 04 bd 00 00 00     mov    0x0(,%edi,4),%eax
   c:    00 
   d:    89 c3                    mov    %eax,%ebx

000000000000000f <start_loop>:
   f:    83 f8 00                 cmp    $0x0,%eax
  12:    74 12                    je     26 <loop_exit>
  14:    ff c7                    inc    %edi
  16:    67 8b 04 bd 00 00 00     mov    0x0(,%edi,4),%eax
  1d:    00 
  1e:    39 d8                    cmp    %ebx,%eax
  20:    7e ed                    jle    f <start_loop>
  22:    89 c3                    mov    %eax,%ebx
  24:    eb e9                    jmp    f <start_loop>

0000000000000026 <loop_exit>:
  26:    b8 01 00 00 00           mov    $0x1,%eax
  2b:    cd 80                    int    $0x80

看一下可执行文件max:

readelf -a max

ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:              0x4000b0
  程序头起点:              64 (bytes into file)
  Start of section headers:          656 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       56 (字节)
  Number of program headers:         2
  节头大小:         64 (字节)
  节头数量:         6
  字符串表索引节头: 5

节头:
  [号] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         00000000004000b0  000000b0
       000000000000002d  0000000000000000  AX       0     0     1
  [ 2] .data             PROGBITS         00000000006000dd  000000dd
       0000000000000038  0000000000000000  WA       0     0     1
  [ 3] .symtab           SYMTAB           0000000000000000  00000118
       0000000000000108  0000000000000018           4     7     8
  [ 4] .strtab           STRTAB           0000000000000000  00000220
       0000000000000043  0000000000000000           0     0     1
  [ 5] .shstrtab         STRTAB           0000000000000000  00000263
       0000000000000027  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

There are no section groups in this file.

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000000dd 0x00000000000000dd  R E    0x200000
  LOAD           0x00000000000000dd 0x00000000006000dd 0x00000000006000dd
                 0x0000000000000038 0x0000000000000038  RW     0x200000

 Section to Segment mapping:
  段节...
   00     .text 
   01     .data 

There is no dynamic section in this file.

该文件中没有重定位信息。

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table '.symtab' contains 11 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000004000b0     0 SECTION LOCAL  DEFAULT    1 
     2: 00000000006000dd     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test232.o
     4: 00000000006000dd     0 NOTYPE  LOCAL  DEFAULT    2 data_items
     5: 00000000004000bf     0 NOTYPE  LOCAL  DEFAULT    1 start_loop
     6: 00000000004000d6     0 NOTYPE  LOCAL  DEFAULT    1 loop_exit
     7: 00000000004000b0     0 NOTYPE  GLOBAL DEFAULT    1 _start
     8: 0000000000600115     0 NOTYPE  GLOBAL DEFAULT    2 __bss_start
     9: 0000000000600115     0 NOTYPE  GLOBAL DEFAULT    2 _edata
    10: 0000000000600118     0 NOTYPE  GLOBAL DEFAULT    2 _end

No version information found in this file.






参考资料

Linux C编程一站式学习 http://akaedu.github.io/book/

Linux基本操作学习 http://happypeter.github.io/LGCB/book/index.html

亚嵌教育 http://akaedu.github.io/

你是如何学习 Linux 编程的? https://www.zhihu.com/question/20730157?sort=created

十个最值得阅读学习的C开源项目代码 https://blog.csdn.net/deeplee021/article/details/40583877

源码阅读——十个C开源项目 https://my.oschina.net/zhoukuo/blog/335788

源码开放学ARM http://www.lumit.org/


返回