0%

Makefile默认shell

Makefile 默认的 shell 是 /bin/sh:
查看 Makefile 默认的 shell:
$(warning ${SHELL}) # 查看默认 shell

如果在 Makefile 开头加上以下语句,可以指定 shell:
SHELL := /bin/bash

Makefile 的 shell 命令似乎每一条都会新开辟一个 shell 环境来执行,因为每条 shell 命令似乎都执行了一次 .cshrc 脚本。
要想在同一个 shell 环境中执行所有命令,则需要使用分号分割并转义换行符。

Makefile环境变量

你可以通过设置环境变量 MAKEFLAGS 来让所有的子shell都将 make 认作 make -n:
export MAKEFLAGS=”-n”
注:-n, –dry-run 表示只打印将要执行的命令,但是不真正执行它们。

Makefile 设置 PATH 环境变量:
PATH := mypath:$(PATH)
必须使用 “:=” ,因为 “:=” 是简单赋值,但是 “=” 会递归展开,会导致报错 “Recursive variable `PATH’ references itself (eventually)”。

Makefile 定义多行变量:使用 define 语句。
Makefile 接收命令行参数

非常奇特的现象:
$ make clean
cat clean.sh >clean
chmod a+x clean
因为 Makefile 文件中没有 clean 的 recipe,但是当前目录下有个 clean.sh 文件。
但是,当再次执行 make clean,clean 脚本还是不会被执行:
$ make clean
make: `clean’ is up to date.

特殊符号

空格

