现在的位置: 首页 > 综合 > 正文

初识Linux程序

2013年12月08日 ⁄ 综合 ⁄ 共 6221字 ⁄ 字号 评论关闭
Linux程序
Linux的程序由两部分组成:可执行文件和脚本.可执行文件是可以直接由我们的电脑运行的程序,他们对应着Windows下的.exe程序.脚本是另一种程序的指令集合,是要分步来解释的.他们对应着Windows下的.bat或是.cmd文件.
Linux下的可执行文件或是脚本并不需要一个特殊的文件名或是扩展.文件系统的属性可以用来指示这是一个可以运行的程序.在Linux下,我们可以用已编译的程序来替换脚本而不影响其他的程序或是调用他们的其他的用户.事实上,用用户级,在这两者之间并没有实质的区别.
当我们登陆进入Linux系统以后,我们与一个Shell程序(通常是Bash)进行交互,而这个Shell的运行方式类似于Windows下的命令行.他通过在一个指定的目录中进行查找来返回我们所指定的程序名.要查找的目录存放在一个名为PATH的Shell变量中,这与Windows相类似.这个查找目录(我们通常可以加入我们自己的目录)是由我们的系统管理员来进行配置的并且通常包含一引起标准的系统程序存放的目录.他们通常包含下面的目录:
/bin:二进制文件,系统启动时要用到的程序.
/usr/bin:用户二进制文件,可以为用户使用的标准程序.
/usr/local:本地二进制文件,要安装的特殊程序.
作为管理员用户登陆,如root,所使用的PATH变理会包含系统管理程序包含的地方,如/sbin和/usr/sbin.
可选的系统组件或是第三方的程序也行会安装在/opt子目录下,而安装程序会以用户安装脚本的方式加入PATH变量中.
在这里我们要注意的是有与Unix一样在Linux中使用分冒号(:)来分隔PATH变量中的项,这与Ms-dos中所使用的分号(;)不同.如下面的一个PATH变量的例子:
/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/usr/games
C编译器
在Linux系统上,当我们使用c89,cc或是gcc时都会引用系统的编译器,通常是GNU C编译器或是gcc.在Unix系统上,C编译器通常是cc.
下面让我们开始编写,编译和运行我们的第一个Linux程序.也许最好的就是最著名的HelloWorld.
1下面的是Hello.c的源代码:
#inclue <stdio.h>
int main()
{
    printf("Hello World/n");
    exit(0);
}
2编译链接运行程序:
$gcc -o Hello Hello.c
$ ./Hello
Hello World
$
他是如何进行工作的
我们调用GNU C编译器将我们的C源代码转换成可以运行的文件.我们运行这个程序他就会打印出一句问候.这是一个最简单的程序例子,但是如果我们可以走得更远一些,我们就可以编译运行其他的程序例子.如果这个命令没有起作用,我们要确定在我们的系统已经安装了C编译器.例如,在许多的Linux发行版本中有一个名为软件开发的安装选项,而这个是我们必须选上的.
因为这是我们运行的第一个程序,我们要指出一些内容.Hello程序通常会位于我们的用户主目录下.如果我们的Shell变量并不包含到我们用户主目录的引用,Shell就不会找到这个程序.另外,如查PATH变量中的一个目录也含有一个名为Hello的程序,那么就会运行这个命令.如果在PATH变量这个目录位于我们的用户主目录之前也会发生这样的情况.为了避免这样的问题,我们可以在我们的程序之前加上一个./的前缀.这个就会表明Shell要执行的程序位于我们的当前目录下.
如果我们没有通过-o name选项来告诉编译生成的可执行文件的名字,编译器就会将生成的可执行文件命名为a.out.(指汇编输出).如果我们记得我们已经编译了一个程序但是我们却找不到他,这时我们一定要记住要找一下a.out文件.在Unix的早期,在系统上玩游戏的人常常以a.out的形式来运行这些游戏程序以避免为系统管理员发现,因而一些Unix日常用管理程序就会每次都会将全部的a.out程序删除.
系统开发路线图
对于一个Linux开发者来说,了解一些开发工具和资源所在的位置是非常重要的.下面的部分让我们一起来看一下一些重要的目录和文件.
程序
程序会保存在他们的目录中.由系统为日常使用的,包括程序开的程序位于目录/usr/bin中.由系统管理员添加的为某一个主机或是本地网络所用的程序常常位于/usr/local/bin或是/opt中.
系统管理员们比较喜欢/usr/local目录,因为在这个目录中可以保存一些后来添加的程序,这样就实现在与系统提供的程序的分离.以这样的方式来保存/usr的组织是比较有用的,这样在更新系统的时候就只有/usr/local目录下的内容需要保留.因而我们比较推荐的方法就是我们自己编译运行的程序以及要访问的文件都放在/usr/local目录中.
其余的特征和程序系统有他们自己的目录结构和程序目录.其中主要的就是X Window系统,他通常会安装在/usr/X11目录下.
头文件
对于C语言或是其他语言的程序来说需要用头文件来提供常量的定义以及系统及库函数调用的定义.对于C来说这些头文件位于/usr/include目录及其子目录下.我们通常也可以在/usr/include/sys和/usr/include/linux目录里查找到依据于我们正在运行的Linux的典型头文件.其他的程序系统与有include文件存放在编译器可以自动找到的目录中.例如X Window系统的头文件在/usr/include/X11目录中而GNU C++的头文件位于/usr/include/g++目录中.
我们可以通过为C编译器指定-I标记指定子目录中或是不在标准位置的include文件.如:
$ gcc -I/usr/openwin/include fred.c
这样就会在使得编译器在编译fred.c文件时在标准路径查找的同时也在/usr/openwin/include目录中进行头文件的查找.我们也可以在C编译器的手册页中得到更为详细的内容.
我们可以使用grep命令来查找特定定义和函数原型的头文件.假如我们想要知道由#define定义的用来从程序返回的退出状态的名字.我们可以简单的进入/usr/include目录然后使用grep命令来进行相应的查找:
$ grep EXIT_*.h
在这里grep在当前的目录下查找所有的对于字符串EXIT_以.h结尾的所有文件.在这个例子中,这个命令会在文件stdlib.h文件中查找到我们需要的定义.
库文件
库文件是编写用来进行代码重用的预编译的函数的集合.通常,他们是由用来执行一个通用任务的相关功能的集合所组成.库文件的例子包含一些秘密处理的功能(如curses和ncruses库)和数据库访问所用的库(如dbm库).
标准的系统的库文件通常位于/lib和/usr/lib目录下.C编译器(或者更精确的说是链接器)需要被告知查找哪些库文件,在默认的情况下只是会查找标准的C库.这是由计算机速度较慢而CPU较为昂贵的时代遗留下来的.将库文件放在一个标准目录下而希望编译器可以找到是不够的.库文件需要跟随一个特定的命名约定并且要用命令行进行指定.
库文件的名字通常是以lib开始的.余下的部分表示这个库是什么(如c是指C库而m是指算术库).最后的部分是以.开始的用来表明这个库的类型:
.a    传统的静态库
.so    共享库
库文件通常会以静态和共享两种形式存在,我们可以运行ls /usr/lib来看一下运行的结果.我们可以通过为编译器指定全路径或是使用-l标记我们可以指定编译器查找的路径.如下面的例子:
$ gcc -o fred fred.c /usr/lib/libm.a
这个命令会告诉编译fred.c程序生成fred程序,用查找标准C库同时查找算术库来解决函数的引用问题.我们也可以用下面的命令来达到同样的结果:
$ gcc -o fred fred.c -lm
-lm(在l和m之间并没有空格)是位于库目录(/usr/lib)下的libm.a库的简写.-lm选项的另一个优点就是如果存在共享库编译器就会自动选择共享库.
尽管库文件可以以头文件的形式在标准位置找到,我们可以使用-L选项添加另外要查的目录.例如:
$ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11
这个命令会使用在/usr/openwin/lib目录下查找的libX11版本的库来编译和链接x11fred程序.
静态库:
最简单的库格式只是一些保持在一起的处于待用状态的目标文件的集合.当一个程序要使用存放在库中的函数时,他需要包含定义这一个函数的头文件.编译器和链接器可以小心的来处理从而将程序代码和库文件组合到一个单一的可执行程序中.我们必须使用-l选项来表明除了标准的C运行库来需要哪些库文件.
静态库,也会称之为文档,通常是以.a结束的文件名.例如标准的C库和X11库,/usr/lib/libc.a和/usr/X11/lib/libX11.a.
我们可以很容易的使用ar(archive)来创建和维护我们自己的静态库,这样我们就可以使用gcc -c来编译函数.我们应尽可能的将函数放在一个单独的源文件中.如果函数需要访问常规的数据,我们可以将他们放在同一个源文件中并在这个文件中使用静态变量进行声明.
下面我们来创建一个只包含两个函数的小的静态库然后在一个例子中使用他.这个函数叫做fred和bill,只是打印出问候.
1 首先,我们创建单独的源文件,名为fred.c和bill.c.下面的是第一个:
#include <stdio.h>
void fred(int arg)
{
    printf("fred:you passed %d/n",arg);
}
下面的是第二个:
#include <stdio.h>
void bill(char *arg)
{
    printf("bill:you passed %s/n",arg);
}
2 我们可以单独的编译这些函数来创建目标文件从而可以将其包含到一个库中.我们可以通过C编译器的-c选项来完成这样的工作,这样就可以阻止C编译器试着创建一个完整的程序.试着创建一个完整的程序将会得到失败的信息,因为我们并没有定义main函数.
$ gcc -c bill.c fred.c
$ ls *.o
bill.o fred.o
3 现在我们来写一个程序调用bill函数.首先我们最好为我们的库创建一个头文件.这将会在我们的库中声明函数并且会被所有要使用我们这个库的程序所包含.我们最好也要在fred.c和bill.c文件中包含这个头文件.这将有助于找到错误.
/*
 * This is lib.h. It declares the functions fred and bill for users
 */
