一个简单的时间获取客户程序

本系统: Debian 12

如何下载源码并编译运行

官方源码地址:https://github.com/unpbook/unpv13e

git clone https://github.com/unpbook/unpv13e.git

克隆到本地之后,根据README文件一步一步make配置

接下来可以直接 make 想要运行的源码 然后执行即可

  • 一个问题:如何使用gcc来编译官方的源码

    首先把生成的静态库libudp.a 复制到系统依赖文件夹中

    1
    2
    3
    cd ..   //回到unpv13e目录  
    sudo cp libunp.a /usr/lib
    sudo cp libunp.a /usr/lib64

    然后修改unp.h并将其和config.h拷贝到/usr/include中,为了以后include方便

    1
    2
    3
    gedit lib/unp.h   //将unp.h中#include "../config.h"修改为#include "config.h"  
    sudo cp lib/unp.h /usr/include
    sudo cp config.h /usr/include

    然后就可以愉快的编译了:

    1
    2
    cd ./intro  
    gcc daytimetcpcli.c -o daytimetcpcli -lunp // 链接unp库
  • 同理,如果想要自己重新另建一个文件夹来编译运行的话

    首先要完成上面的步骤:静态库和头文件的拷贝

    然后就可以使用gcc daytimetcpcli.c -o daytimetcpcli -lunp 来运行了

  • 另一个问题:如何使用两台主机测试

    这里我使用的是阿里云服务器

    **关键就是你要在安全组里开放13端口(时间服务器端口),而不是进程的端口号 **

    这里测试的话你可以使用下面命令

    1
    2
    ping ipadress			# 使用ping命令测试本地主机能否连接服务器
    telnet ipaddress 13 # 使用telnet命令测试服务器端口是否正常开启

    你可以在云服务器中使用下面命令来开放监听端口

    1
    sudo ./daytimetcpsrv &

    然后在本地主机上使用下面命令来测试

    1
    ./daytimetcpcli ipaddress	# 这里替换为云服务器的ip地址

参考

unix网络编程:编译源代码相关的问题

一些基本概念

CORS(跨域资源共享):浏览器的安全特性,允许你控制哪些外部域名访问你的API

API路由:简单来说就是根据客户端发起的请求(包括请求的方法,如GET、POST、PUT等,以及请求的URL路径)将请求映射到不同的处理逻辑中:

1.匹配客户端发来的请求

2.根据请求的路径和方法,找到并执行相应的代码(处理逻辑)

3.返回一个响应给客户端

代码解析

1
2
3
4
5
6
7
8
#Declare Endpoint
ENDPOINT("PUT", "/users/{userId}", putUser,
PATH(Int64, userId),
BODY_DTO(Object<UserDto>, userDto))
{
userDto->id = userId;
return createDtoResponse(Status::CODE_200, m_database->updateUser(userDto));
}

本段代码的作用是声明一个HTTP请求的API路由:

  • put: 说明这是一个HTTP PUT请求, 用于更新资源
  • /users/{userId}: 定义了API的路径,其中{userId}表示特定的用户,用来定位特定用户
  • putUser: 处理该请求的函数或者方法
  • PATH(Int64, userId): ;将路径中提取的参数userId绑定到Int64类型的userId变量中
  • BODY_DTO(Object<UserDto>, userDto): 请求体中的数据会被反序列化为一个UserDto对象,并包装在Object类型中。可以通过userDto访问请求体中的数据并进行处理,Object是oatpp框架中的一个智能指针类型

我们可以从这里得到ENDPOINT宏的参数说明和用法

1
ENDPOINT("<http-method>", "<path>", <method-name>, <optional param-mappings>)

当然你也可以直接在源码中找到ENDPOINT的定义:

1
2
3
4
5
6
7
8
 * Codegen macro to be used in `oatpp::web::server::api::ApiController` to generate Endpoint.
* @param METHOD - Http method ("GET", "POST", "PUT", etc.).
* @param PATH - Path to endpoint (without host).
* @param NAME - Name of the generated method.
* @return - std::shared_ptr to &id:oatpp::web::protocol::http::outgoing::Response;.
*/
#define ENDPOINT(METHOD, PATH, ...) \
OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_BINARY_SELECTOR(OATPP_MACRO_API_CONTROLLER_ENDPOINT_MACRO_, (__VA_ARGS__)) (METHOD, PATH, __VA_ARGS__))

这个宏定义一个HTTP endpoint,在ApiController中处理不同的HTTP方法,根据提供的HTTP方法和路径,利用多重宏选择机制和宏展开来生成适合的代码

前两天突发奇想想把一直在用的实机(Debian 12)换成Anolis OS,在安装这个系统的时候碰到了不少问题,现记录于此.

机器配置:

笔记本品牌: HUAWEI MateBook D 14

CPU型号: R5 4500U

内存: DDR4 16GB 2666MHz

硬盘: SSD 512GB

网络通信: Realtek RTL8821CE/RTL8822CE Wi-Fi

安装:

准备: 至少16GB的U盘

  1. Anolis官网下载需要的镜像

  2. 通过u盘刻录软件将镜像刻录到U盘(我用的是rufus), 其中刻录的时候要注意的是:

    1.分区类型(MBR或GPT)一定要选择与系统相匹配的类型, MateBook D 14要选择GPT,不然会导致启动u盘的时候被验证失败(Error: Vertification failed).

    2.写入模式要选择DD模式, 不要选择ISO模式, 不然会导致进入安装界面的时候本地仓库识别不到.

  3. 最后根据引导完成安装即可.