Makefile对空格的处理,似乎是:从第一个非空格开始,到明确的截止符(换行、逗号、括号、注释标记’#’等)为止。
a = b #注意末尾有3个空格
$(warning a=$(a)c)

结果:
a= b c

Makefile转义符:
字符 转义方法
$ $$
# #
\ \

注意:这里说的是Makefile中的转义符,不是bash中的转义符。

括号

引用变量时,Shell使用大括号,Makefile则大括号和小括号都行。但是在命令中使用Shell变量就需要使用大括号。

参考

Makefile调试方法

要调试 Makefile 并查看其执行过程,可以使用以下几种方法:
1. 使用 make 的 -n 或 –dry-run 选项:这将显示 Makefile 中的所有命令,而不会真正执行它们。
make -n
1. 使用 make 的 -d 或 –debug 选项:这将显示详细的调试信息,包括变量的展开和规则的匹配过程。
make -d
1. 使用 make 的 -p 或 –print-data-base 选项:这将打印所有的变量、规则和隐含规则。
make -p
1. 在 Makefile 中添加调试信息:你可以在 Makefile 中添加一些调试信息,例如使用 $(info …) 来打印变量的值。
print:
@$(foreach V, $(.VARIABLES), $(info $(V) = $($(V))))
1. 使用 make 的 -j 选项:如果你使用并行执行,可以使用 -j 选项来限制并行任务的数量,并更容易地跟踪输出。
make -j1

makefile打印所有变量的值:
debug:
@$(foreach V, $(.VARIABLES), $(info $(V) = $($(V))))
然后在命令行中运行:
make debug
这将打印所有变量及其值。

print-%:
@echo $* = $($*)

print-% 是一个 Makefile 目标,用于打印变量的值。具体步骤如下:
1. 定义一个目标 print-%,其中 % 是一个通配符,表示任意变量名。
2. 使用 @echo $* = $($*) 打印变量名和变量值。

示例代码:
print-%:
@echo $* = $($*)
使用方法:
在命令行中运行:
make print-VARIABLE_NAME
例如:
make print-XTENSA_SW_RELEASE
这将打印 XTENSA_SW_RELEASE 变量的值。

命令行参数

See here.

@: See here.

This works fine for me:

1
2
3
4
5
6
7
8
9
10
11
12
# If the first argument is "run"...
ifeq (run,$(firstword $(MAKECMDGOALS)))
# use the rest as arguments for "run"
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# ...and turn them into do-nothing targets
# TODO: What does the following line mean?
# $(eval $(RUN_ARGS):;@:)
endif

# "cmd" refers to any command
run:
cmd $(RUN_ARGS)

makefile同名目标处理方式

参考:

makefile将命令结果赋值给变量

Makefile中短划线

1
2
all:
-/bin/rm -rf *.log

其中,”-/bin/rm“的短划线”-“是一个特殊前缀,表示忽略命令执行过程的错误。

为每个源文件生成一个可执行程序

1
2
3
4
5
6
7
SRCS = $(wildcard *.c)

all: $(SRCS:.c=)

# Unnecessary, as the default rules are adequate.
.c:
gcc $(CPFLAGS) $< -o $@

最后两行其实不需要,默认规则已经足够了。

其中,$(SRCS:.c=.o)表示将变量SRCS中的每个单词(以空格分割)中的.c替换为.o。以上代码则是将所有.c都去掉。

Makefile保留中间文件

🛡️ 方法一:使用 .PRECIOUS 保留中间文件
这是最直接的方式,告诉 make 不要删除某些目标,即使构建失败:

1
.PRECIOUS: %.o %.d

你可以指定具体的文件名或模式(如 %.o 表示所有 .o 文件)。

🛡️ 方法二:使用 .SECONDARY 保留中间文件但不强制重建
如果你希望保留中间文件,但又不希望它们被视为最终目标,可以用:

1
.SECONDARY: intermediate.o

或者:

1
.SECONDARY:

这会保留所有中间文件。

🧠 方法三:避免使用“中间目标”语法
如果你写了类似:

1
intermediate: ;

这种空规则会让 make 把 intermediate 视为中间目标,构建失败时自动删除。避免这种写法,或者配合 .PRECIOUS 使用。

🛠️ 方法四:将中间文件输出到特定目录
你可以将中间文件集中到一个目录中,便于管理和保留:

1
2
3
4
OBJ_DIR := build/obj
$(OBJ_DIR)/%.o: %.cpp
$(CXX) -c $< -o $@
然后用 .PRECIOUS: $(OBJ_DIR)/%.o 保留它们。

📌 示例:保留 .o 和 .d 文件

1
2
3
4
5
6
7
.PRECIOUS: %.o %.d

main: main.o
$(CC) $^ -o $@

main.o: main.c
$(CC) -c $< -o $@

gcc可以使用 -save-temps 选项保留中间文件:

1
gcc -save-temps -c main.c

🧠 补充:-save-temps=obj

如果你只想把中间文件保存在目标文件所在目录,而不是当前目录,可以使用:

1
gcc -save-temps=obj main.c -o main

Color Scheme

Refer to the link.

Add these three lines to ~/.bashrc

1
2
3
4
5
$ vi ~/.bashrc
export LS_OPTIONS='--color=auto'
# dircolors - color set for ls
eval "$(dircolors -b)"
alias ls='ls $LS_OPTIONS'

1. 术语

resident set size (RSS)

2. 文件系统

/proc/self/statm

3. 相关命令

3.1. top

3.1.1. Fields

TIME+1 TIME
5432:01 means “5432 minutes and 1 second” 90,32 means “90 hours and 32 minutes”
25:15.20 means “25 minutes, 15 seconds and 20% of 1 second” 25:15 means “25 minutes and 15 seconds”

3.2. ps

3.2.1. 输出说明

TIME: the cumulated CPU time in [DD-]hh:mm:ss format (time=TIME)

Field value & means
TIME 1-18:09:38 means “1 day, 18 hours, 9 minutes and 38 seconds”

4. 相关系统调用

4.1. fork

子进程是父进程的副本,获取了父进程的数据空间、堆和栈的副本,但是他们共享正文段。

写时复制(Copy-On-Write,COW):不复制父进程的完全副本,只有在父或子进程尝试修改这些区域时,则为修改区域的那块内存制作一个副本,通常是虚拟内存的一页。

博主注: 如果父进程尝试修改共享的内存页,内核会为父进程同样制作副本,而把原本的内存页留给子进程使用,这会导致即使在父进程中,原本的内存页的物理地址也会发生改变,从而使得 RDMA 等机制发生错误(要求物理地址保持不变),所以此时需要使用 madise 设置 MADV_DONTFORK 标志。

在 Linux 中,写时复制(Copy-On-Write, COW)机制主要通过页表和内存管理单元(MMU)来实现。以下是 COW 在 Linux 中的具体实现步骤:

页表标记:当进程调用 fork() 创建子进程时,父进程和子进程的页表会标记共享的内存页为只读。这意味着这些页在初始状态下是共享的,且无法被写入。

页错误处理:如果父进程或子进程尝试写入这些只读页,会触发页错误(page fault)。操作系统内核会捕获这个页错误,并执行 COW 机制。

内存页复制:在页错误发生时,操作系统会分配一个新的物理内存页,并将原始页的内容复制到这个新页中。然后,页表会更新,以指向新的可写内存页。

页表更新:操作系统更新进程的页表,使得写入操作可以在新的内存页上进行,而不会影响其他进程共享的原始页。

引用计数:操作系统维护每个内存页的引用计数,以跟踪有多少进程共享该页。当引用计数减少到零时,内存页可以被释放。

4.2. mmap

TODO

4.3. madvise

madvise 是一个系统调用,用于向内核提供关于内存使用的建议。

1
2
3
#include <sys/mman.h>

int madvise(void *addr, size_t length, int advice);

4.3.1. 选项 advice:

4.3.1.1. MADV_DONTFORK

MADV_DONTFORK 阻止 fork() 后的子进程看见这些范围的内存页。

这意味该内存不会被复制,即在 fork() 调用时,指定的内存区域不会被子进程继承,避免写时复制导致的页物理地址发生变化。在 fork() 之后,如果父进程对共享内存页进行写操作,写时复制(COW)机制会将这些页复制到新的物理位置。这会导致 RDMA 操作使用的内存地址不一致,从而引发数据错误。

拓展: RDMA 中,ibv_fork_initRDMAV_HUGEPAGES_SAFE 会调用 madvise() 来为 IB 卡的 DMA 内存页设置 MADV_DONTFORK ,以避免数据损坏。

5. 参考

what-does-virtual-memory-size-in-top-mean
[2]: https://blog.csdn.net/weixin_42319496/article/details/125940896
[3]: https://docs.nvidia.com/networking/display/rdmaawareprogrammingv17/ibv_fork_init

See the article: Prefixes for binary multiples

Table:


Prefixes for binary multiples
 Factor  Name  Symbol  Origin Derivation 
 210 kibi Ki kilobinary: (210)1 kilo: (103)1
 220 mebi Mi megabinary: (210) mega: (103)2
 230 gibi Gi gigabinary: (210)3 giga: (103)3
 240 tebi Ti terabinary: (210)4 tera: (103)4
 250 pebi Pi petabinary: (210)5 peta: (103)5
 260 exbi Ei exabinary: (210)6 exa: (103)6

Examples and comparisons with SI prefixes
one kibibit  1 Kibit = 210 bit = 1024 bit
one kilobit  1 kbit = 103 bit = 1000 bit
one byte  1 B = 23 bit = 8 bit
one mebibyte  1 MiB = 220 B = 1 048 576 B
one megabyte  1 MB = 106 B = 1 000 000 B
one gibibyte  1 GiB = 230 B = 1 073 741 824 B
one gigabyte  1 GB = 109 B = 1 000 000 000 B

Variadic function in C

See more: Variadic functions in C

vprintf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdarg.h>
#include <stdio.h>

void myPrintf(const char* format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}

int main() {
long int time = 100;
myPrintf("Elapsed time is: %ld seconds.\n", time);

return 0;
}

Output:

1
2
$ ./a.out 
Elapsed time is: 100 seconds.

ap_list + vsnprintf

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
#include <stdarg.h>
#include <stdio.h>
#include <sys/time.h> // gettimeofday
#include <time.h>
#include <sys/socket.h>

using namespace std;

void foo(int id, const char *fmt, ...)
{
constexpr int MAXLEN = 1024;
char buf[MAXLEN];
int n;

n = snprintf(buf, MAXLEN, "INFO(%d): ", id);

va_list ap;
// ap will be the pointer to the last fixed argument of the variadic function.
va_start(ap, fmt);
n += vsnprintf(buf + n, MAXLEN - n, fmt, ap);
// This ends the traversal of the variadic function arguments.
va_end(ap);

struct timeval tv;
gettimeofday(&tv, NULL);
struct tm tm;
localtime_r(&tv.tv_sec, &tm);
char timebuf[64];
// size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
snprintf(timebuf, MAXLEN - n, "%d-%02d-%02d %d:%d:%d.%ld %s",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, tm.tm_zone);

printf(buf, timebuf);
}

int main()
{
const char* name = "Tony";
// "%%s" is a placeholder of the timestamp for the vsnprintf function.
foo(123, "Hello %s at %%s\n", name);
// Will print:
// INFO(123): Hello Tony at 2023-10-04 21:25:23.682853 CST

return 0;
}

va_list + va_arg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdarg.h>
#include <stdio.h>

constexpr bool debug = true;

int printDebugLog(const char* fmt, ...) {
if (debug) {
// The first argument doesn't need to traverse via va_list.
printf("fmt=%s\n", fmt);
va_list ap;
va_start(ap, fmt);
long int t = va_arg(ap, long int);
va_end(ap);
printf(fmt, t);
}
return 0;
}

int main() {
long int time = 100;
printDebugLog("Elapsed time is: %ld seconds.\n", time);

return 0;
}

Output:

1
2
3
4
$ ./a.out 
fmt=Elapsed time is: %ld seconds.

Elapsed time is: 100 seconds.

inline namespace

You can define and specialize members of an inline namespace as if they were also members of the enclosing namespace.
inline namespace 表示该命名空间也是上级命名空间的成员。
参考: Inline namespace definitions (C++11)

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace A {
#if USE_INLINE_B
inline
#endif
namespace B {
int foo(bool) { return 1; }
}
int foo(int) { return 2; }
}

int main(void) {
return A::foo(true); // 如果USE_INLINE_B为0,则该句出错:找不到定义。
}

Unnamed / anonymous namespaces vs static in namespace

Refer to: Unnamed / anonymous namespaces vs static in namespace

  1. at a drop of a hat: 毫不犹豫。见Cambridge dict.
  2. beyond your ken: 超过你的认知范畴
    ken: the range of perception, understanding or knowledge. 参考词典

Sheldon: Woman, you’re playing with forces beyond your ken.
Penny: Yeah, your ken can kiss my Barbie.

Penny想要气气Sheldon,她故意把ken曲解成玩偶,说:你的ken可以和我芭比娃娃谈恋爱。
出处: word reference

  1. a rule of thumb: 经验法则。见柯林斯词典
  2. vice versa: 反之亦然。近义词:conversely.
  3. on (a) par with: 相当,对等,可媲美。See more: on (a) par with
  4. IMHO: In my humble opinion 恕我直言。humble 谦逊的。
  5. AFAIK: As far as I know 据我所知。
  6. miss the forest for the trees. / not see the forest for the trees. 只见树木、不见森林。参见剑桥词典:to be unable to get a general understanding of a situation because you are too worried about the details 因太注重细节而无法从整体角度看待问题。
  7. irrespective of 不论
  8. sell sb out. 出卖某人。
  9. mood swings. 情绪波动。 swing v. 摇摆;swings n. 秋千。
  10. buy that… 相信…… e.g. I didn’t think the doctor was gonna buy that…
  11. Roger that. / Copy that. 参考

人物

Daisy Armstrong
Miss Mary Debenham
Colonel Arbuthnott
Hercule Poirot
Lieutenant Dubosc

台词

1930
The Armstrong Home, Long Island, N.Y.

Armstrong Baby Kidnapped.
Crushed toys, crushed hopes.

Baby taken in dead of night.
Armstrong Household Turmoil.
Baby Daisy Taken from Armstrong Home.
Staff Wakened But Hopeless.

DAISY ARMSTRONG - Child of Colonel and Mrs. Hamish Armstrong was dramatically kidnapped last night while her parents were being driven to the airport by their chauffeur.

The discover of the missing child was made by the chauffeur on returning from the airport where he found the family nurse bound and gagged in the room next to Baby Daisy’s bedroom.

The police were informed immediately and investigations began at once. They described kidnapping as one of the most vicious crimous that can be commited.

Daisy found slain.

5 years later
The Asian side of Istanbul.

Miss Mary Debenham

Salacak

Welcome aboard.

Bosporus Ferry will shortly depart from Istanbul Sirkeci Station, connecting with the Orient Express.

Here’s your ticket, Monsieur Poirot. I’m afraid you’ve still got another hour.

Well, then, please do not wait.

Not wait? After all you’ve done for us, Monsieur Poriot? My general’s orders were to ensure your safe departure. He also wished me to thank you again for saving the honor of the British garrison in Jordan. The brigadier’s confession was opportune. I say, how did you do it? Was it the old thumbscrew, you know, the rack?

Chop-chop

I hope we did the right thing booking you into a hotel on the European side rather than the Asia side.
我希望我们给你预订的是欧洲酒店而不是亚洲酒店是正确的。

I have no prejudice with either continent.

The crossing should be pleasant. The Borporus is always calm.
穿行应该会很愉快。博斯普鲁斯海峡总是很平静。

You have crossed by the ferry?
你乘渡船穿渡过?

词汇

Daisy: 雏菊;人名(黛西)。
kidnapped: 绑架
crushed: 破灭的,压碎的
dead of night: 深夜时分,the quietest, darkest part of the night.
household: 家庭;户;家;家族;家人
turmoil: n. 动荡;动乱
staff: 工作人员。
wakened: waken v. 醒来。wakened 是waken的过去式和过去分词。
chauffeur: 美/ʃoʊˈfɝː/ 英/ˈʃəʊ.fər/ 司机。
bound: 这里是bind的过去式和过去分词,绑定、捆绑。
gag: n./v. 堵嘴;插科打诨。
vicious: 恶毒的。
slay: To kill in a violent way. 残忍杀害。
Istanbul: 伊斯坦布尔,为土耳其首都直至1923年。
Salacak: garrison: A group of soliders living in or defending a town or building, or the building that the soliders live in. 守军;守备营地。
Jordan: 约旦(西亚国家)。
brigadier: 英/ˌbrɪɡəˈdɪə(r)/ 美/ˌbrɪɡəˈdɪr/ 准将。
confession: n. 坦白;忏悔;认罪。
opportune: 英/ˈɒpətjuːn/ 美/ˌɑːpərˈtuːn/ 适当的;及时的。
thumbscrew: 螺丝;拧紧 。
rack: chop-chop: chop v./n. 砍;向下猛击。chop-chop 赶快,快点。
Bosporus Strait: 博斯普鲁斯海峡,又称伊斯坦布尔海峡,介于欧洲与亚洲之间的海峡。它长约30公里,最宽处宽约3,700米,最窄处宽约700米。北连黑海,南通马尔马拉海(土耳其内海),土耳其第一大城伊斯坦堡即隔着博斯普鲁斯海峡与小亚细亚半岛相望,是黑海沿岸国家出海第一关口,也是连接黑海以及地中海的唯一航道。因此,俄罗斯的黑海舰队必须通过博斯普鲁斯海峡才能前往地中海。
ferry: n./v. 渡船;摆渡