void bill(char *);
void fred(int);
4调用程序(program.c)也是相当简单的.他包含这个库的头文件并且从这个库中调用其中的一个函数.
#include "lib.h"
int main()
{
    bill("Hello World");
    exit(0);
}
5 现在我们可以编译这个程序并进行测试.这一次我们要显示的向编译器指定目标文件,使编译器使用我们前面已编译的目标文件bill.o来进行编译和链接.
$ cgcc -c program.c
$ cgcc -o program program.o bill.o
$ ./program
bill: we passed Hello World
$
6 现在我们来创建并使用库.我们使用ar程序来创建文档并向其中添加我们的目标文件.这个程序所以名为ar是因为他可以创建归档或是集合,将单个的文件放在一个大的文件中.这里我们要注意的就是我们可以使用ar程序来任何类型的文件归档.
$ ar crv libfoo.a bill.o fred.o
a - bill.o
a - fred.o
7 这样我们就创建了这个库并在其中添加了两个目标文件.要成功的使用这个库,一些系统,特别是由Berkeley Unix发展来的系统,需要为这个库创建一个内容表.我们这里可以使用ranlib命令.在Linux中,当我们使用GNU的软件开发工具时,这一步并不是必需的(但是却没有坏处).
$ ranlib libfoo.a
现在我们就准备好并可以使用了.我们可以像下面这样来添加要为编译器使用来创建我们程序的文件列表:
$ gcc -o program program.o libfoo.a
$ ./program
bill: you passed Hello World
$
我们也可以使用-l选项来访问我们的库,但是因为他并不是任何一个标准的位置,我们需要使用-L选项来告诉编译器他所在的位置:
$ gcc -o program program.o -L. -lfoo
在这里-L选项告诉编译器在当前的目录下查找库.-lfoo选项告诉编译器使用名为libfoo.a的库.
要查看在一个目标文件,库或是可执行程序中含有哪些函数,我们可以使用nm命令.如果我们查看一下program和lib.a,我们可以看到库包含fred和bill两个函数,而程序只包含一个.当这个程序创建时,他只会从库中包含一个他实际会用到的函数.包含了一个在库中声明了所有函数的头文件,但是在最终的程序中并不会包含整个库文件.
共享库
静态库有一个缺点:如果我们要同时运行许多程序而他们要使用同一个库中的函数,这时我们就需要在内存中需要同一个函数的多份拷贝以及在他们的程序中的多份拷贝.这样就会浪费许多宝贵的内存和磁盘空间.
许多Unix以及Linux系统支持共享库可以解决这个问题.共享库与静态库的位置相同,但是共享库有着不同的文件名后缀.在典型的Linux系统上,标准的数学库的共享版本为/usr/lib/libm.so.
当一个程序要使用共享库时,他以这样的方式来进行链接:他并不包含函数代码,但是却引用运行时可以访问的共享代码.当结果程序装入内存运行时,函数引用调用共享库,从而共享库会在必须时装入内存.
采用这样的方式,系统只需维护和在磁盘上保存一份可同时被许多程序使用的共享库.共享库的另一个好处就是可以独立于依赖他的程序进行更新.当需要时使用的是由/usr/lib/libm.so到/usr/lib/libm.soN的系统链接.当Linux启动一个程序时,他会决定程序所需的共享库的版本,从而防止新版本的共享库破坏旧版本的程序.
对于Linux系统,程序会小心处理装入共享库,而决定客户端程序函数引用的被之为ld.so.为共享库而要查找的另外的位置是由文件/etc/ld.so.conf来配置的,这在有改变发生时需要由ldconfig来生成.
我们可以通过实用程序ldd来查看一个程序所需的共享库.例如,如果我们在我们的例子程序运行这个程序,我们会得到下面的输出:
$ ldd program
        libc.so.6 => /lib/libc.so.6 (0x4002a000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
在这个例子中,我们可以看到标准的C库是共享的.我们的程序所需的版本为6.其他的Unix系统也会有类似的程序来访问共享库.我们要以查看我们的系统文档来得到更为详细的信息.

抱歉!评论已关闭.