makefile 基础教程

对于一个简单的多文件程序:

main.c

#include <stdio.h>

void fun();

int main()
{
	fun();
}

str.c

#include <stdio.h>

void fun()
{
	printf("hello worldn");
}

如上,程序被分别放置在多个文件的情况,此时我们想要编译出这这个程序的代码为:

gcc main.c str.c -o main

执行之后gcc编译器会分别编译main.c和str.c两个源文件,并生成其相应的对象文件后,链接成新的可执行文件main(也就是“-o”参数所指定的名称)
可以想象在面对多文件编程的时候直接使用gcc命令编译,未来将会出现的情况:

1. 编译命令冗长
如果一个程序的源文件有数十个,那么编译命令便会在gcc之后跟上数十个文件的文件名,还包括一些调试选项参数或者某些函数要引用某些特定特定的库,可能使得直接使用gcc编译变成一件繁琐的过程

2. 编译时间变长
对于上述的例子程序而言,没次通过 gcc main.c str.c -o main 命令来编译链接的时候,我们的gcc编译器都会重新生成main.c和str.c的对象文件。这也意味着,即使我只是修改了str.c中的一行代码,包括main.c在内这两个文件都会被重新编译。在编程初期这不是什么大问题,可是当程序变大,文件变多之后这个问题就变得很严重了。因为如果一个程序的源文件有100个的话,仅仅修改了其中一个文件的一行代码,通过上述的方式,也会将所有100个文件重新编译。如此会造成大量的时间浪费。

我们也可以使用shell脚本来解决输入问题,不过即使这样也需要重新编译所有文件,程序较大源文件较多的时候同样会造成效率低下。

解决方案:
make 程序。通过读入当前目录下的 makefile 来配置自动编译。通过使用make可以很好的解决上面的两个问题。

main : main.o str.o		# 完成 main 依赖 main.o str.o
	gcc main.o str.o -o main	# 完成 main 的具体命令
main.o : main.c			# 完成 main.o 依赖 main.c
	gcc main.c -c		# 完成 main.o 的具体命令
str.o : str.c			# 完成 str.o 依赖 str.c
	gcc str.c -c		# 完成 str.o 的具体命令

这是一个非常简单的makefil1e,注意每一句的具体命令都要向后缩进一行(使用Tab键缩进,不用会报错遗漏分隔符),在编写好之后,在当前目录下执行make命令可以看到终端上显示的信息:

gcc main.c -c
gcc str.c -c
gcc main.o str.o -o main

可以想象make程序的解释器是从第一行开始解析makefile文件的内容,然后看到 main 便将其作为最后生成的目标,然后是“:”冒号。
冒号这里意思可以很简单的理解为依赖,即实现冒号左边的目标要依赖冒号右边的各项。在第二行则是生成最后目标可执行文件 main 的gcc代码。
因为生成main所依赖的main.o还不存在所以,程序先找到生成 main.o 的部分去执行,然后生成 main.o 之后发现 str.o 还没生成,于是程序又会去找 生成 str.o 的部分去执行。最后依赖项都已经充足才回链接生成 main 。
这也是为什么执行 make 看到的命令会是

gcc main.c -c			 # 编译main.c 生成 main.o
gcc str.c -c			 # 编译str.c 生成 str.o	
gcc main.o str.o -o main # 链接main.o str.o 生成 main

makefile 的选项

事实上makefile除了直接通过make来执行,还可以跟上参数来调用

main : main.o str.o
	gcc main.o str.o -o main
main.o : main.c
	gcc main.c -c
str.o : str.c
	gcc str.c -c

clear :
	rm *.o -rf

在编写完上述makefile之后,执行 make 命令与我们开始写的没有什么区别,但是我们可以通过给 make 命令一个参数来调用新加的clear 项。

make clear