网络修复

由于本系统的网卡是Realtek RTL8821CE/RTL8822CE Wi-Fi, 在安装完Anolis OS系统后会出现网卡驱动无法加载的问题, 进入官网后发现Realtek官网并没有提供Linux系统的RTL8821CE/RTL8822CE Wi-Fi网卡驱动, 在网上搜索之后我发现了一个可以修复问题的方法:

西班牙大神做的一个RTL8822CE Wi-Fi网卡驱动(github页面), 但是作者只提供了.deb版本, 并没有.rpm版本, 但是我们可以从源码编译安装:

  1. 从其他设备到github页面下载驱动.
  2. 通过u盘拷贝到本系统, 注意: Anolis OS系统缺少对NTFS文件系统的支持, 因此无法挂在NTFS格式的U盘, 所以最好使用FAT32格式的U盘.
  3. 通过以下指令安装驱动:
1
2
3
4
5
6
7
8
9
10
11
12
13
# 将配置文件复制到/etc/modprobe.d/
$ sudo cp rtl88x2ce-dkms/rtw88_blacklist.conf /etc modprobe.d/rtw88_blacklist.conf

# 创建源码目录
$ sudo mkdir /usr/src/rtl88x2ce-35403

# 复制驱动源码文件
$ sudo cp -Rv rtl88x2ce-dkms/* /usr/src/rtl88x2ce-35403/

# 编译并安装驱动
$ cd /usr/src/rtl88x2ce-35403
$ make
$ sudo make install
  1. 加载模块

$ sudo modprobe 88x2ce

  1. 重启并检查是否可以使用

闲来无事刷个题目用所有方法解一遍斐波那契,没想到在记忆化翻车了

呆码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int fib(int n) {
vector<int> memo(n + 1, -1);
memo[0] = 0;
memo[1] = 1;

function<int(int)> mdfs = [&](int i) -> int {
if(i <= 1) return i;

if(memo[i] != -1) return memo[i];

memo[i] = mdfs(i - 1) + mdfs(i - 2);

return memo[i];
};

return mdfs(n);
}

看到代码通不过案例真的破防,检查了很多遍发现lambda函数没有问题,其他地方都看起来很河狸。

也问了gpt-4o, 同样没有给我正确的解释,对于gpt来说还是超前了嘛(黑人问号)

最后解决问题是因为这段代码只有n = 0时无法通过,所以我加代码又重试了两次, 都通过了,第一次是在函数开头加了if (n <= 1) return n;第二次是将第4行的memo[1] = 1;改为if(n > 0) memo[1] = 1;

! 诶,立马意识到是memo数组出了问题, 所以将代码改了一个地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int fib(int n) {
vector<int> memo(n + 10, -1); // 改为了n + 10, n == 0时memo数组长度只有1, 那么第4行的memo[1] = 1数组越界
memo[0] = 0;
memo[1] = 1;

function<int(int)> mdfs = [&](int i) -> int {
if(i <= 1) return i;

if(memo[i] != -1) return memo[i];

memo[i] = mdfs(i - 1) + mdfs(i - 2);

return memo[i];
};

return mdfs(n);
}

至此, 精神状态又美丽了几分。

unordered_map 的排序

unordered_map是C++的哈希表结构, unorded_map并不能直接排序, 如果要排序,一般来说需要将unordered_map转换为vector再通过自定义排序函数使用sort进行排序。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool cmp(pair<char, int>& a, pair<char,int>& b) {
return a.second > b.second; // 按照second降序
}

void sort_unordered_map() {
unordered_map<char, int> mp;

int idx = 1;
for(char c = 'a'; c <= 'z'; c++) {
mp[c] = idx++;
}

// 利用vector构造函数快速转换
vector<pair<char, int>> sorted_mp(mp.begin(), mp.end());

sort(sorted_mp.begin(), sorted_mp.end(), cmp);

// 此时可以选择直接使用排序后的vector或者再转换为哈希表
for(auto [a, b] : sorted_mp) {
cout << a << "" << b << endl;
}
}

关于sort函数:

默认函数有两种,第一个参数和第二个参数都是容器的起始地址和结束地址,第三个参数是排序函数,接受范围内的两个元素作为参数并返回可转换为布尔值的二元函数。返回的值表示作为第一个参数传递的元素是否被视为在其定义的特定严格弱排序中位于第二个参数之前。

template <class RandomAccessIterator> void sort (RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare> void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

sort对于不同类型的默认排序:

对数组排序: 默认从小到大排序

pair排序,默认对first从小到大排序,first相同是对second从小到大排序

对结构体: 一般需要自定义函数

二叉树的知识每次用都要重新复习一遍,所以索性写一篇博客加强记忆

二叉树的遍历有四种方式:前序遍历、中序遍历、后续遍历、层序遍历,下面主要通过递归和显式栈的方式来实现前中后序遍历, 层序遍历使用队列实现

前中后序遍历可以简便的记为: ‘中’的位置,即

​ 前序遍历为:中左右

​ 中序遍历为:左中右

​ 后续遍历为:左右中

0.二叉树的定义

1
2
3
4
5
6
7
template<class T>
struct TreeNode{
T val;
TreeNode* left;
TreeNode* right;
TreeNode(T val): val(val), left(nullptr), right(nullptr);
};

1.前序遍历

前序遍历笼统来讲就是先访问根节点,再访问根节点的左子树,最后访问根节点的右子树

具体规则如下:

  • 如果根节点为空,则返回
  • 如果根节点不为空:
    1. 访问根节点
    2. 以前序遍历的方式访问左子树
    3. 以前序遍历的方式访问右子树

img

1.1 前序遍历的递归实现

步骤:

  1. 判断根结点是否为空, 为空则返回
  2. 访问根节点
  3. 递归遍历左子树
  4. 递归遍历右子树

递归代码实现:

1
2
3
4
5
6
7
8
vector<int> res;
void preorder(TreeNode<int>* root) {
if(!root) return;

res.push_back(root->val);
preorder(root->left);
preorder(root->right);
}

时间复杂度分析: 每个节点只会被访问一次,即O(n), 其中n是树中节点的数量

空间复杂度分析: 递归的空间复杂度取决于树的高度,最坏情况下(完全不平衡的树)可以达到O(n),其中n是树的节点数。对于平衡树,空间复杂度为O(log n)。

1.2 前序遍历的显式栈实现

显式栈即使用栈来模拟递归的过程

前序遍历的顺序为:根节点 - 左子树 - 右子树, 根据栈的特点, 入栈的顺序为: 右子树 - 左子树 - 根节点

步骤:

  1. 判断根节点时候为空, 为空则返回
  2. 初始化维护一个栈,将根节点入栈
  3. 当栈不为空时:
    1. 弹出并访问栈顶元素
    2. 当node右子树不为空,将右子树入栈
    3. 当node左子树不为空,将左子树入栈

显式栈的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector<int> preorder(TreeNode<int>* root) {
vector<int> res;
stack<TreeNode<int>*> st;
st.push(root);

while(st.size()) {
TreeNode<int>* node = st.top();
res.push_back(node->val);
st.pop();

if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}

return res;
}

时间复杂度分析:与递归相同, 每个节点只会被访问一次,即O(n), 其中n是树中节点的数量

空间复杂度分析:理论上与递归相同,但在实践中,因为手动管理栈,有时可以通过优化减少空间使用。

2. 中序遍历

中序遍历简单来说就是先访问根节点的左子树,再访问根节点,最后访问根节点的右子树

具体规则如下:

  1. 如果root为空则返回
  2. 如果root非空:
    1. 中序遍历根节点左子树
    2. 访问根节点
    3. 中序遍历根节点右子树

img

因为中序遍历的顺序是左中右,所以如果二叉树是搜索树, 可以利用中序遍历来验证

2.1 中序遍历的递归实现

步骤:

  1. 判断根结点是否为空, 为空则返回
  2. 递归遍历左子树
  3. 访问根节点
  4. 递归遍历右子树

递归代码实现:

1
2
3
4
5
6
7
8
vector<int> res;
void inorder(TreeNode<int>* root) {
if(!root) return ;

if(root->left) inorder(root->left);
res.push_back(root->val);
if(root->right) inorder(root->right);
}

时间复杂度分析: O(n),同前序遍历

空间复杂度分析:同前序遍历

2.2 中序遍历的显式栈实现

因为是递归的显式栈实现,所以会比较难理解。

与前序遍历不同,访问根节点要放在左子树遍历完之后。因此我们需要保证:在左子树访问之前,当前节点不能提前出栈

我们应该从根节点开始,循环遍历左子树,不断将当前子树的根节点放入栈中,直到当前节点无左子树时,从栈中弹出该节点并进行处理。

然后再访问该元素的右子树,并进行上述循环遍历左子树的操作。这样可以保证最终遍历顺序为中序遍历顺序。

二叉树的中序遍历显式栈实现步骤如下:

  1. 判断二叉树是否为空,为空则直接返回。
  2. 初始化维护一个空栈。
  3. 当根节点或者栈不为空时:
    1. 如果当前节点不为空,则循环遍历左子树,并不断将当前子树的根节点入栈。
    2. 如果当前节点为空,说明当前节点无左子树,则弹出栈顶元素node,并访问该元素,然后尝试访问该节点的右子树。

迭代代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vector<int> inorder(TreeNode<int>* root) {
if(!root) return ;
vector<int> res;

stack<TreeNode<int>*> st;

while(root || st.size()) {
while(root) {
st.push(root);
root = root->left;
}

TreeNode<int>* node = st.top();
st.pop();
res.push_back(node->val);
root = node->right;
}

return res;
}

3. 后序遍历

  • 如果二叉树为空,则返回。
  • 如果二叉树不为空,则:
  1. 以后序遍历的方式遍历根节点的左子树。
  2. 以后序遍历的方式遍历根节点的右子树。
  3. 访问根节点。

img

3.1 后序遍历递归实现

二叉树的后序遍历递归实现步骤为:

  1. 判断二叉树是否为空,为空则直接返回。
  2. 先递归遍历左子树。
  3. 然后递归遍历右子树。
  4. 最后访问根节点。

二叉树的后序遍历递归实现代码如下:

1
2
3
4
5
6
7
8
vector<int> res;
void postorder(TreeNode<int>* root) {
if(!root) return;

if(root->left) postorder(root->left);
if(root->right) postorder(root->right);
res.push_back(root->val);
}

3.2 后序遍历显式栈实现

不同于前两个遍历, 后续遍历需要保证: 再访问完根节点的左右两颗子树前, 不能访问根节点.

所以我们需要先遍历到最左边的节点, 然后访问判断其右子树是否存在或者已经访问过, 如果不存在或者访问过, 那么就访问根节点,否则访问右子树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
vector<int> postorder(TreeNode<int>* root) {
if(!root) return ;
vector<int> res;
stack<TreeNode<int>*> st;
TreeNode<int>* prev;

while(root || st.size()) {
while(root) {
st.push(root);
root = root->left;
}

TreeNode<int>* node = st.top();
st.pop();

if(!node->right || prev == node->right) {
res.push_back(node->val);
prev = node;
root = nullptr;
}
else {
st.push(node);
root = node->right;
}
}
return res;

}

4.层序遍历

层序遍历顾名思义, 就是按照树的层数一层一层遍历,一般采用双向队列实现

层序遍历规则如下:

  1. 如果二叉树为空则返回
  2. 如果二叉树不为空:
    1. 访问第一层的节点
    2. 访问第二层的节点
    3. 访问最后一层的节点

img

4.1 层序遍历的队列实现

按照上面的规则我们很容易实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
vector<vector<int>> levelorder(TreeNode<int>* root) {
vector<vector<int>> res;
if(!root) return res;

queue<TreeNode<int>*> q;
q.push(root);

while(q.size()){
int len = q.size(); // 队列保存当前层的节点, 所以len是当前层节点的数量
vector<int> tmp; // 存放当前层节点的值
for(int i = 0; i < len; i++) { // 只处理当前层的节点
TreeNode<int>* node = q.front(); // 取出队头元素处理
q.pop(); // 删除队头元素
tmp.push_back(node->val); // 访问队头元素

if(node->left) q.push(node->left); // 如果左节点不为空, 把左节点入队
if(node->right) q.push(node->right);
}
res.push_back(tmp);
}


return res;
}

参考资料

文件描述符在Unix系统中是一个整数,用来标识打开的文件或者I/O资源,每个进程默认会打开三个标准文件描述符(File Descriptors, FDs),用于处理输入和输出。下面介绍这三个FD

  1. 标准输入(Standard Input)
    • 文件描述符编号0
    • 作用:用于进程读取输入数据(默认来自键盘输入)
    • 相关符号:在Shell中通常用 <<< 进行重定向(如 command < input.txt
  2. 标准输出(Standard Output)
    • 文件描述符编号1
    • 作用:用于进程输出正常结果(默认显示到终端)
    • 相关符号:在Shell中通常用 >>> 进行重定向(如 command > output.txt
  3. 标准错误(Standard Error)
    • 文件描述符编号2
    • 作用:用于输出错误或诊断信息(默认显示到终端)
    • 相关符号:在Shell中用 2>2>> 重定向错误(如 command 2> error.txt

那我们在看以下代码便会清晰的多:

echo log > /dev/null 2>&1

这条命令由以下部分组成:

  • **echo log**:向标准输出(stdout)打印字符串 log
  • **> /dev/null**:将标准输出(文件描述符 1)重定向到 /dev/null
  • **2>&1**:将标准错误(文件描述符 2)重定向到当前标准输出(即 /dev/null

关键概念

(1) /dev/null 的作用

  • /dev/null 是一个特殊的设备文件,写入它的内容会被直接丢弃
  • 常用于屏蔽命令的输出或错误信息

(2) 文件描述符重定向

  • > 默认重定向标准输出(1> 的简写)
  • 2> 表示重定向标准错误(文件描述符 2
  • &1 表示引用当前标准输出的目标(即 /dev/null

3. 执行流程

  1. echo log 输出内容
    echo 命令默认将 log 写入标准输出(文件描述符 1)。
  2. > /dev/null 重定向标准输出
    将标准输出(1)从默认的终端屏幕重定向到 /dev/null,导致 log 被丢弃,不会显示在终端。
  3. 2>&1 重定向标准错误
    • 2> 表示重定向标准错误(文件描述符 2)。
    • &1 表示将标准错误指向当前标准输出的目标(即 /dev/null)。
    • 因此,标准错误(如有)也会被丢弃。

使用strace工具追踪main.c的gcc编译过程及a.out的执行过程

追踪gcc编译过程

1
strace -o /dev/stdout gcc hello.c 2>&1 | vim -

将strace追踪信息利用|(管道) 传递给vim, 这边要注意的是strace的输出是直接发送到标准错误输出stderr, 而不是标准输出stdout, 所以用-o dev/stdout将输出写到标准输出中, 2>&1是将标准错误stderr(文件描述符2)重定向到标准输出stdout(文件描述符1)中, 详情见此处.

那么我们可以得到这些信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
execve("/usr/bin/gcc", ["gcc", "hello.c"], 0x7ffde6f6a6e8 /* 57 vars */) = 0
brk(NULL) = 0x198a000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1a6461f000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=94102, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 94102, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1a64608000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20t\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1922136, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1970000, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1a64427000
mmap(0x7f1a6444d000, 1396736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7f1a6444d000
mmap(0x7f1a645a2000, 339968, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17b000) = 0x7f1a645a2000
mmap(0x7f1a645f5000, 24571段:程序启动和初始化(行1-15
解释:这一段描述了gcc编译器启动的初始步骤。execve系统调用用于执行gcc程序,后续的brk和mmap调用进行内存分配和管理,为程序的运行准备必要的资源。接着,通过openat和read系统调用,程序尝试访问和读取系统的动态链接器配置(/etc/ld.so.cache)和标准C库(libc.so.6),这是程序运行前的必要准备,以确保能够正确链接到所需的共享库。
2段:环境设置(行16-27
解释:程序继续进行环境设置,包括通过mmap为特定的数据结构分配更多内存,设置线程特定的数据(如arch_prctl调用设定线程的FS寄存器),并进行一些内存保护设置(mprotect调用)。这一部分的操作主要关注于为程序的执行和系统调用提供必要的环境配置。
3段:本地化和错误消息处理(行28-49
解释:gcc尝试加载本地化支持文件,以便能够显示本地化的错误消息。通过一系列的openat调用搜索本地化文件,所有尝试均返回ENOENT(文件不存在),说明在指定的路径下没有找到相应的本地化文件。此外,ioctl调用的失败表明尝试获取终端属性未成功,这通常是因为gcc可能在没有终端的环境中运行(例如,脚本或其他自动化工具中)。
4段:编译前的准备(行50-95
解释:这段涉及到编译前的准备工作,包括对信号处理的设置(如rt_sigaction调用),以确保在编译过程中能够正确处理诸如中断(SIGINT)和挂起(SIGHUP)等信号。接着,gcc进行一系列的路径和文件检查,确认编译器组件和工具链的位置(如查找gcc,cc1,以及相关库文件的位置)。这些步骤确保了编译过程可以找到所有必要的编译器组件和工具。
5段:编译过程(行96-204
解释:这是实际编译过程的核心部分,涉及多个阶段,包括预处理、编译、汇编和链接。在这一段中,gcc生成了临时文件(如汇编代码文件),调用子进程(通过vfork和execve)来执行如cc1(gcc的编译器前端),汇编器(as),以及链接器(ld或collect2)。此外,通过pipe2和wait4等系统调用进行进程间通信和同步,确保编译过程按顺序进行。各种临时文件被创建和删除(unlink),是编译过程中间步骤的产物。
6段:清理和退出(行205-210
解释:在编译任务完成后,gcc进行清理工作,包括删除临时文件,并最终通过exit_group系统调用退出程序。这表示编译过程已经全部完成,所有资源被适当地释放,gcc以返回状态码0正常退出,标志着编译成功。, 0) = 0x7f1a645fb000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1a64424000
arch_prctl(ARCH_SET_FS, 0x7f1a64424740) = 0
set_tid_address(0x7f1a64424a10) = 16201
set_robust_list(0x7f1a64424a20, 24) = 0
rseq(0x7f1a64425060, 0x20, 0, 0x53053053) = 0
mprotect(0x7f1a645f5000, 16384, PROT_READ) = 0
mprotect(0x539000, 12288, PROT_READ) = 0
mprotect(0x7f1a64651000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f1a64608000, 94102) = 0
getrandom("\xe0\xcb\x2e\x04\x26\xb5\x6a\x8e", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x198a000
brk(0x19ab000) = 0x19ab000
brk(0x19cd000) = 0x19cd000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=6209168, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 6209168, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1a63e00000
close(3) = 0
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2996, ...}, AT_EMPTY_PATH) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2996
read(3, "", 4096) = 0
close(3) = 0
openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US.utf8/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.utf8/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/gcc-12.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
ioctl(2, TCGETS, 0x7ffea2eb7580) = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(2, TCGETS, 0x7ffea2eb7590) = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(2, TCGETS, 0x7ffea2eb7580) = -1 ENOTTY (Inappropriate ioctl for device)
rt_sigaction(SIGINT, {sa_handler=SIG_IGN, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGINT, {sa_handler=0x4079b0, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_IGN, sa_mask=[INT], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=SIG_IGN, sa_mask=[HUP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGHUP, {sa_handler=0x4079b0, sa_mask=[HUP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_IGN, sa_mask=[HUP], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=SIG_IGN, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGTERM, {sa_handler=0x4079b0, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_IGN, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGPIPE, {sa_handler=0x4079b0, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, 8) = 0
rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1a64463050}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_STACK, {rlim_cur=65536*1024, rlim_max=RLIM64_INFINITY}, NULL) = 0
access("/usr/local/bin/gcc", X_OK) = -1 ENOENT (No such file or directory)
access("/usr/bin/gcc", X_OK) = 0
newfstatat(AT_FDCWD, "/usr/bin/gcc", {st_mode=S_IFREG|0755, st_size=1301496, ...}, 0) = 0
readlink("/usr", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
readlink("/usr/bin", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
readlink("/usr/bin/gcc", "gcc-12", 1023) = 6
readlink("/usr/bin/gcc-12", "x86_64-linux-gnu-gcc-12", 1023) = 23
readlink("/usr/bin/x86_64-linux-gnu-gcc-12", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
access("/usr/local/bin/gcc", X_OK) = -1 ENOENT (No such file or directory)
access("/usr/bin/gcc", X_OK) = 0
newfstatat(AT_FDCWD, "/usr/bin/gcc", {st_mode=S_IFREG|0755, st_size=1301496, ...}, 0) = 0
readlink("/usr", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
readlink("/usr/bin", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
readlink("/usr/bin/gcc", "gcc-12", 1023) = 6
readlink("/usr/bin/gcc-12", "x86_64-linux-gnu-gcc-12", 1023) = 23
readlink("/usr/bin/x86_64-linux-gnu-gcc-12", 0x7ffea2eb5f60, 1023) = -1 EINVAL (Invalid argument)
access("/usr/lib/gcc/x86_64-linux-gnu/12/", X_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/", X_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/specs", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/specs", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/specs", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/specs", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/", X_OK) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper", {st_mode=S_IFREG|0755, st_size=1180024, ...}, 0) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper", X_OK) = 0
access("/tmp", R_OK|W_OK|X_OK) = 0
newfstatat(AT_FDCWD, "/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}, 0) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=2059, tv_nsec=553951022}) = 0
openat(AT_FDCWD, "/tmp/ccbdUzto.s", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
close(3) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/cc1", {st_mode=S_IFREG|0755, st_size=33342568, ...}, 0) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/cc1", X_OK) = 0
pipe2([3, 4], O_CLOEXEC) = 0
vfork() = 16202
close(4) = 0
read(3, "", 16) = 0
close(3) = 0
wait4(16202, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 16202
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16202, si_uid=1000, si_status=0, si_utime=1 /* 0.01 s */, si_stime=1 /* 0.01 s */} ---
clock_gettime(CLOCK_MONOTONIC, {tv_sec=2059, tv_nsec=601463828}) = 0
openat(AT_FDCWD, "/tmp/ccvZGP4P.o", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
close(3) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/12/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/12/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu-as", 0x7ffea2eb7230, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/as", 0x7ffea2eb72f0, 0) = -1 ENOENT (No such file or directory)
pipe2([3, 4], O_CLOEXEC) = 0
vfork() = 16203
close(4) = 0
read(3, "", 16) = 0
close(3) = 0
wait4(16203, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 16203
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16203, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/collect2", {st_mode=S_IFREG|0755, st_size=639192, ...}, 0) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/collect2", X_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so", R_OK) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/12/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/x86_64-linux-gnu/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/bin/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/../lib/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/12/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/lib/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/lib/../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/.", 0x7ffea2eb7420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
clock_gettime(CLOCK_MONOTONIC, {tv_sec=2059, tv_nsec=611785599}) = 0
openat(AT_FDCWD, "/tmp/ccmo4bus.res", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
close(3) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/Scrt1.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/Scrt1.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/Scrt1.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/../lib/Scrt1.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/12/Scrt1.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o", R_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/crti.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/crti.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/crti.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/../lib/crti.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/12/crti.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o", R_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o", R_OK) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/../lib/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/12/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/lib/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/lib/../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/12/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/.", {st_mode=S_IFDIR|0755, st_size=126976, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/../lib/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/.", 0x7ffea2eb6640, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/../../../.", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o", R_OK) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/crtn.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/12/crtn.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/x86_64-linux-gnu/crtn.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/lib/../lib/crtn.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/12/crtn.o", R_OK) = -1 ENOENT (No such file or directory)
access("/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o", R_OK) = 0
newfstatat(AT_FDCWD, "/usr/lib/gcc/x86_64-linux-gnu/12/collect2", {st_mode=S_IFREG|0755, st_size=639192, ...}, 0) = 0
access("/usr/lib/gcc/x86_64-linux-gnu/12/collect2", X_OK) = 0
pipe2([3, 4], O_CLOEXEC) = 0
vfork() = 16204
close(4) = 0
read(3, "", 16) = 0
close(3) = 0
wait4(16204, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 16204
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=16204, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
newfstatat(AT_FDCWD, "/tmp/ccmo4bus.res", {st_mode=S_IFREG|0600, st_size=0, ...}, 0) = 0
unlink("/tmp/ccmo4bus.res") = 0
newfstatat(AT_FDCWD, "/tmp/ccvZGP4P.o", {st_mode=S_IFREG|0600, st_size=1384, ...}, 0) = 0
unlink("/tmp/ccvZGP4P.o") = 0
newfstatat(AT_FDCWD, "/tmp/ccbdUzto.s", {st_mode=S_IFREG|0600, st_size=541, ...}, 0) = 0
unlink("/tmp/ccbdUzto.s") = 0
exit_group(0) = ?
+++ exited with 0 +++

信息非常乱, 但是我先列出每个函数的作用(生成自gpt):

系统调用解释

以下是对提供的strace输出中每行开头的系统调用或相关函数的解释:

  • execve: 执行一个程序,是程序运行的起点。替换当前进程的映像、数据和堆栈等为新程序的映像和数据,新程序从main开始执行。

  • brk: 调整数据段末尾的地址,用于增加或减少数据段的大小,即动态分配或释放内存。

  • mmap: 创建一个新的映射(mapping)在调用进程的虚拟地址空间,用于分配内存或将文件直接映射到内存中。

  • access: 检查调用进程是否可以访问指定的文件路径,用于检查文件是否存在及其读写权限。

  • openat: 打开一个文件或目录,AT_FDCWD表示相对于当前工作目录的路径。

  • newfstatat: 获取文件的状态信息,如大小、权限、最后修改时间等。

  • close: 关闭一个文件描述符,释放其所占用的资源。

  • read: 从文件描述符指向的文件中读取数据。

  • pread64: 从文件描述符指向的文件中读取数据,可以指定从哪个位置开始读取,不改变文件的当前偏移量。

  • arch_prctl: 设置特定架构的进程或线程的状态,如线程局部存储区域。

  • set_tid_address: 设置线程ID的存储地址,用于内核在特定事件(如线程退出)时更新信息。

  • set_robust_list: 设置线程的鲁棒列表(robust list),用于处理锁的死亡解锁。

  • rseq: 重启序列系统调用,用于在用户空间实现快速的锁和同步机制。

  • mprotect: 改变一块内存区域的保护属性,如设置为只读。

  • ioctl: 对设备特定的输入/输出操作进行控制。

  • rt_sigaction: 检查或修改与特定信号相关联的处理动作。

  • prlimit64: 获取或设置进程资源限制,如最大可用内存、最大打开文件数等。

  • getrandom: 从内核的随机数生成器获取随机数。

  • unlink: 删除一个文件的链接,如果是文件的最后一个链接,并且没有进程打开该文件,则文件被删除。

  • exit_group: 终止调用进程所在的整个进程组,通常在程序结束时调用。

下面对追踪信息进行基本解释:

GCC编译器执行过程分析

第1段:程序启动和初始化(行1-15)

这一段描述了gcc编译器启动的初始步骤。execve系统调用用于执行gcc程序,后续的brkmmap调用进行内存分配和管理,为程序的运行准备必要的资源。接着,通过openatread系统调用,程序尝试访问和读取系统的动态链接器配置(/etc/ld.so.cache)和标准C库(libc.so.6),这是程序运行前的必要准备,以确保能够正确链接到所需的共享库。

第2段:环境设置(行16-27)

程序继续进行环境设置,包括通过mmap为特定的数据结构分配更多内存,设置线程特定的数据(如arch_prctl调用设定线程的FS寄存器),并进行一些内存保护设置(mprotect调用)。这一部分的操作主要关注于为程序的执行和系统调用提供必要的环境配置。

第3段:本地化和错误消息处理(行28-49)

gcc尝试加载本地化支持文件,以便能够显示本地化的错误消息。通过一系列的openat调用搜索本地化文件,所有尝试均返回ENOENT(文件不存在),说明在指定的路径下没有找到相应的本地化文件。我没有下载本地化文件,所以这一步会加载失败,并且如果GCC未找到对应的.mo文件,它会回退到默认的(通常是英文)消息。此外,ioctl调用的失败表明尝试获取终端属性未成功,这通常是因为gcc可能在没有终端的环境中运行(例如,脚本或其他自动化工具中)。

本地化

本地化(Localization),通常简写为l10n(因为单词中”L”和”n”之间有10个字母),指的是将产品(如软件、文档、网站等)或内容调整以适应特定地区或语言的用户的过程。这不仅涉及到翻译文本内容,还包括符合当地文化、法律和需求的各种适应性调整。本地化的目标是为用户提供尽可能亲切和自然的使用体验,使产品看起来像是为特定的用户群体量身定做的。以下是本地化过程中常涉及的一些关键方面:

  1. 语言和文本翻译

将产品界面、帮助文档、用户手册等文本从一种语言翻译到另一种语言,确保语言的准确性和地道性。

  1. 文化适应性

考虑和适应目标地区的文化习俗和预期,如颜色、图标的文化含义,以及布局方式等,以避免文化冲突或误解。

  1. 法律和规范遵循

确保产品符合目标地区的法律法规和业务习惯,包括隐私政策、版权信息、数据保护规则等。

  1. 技术适应性

适应目标地区的技术标准和习惯,如日期和时间格式、数字格式(小数点和千位分隔符)、度量单位(公制或英制)、货币单位等。

  1. 排版和界面调整

根据语言的阅读方向(如从左到右或从右到左)调整用户界面的布局和排版,确保界面元素的适当展示和用户交互的便利性。
通过本地化,可以大大增强用户体验,提高产品的市场接受度和用户满意度。对于全球市场的软件开发者而言,本地化是一个重要的步骤,有助于将产品推广到不同语言和文化背景的用户中。

第4段:编译前的准备(行50-95)

这段涉及到编译前的准备工作,包括对信号处理的设置(如rt_sigaction调用),以确保在编译过程中能够正确处理诸如中断(SIGINT)和挂起(SIGHUP)等信号。接着,gcc进行一系列的路径和文件检查,确认编译器组件和工具链的位置(如查找gcc,cc1,以及相关库文件的位置)。这些步骤确保了编译过程可以找到所有必要的编译器组件和工具。

第5段:编译过程(行96-204)

这是实际编译过程的核心部分,涉及多个阶段,包括预处理、编译、汇编和链接。在这一段中,gcc生成了临时文件(如汇编代码文件),调用子进程(通过vforkexecve)来执行如cc1(gcc的编译器前端),汇编器(as),以及链接器(ld或collect2)。此外,通过pipe2wait4等系统调用进行进程间通信和同步,确保编译过程按顺序进行。各种临时文件被创建和删除(unlink),是编译过程中间步骤的产物。

第6段:清理和退出(行205-210)

在编译任务完成后,gcc进行清理工作,包括删除临时文件,并最终通过exit_group系统调用退出程序。这表示编译过程已经全部完成,所有资源被适当地释放,gcc以返回状态码0正常退出,标志着编译成功。

一些gcc常用的选项

  • -c: 只编译但不链接, 生成目标文件(.o), 无法识别的文件将被忽略; 原文:

Compile or assemble the source files, but do not link. The linking stage simply is not done. The ultimate output is in the form of an object file for each source file.

By default, the object file name for a source file is made by replacing the suffix ‘.c’, ‘.i’, ‘.s’, etc., with ‘.o’.

Unrecognized input files, not requiring compilation or assembly, are ignored.

  • -S : 将源代码编译成汇编代码,而不是直接生成机器代码或可执行文件。使用这个选项时,GCC会执行编译过程中的前几个步骤(如预处理、编译),但会停在生成汇编代码这一步,不会进一步进行汇编成目标代码或进行链接。

  • -E: -E选项用于仅执行预处理步骤。GCC会处理源代码中的预处理指令,如宏定义的扩展、条件编译指令的处理、包含文件的插入等,但它不会编译、汇编或链接代码。注意: 默认情况下,这个输出会直接发送到标准输出(即屏幕), 所以可以使用|传到vim或者其他编辑器中查看:

    1
    gcc -E helloworld.c | vim -  
  • -o: 这一部分强烈推荐看原文, -o选项允许指定主输出文件的名称和位置,这适用于任何类型的输出,无论是可执行文件、目标文件、汇编文件还是预处理的C代码。

    • 这一部分我直接贴出gpt对原文的解读:

      主输出定位:如果没有指定-o选项,默认情况下,可执行文件会被放置在a.out中,源文件source.suffix的目标文件会命名为source.o,其汇编文件命名为source.s,预编译头文件命名为source.suffix.gch,所有预处理的C源代码输出到标准输出(屏幕)。

      辅助输出和转储输出:虽然-o仅指定主输出,但它也影响辅助输出和转储输出的命名和定位。除非有其他指定,这些输出文件默认被放置在主输出文件相同的目录下。对于辅助输出,输入文件的后缀会被替换为辅助输出文件类型的后缀;对于转储输出,转储文件的后缀会被添加到输入文件后缀之后。在编译命令中,辅助输出和转储输出的基础名称是主输出的名称;在编译并链接命令中,主输出名称(去掉可执行文件后缀)与输入文件名称结合使用。如果它们的基础名称相同,结果就是该基础名称,否则,它们通过短横线连接。

      示例

      gcc -c foo.c ...会产生foo.o作为主输出,辅助输出和转储输出也会放置在它旁边,例如辅助文件foo.dwo(对于-gsplit-dwarf)和转储文件foo.c.???r.final(对于-fdump-rtl-final)。

      如果明确指定了非链接器的输出文件,辅助和转储文件默认使用相同的基础名称。例如,gcc -c foo.c -o dir/foobar.o ...将辅助输出命名为dir/foobar.*,转储输出命名为dir/foobar.c.*

      链接输出会为辅助和转储输出添加前缀。例如,gcc foo.c bar.c -o dir/foobar ...通常会将辅助输出命名为dir/foobar-foo.*和dir/foobar-bar.*,转储输出命名为dir/foobar-foo.c.*dir/foobar-bar.c.*

      唯一的例外是当可执行文件与单一输入文件共享基础名称时,例如gcc foo.c -o dir/foo ...,这种情况下,辅助输出被命名为dir/foo.*,转储输出被命名为dir/foo.c.*

      调整辅助和转储输出的位置和名称:可以通过-dumpbase-dumpbase-ext-dumpdir-save-temps=cwd-save-temps=obj选项调整辅助和转储输出的位置和名称。

      简而言之,-o选项不仅允许你指定主输出文件的名称和位置,它还间接影响了辅助输出和转储输出的命名和定位规则。这在管理大型项目和自定义构建过程时非常有用。

  • -g: 在可执行文件中生成标准调试信息。-g选项生成的调试信息会尽量适配不同的调试器,但主要还是以GDB(GNU Debugger)为主。

  • -ggdb :为GDB调试器优化,生成最丰富的调试信息,包括一些特定于GDB的优化,以提供更好的调试体验。

  • -Wall: 开启大多数编译器警告,有助于发现潜在的错误。

  • Werror: 将所有的警告当作错误处理。

  • -D: 定义宏,即在编译时从命令行向源代码中引入宏定义。 基本语法如下: -Dname或者-Dname=definition, 其中,name是要定义的宏的名称,definition是这个宏的值。如果没有指定definition,宏name的值默认为1。

    • eg:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      #include <stdio.h>

      int main() {
      #ifdef DEBUG
      printf("Debug mode is enabled.\n");
      #else
      printf("Debug mode is not enabled.\n");
      #endif
      return 0;
      }

    使用-D选项来定义DEBUG宏:

    1
    gcc -DDEBUG example.c -o example

    这将使得编译的程序在运行时输出:“Debug mode is enabled.” 因为DEBUG宏在编译时被定义了,所以预处理器会包含#ifdef DEBUG和#endif之间的代码。

0%