执行后会发现它弹出一条命令,内容就是我们写在 makefile 中的 rm *.o -rf
通过这样的方式可以很简单的实现“.o”文件的清理。事实上这个就是简单的 makefile 中各项调用的方式。
值得一提的是,这里博主把 makefile 中的每个生成目标都叫做“选项”,这时博主自定义的称呼(如果有人知道更准确的称呼,还望评论联系博主)。
例如刚刚的 make clear 命令就调用了 clear 这个选项,而 clear 这个选项没有依赖项,也就可以视为已经满足执行条件便直接执行其后跟随的代码。按照这个理论我们也可以单独调用如 str.o 这个选项:

make str.o

执行之后会发现终端上显示:

gcc str.c -c			 # 编译str.c 生成 str.o	

是的,这样确实单独只调用了一个选项。

有些思维比较灵活的,可能已经理解我的意思了,紧紧只是把冒号右边的东西当成一个选项来看的话,这个makefile还可以改写成:

main : item1 item2
	gcc main.o str.o -o main
item1 : main.c
	gcc main.c -c
item2 : str.c
	gcc str.c -c

clear :
	rm *.o -rf

至此,我们可以对 makefile 有了更进一步的理解,我们知道了 makefile 中分可以分成两种东西:
1.选项以及它的依赖项
2.这个选项对应的shell命令

在 makefile 中一个选项的依赖项可以是另一个选项(这会导致另一个选项先执行),也可以是一个外部的文件名。然后,在使用 make 命令的时候可以通过 make 选项名 来单独调用某一个选项的代码来执行(当然,如果依赖项中有另一个选项的话,也会先执行另一个选项),除此之外默认就是执行第一个选项。

好的,如果你已经掌握了这些内容,那么恭喜你对于 makefile 执行的基本思路你已经知道的差不多了。

makefile 的变量

现在我们要跟深入的了解一些 makefile 的变量。依旧是通过一个问题来引出,且慢慢来看博主的叙述:
gcc作为一款成熟的编译器,其中自然包括的许多的编译选项,例如 -E 的只预处理不编译,又例如程序优化、调试等等,真的算起来gcc的编译选项实际上有上百种。虽然说中间的一些选项,可能我们一辈子都不会用到,但是不能否认这些编译选项的重要性。例如用于gdb调试的 -g 选项。
好吧,不说闲话,我们回到正题。在出现了编译选项的时候,我们刚才的 makefile 又要对此作出相应的更改了。

main : main.o str.o
	gcc main.o str.o -o main
main.o : main.c
	gcc -g main.c -c
str.o : str.c
	gcc -g str.c -c

那么新的问题出现了,我这里是两个文件编译,于是我加了两个 -g 可是如果我有几十个文件的话,那么为了调试,我是不是要加几十个 -g ?如果我加了几十个 -g 写了一阵子之后,我们调试结束了是不是又要将这几十个 -g 删掉?

感觉有点麻烦吧。这也是引出 makefile 变量的问题,我可以通过设置一个变量解决:

CC = gcc -g

main : main.o str.o
	${CC} main.o str.o -o main
main.o : main.c
	${CC} main.c -c
str.o : str.c
	${CC} str.c -c

变量名不用定义类型直接写出来,然后等号之后紧跟着就是变量的值。调用变量时通过 ${变量名} 这样来调用即可。如此碰到很多选项要编译也不用一直改编译选项的麻烦了。更多一点还可以这样:

CC = gcc -g
OBJ = main.o str.o
main : ${OBJ}
	${CC} ${OBJ} -o main
main.o : main.c
	${CC} main.c -c
str.o : str.c
	${CC} str.c -c

当然上述方案是可行的,只不过这样写还不够简单哦,到了这里,我们终于可以引出 makefile 中一直让不少新手瞠目结舌的地方了:

CC = gcc -g
OBJ = main.o str.o
main : ${OBJ}
	${CC} $^ -o $@
main.o : main.c
	${CC} $^ -c
str.o : str.c
	${CC} $^ -c

是不是突然觉得有点眼花?比如其中的 $^ 和 $@ 事实上这是一种自动化变量,make程序可以自动通过具体情况来生成这个变量:

$^ 依赖的所有文件 ( 观察一下 ^ 的形状是不是有点像包括所有文件感觉 )
$@ 依赖的目标文件 ( 联想一下 @ 的单词 at, 是不是一条微博写完了就该去 @ 目标了 ^_^ )

这个这样再反过来看下开始的 makefile 是不是感觉有点理解了?

如果感觉没什么问题的话,那么基本的 makefile 对你来说已经不是什么大问题了,还有兴趣了解更多的话,可以带着如下的问题去搜索一下 makefile 的更多功能:

CC = gcc -g
OBJ = main.o str.o
main : ${OBJ}
	${CC} $^ -o $@
.c.o:
	${CC} -c $< -o $@
Advertisements

putty 基础用法

今天一个同学问我putty怎么用,因为是在网上问的,顿时就有点噎住,不知道怎么说。所以特此写一个基础的用法,希望对各位有帮助。

关于putty的作用,简单点说就是用来远程登录并操控计算机的一个软件。
至于Telnet、SSH什么的,大家只要知道这些都是计算机上接受远程登录的服务就可以了。


以上就是putty的界面了,我们首先从右边开始看。最上面写着:
basic options for your putty session, 意思就是putty会话的基本选项。值得注意的是session这个单词还是比较常见的,一般是翻译成“会话”的意思。


接下来是通过主机名或者ip地址连接你的远程计算机。主机名称,即域名如果有相关知识的人应该知道域名是可以被DNS服务器解析成ip地址的,所以直接填写ip地址也是可以的。

下方的protocol是协议的意思,也即选择协议,如果是连接linux的话默认使用ssh就可以了,然后ssh的默认端口是22,所以port那里也是不用修改的。(关于这些协议的详细大家可以搜索一下看看)


如果是连接本地的虚拟机的话,可以通过ipconfig命令来获取虚拟机的ip,然后通过putty来连接。直接ip填好之后按下open就可以远程登录了(如果两台电脑可以连通的话)。操作起来跟直接用linux虚拟机差不多,某些地方还要比虚拟机的命令行方便(比如翻页还有复制)。

其他功能

字体设置:
左侧Category->window->Appearance->右边的font settings->change..
然后可以开始设置字体

编码设置:
左侧Category->window->Translation->右侧上方的下拉菜单,建议选择utf-8


putty登录之后可以通过但窗口上方右键然后选择change setting来调出putty 设置。


然后保存这次的会话和设置一样是在登上linux之后用上述方法调出putty设置,然后在左侧Category->session这里保存。。直接在text框上面写上你想要保存为的名称然后点save,下一次就可以直接在登录的时候双击名称就可以登录了。

Linux 查看进程和删除进程

ps 简介
ps 命令就是最根本相应情况下也是相当强大地进程查看命令.运用该命令可以确定有哪些进程正在运行和运行地状态、 进程 是否结束、进程有没有僵死、哪些进程占用了过多地资源等等总之大部分信息均为可以通过执行该命令得到地.

ps [选项]
-e 显示所有进程,环境变量
-f 全格式
-h 不显示标题
-l 长格式
-w 宽输出
a 显示终端上地所有进程,包括其他用户地进程
r 只显示正在运行地进程
x 显示没有控制终端地进程

查看当前正在运行的进程:

ps -ef

可以通过管道密令的 grep 搜索过滤

ps -ef | grep java

便可以找到进程信息中出现 java 这个字符串的进程信息

ps -aux | grep java

通过 -aux 可以显示信息的所有状态

kill 命令用于终止进程

# PID 为进程ID
kill -9 [PID]

-9 表示第九个信号,即 SIGKILL 杀死进程

关于更多的信号可以通过:

kill -l

来查看信号列表

 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6
59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX