《Linux程序设计》学习笔记04——Linux环境

来源:互联网 发布:店老大软件 编辑:程序博客网 时间:2024/04/29 22:35


Linux编写程序时,必须考虑到程序将在一个多任务环境中运行。这意味着在同一时间会有多个程序运行,它们共享内存、磁盘空间和CPU周期等机器资源。甚至同一程序也会有多个实例同时运行。最重要的是,这些程序能够互不干扰,了解他们的环境,并且能正确运行以避免冲突——例如试图与其他程序同时写同一个文件。

 

程序参数

无论操作系统何时启动新程序,参数argcargv都被设置并传递给main。这些参数通常由其他程序提供,这个程序一般是shell,它请求操作系统启动该新程序。

注意Linuxshell一般会在设置argcargv之前对文件名参数进行通配符扩展,而MS-DOSshell则期望程序接受带通配符的参数并执行它们自己的通配符扩展。

命令行参数在向程序传递信息方面是很有用的。许多工具程序都是有命令行参数来改变程序的行为或设置选项。这些参数大多以短横线(-)开头;不带后续参数的选项还可在一个短横线后归并到一起。

当需要在自己的程序中处理这些命令行参数时,我们需要自己对这些参数解析从而判断出有效的参数。当参数个数很多时,这一过程将是非常繁琐的。实际上,X/Open规范定义了命令行选项的标准用法,同时定义了在C语言程序中提供命令行开关的标准编程接口:getopt函数。

getopt函数将传递给main程序的argcargv作为参数,同时接受一个选项指定字符串optstring,该字符串高速getopt哪些选项可用,以及每个选项是否有关联值。程序将循环调用getopt对选项参数进行处理,直到getopt返回-1时处理完毕。关于getopt的详细信息请查阅man手册。

我们在实际使用中可能会选择长参数,它以双短横线(--)表示。GNU C库包含了getopt的另一个版本,称为getopt_long,它能同时接受短参数和长参数。getopt_long函数getopt函数多了两个参数,一个被定义为option结构(它指定了函数可以接受的长参数和函数对应返回的值),另一个通常被置NULL

提示:在使用getopt_long函数时,除了要包含头文件getopt.h外,还需要把常量_GNU_SOURCE一同包含进来。

 

无参数和void参数

在定义自己的程序时,当不需要传递参数时我们可能置参数列表为空或者填入void。那么这两种方式相同么?考虑下面两个函数

void foo1();

void foo2(void);

当我们使用foo1(1,2)调用foo1函数时编译器将不报任何错误,同时可以正常运行;它的执行结果与调用foo1()的结果一致。然而,当我们使用foo2(1,2)调用foo2函数时编译器将报错。因此,无参数和void参数使得函数显现出两种不同的行为。

实际上,void参数指定函数在调用时不能有任何参数;而无参数则没有对参数的传递做任何的规范,也就是说,你可以传递任何类型的任意个数的参数。

 

环境变量

环境变量是一把双刃剑,使用它的时候要小心!与命令行选项相比,它们对用户来说更加“隐蔽”,这样就使得调试变得更加困难。从某种意义上来说,环境变量就像全局变量一样,它们会改变程序的行为,产生不可预期的结果。

对于环境环境变量的读写操作可以有两种方式,在《Unix环境下C语言编程及项目实践》的读书笔记4中有详细的介绍。

 

时间和日期

通常能确定时间和日期对一个程序来说是非常有用的。

我们可以使用time函数获取一个time_t类型的时间值,该值是从格林威治时间到当前时间点的秒数。函数localtime将一个time_t类型的时间值转换为tm结构,通过该结构可以清晰得了解当前的年、月、日、时、分等。函数mktime的功能则相反,它将一个tm结构的时间值转换为time_t类型。上面的几个函数在《精通UnixC语言编程及项目实践》的读书笔记5中有详细的介绍。

为了得到更“友好”的时间和日期表示,像date命令输出的那样,我们可以使用asctime函数和ctime函数。实际上,为了对时间和日期字符串的格式有更多的控制,Linux和现代的类UNIX系统提供了strftimestrptime函数。它很像是一个针对时间和日期的sprintfsscanf函数,具体的信息请查看man手册。

注意:编译包含了strptime函数的程序时,需要在包含time.h头文件的语句之前包含_XOPEN_SOURCE宏定义。

提示:我们在程序中经常使用sleep函数来完成指定时间的睡眠。实际上,sleep函数只能指定秒级的时间,如果要精确到微妙级(10-6s)可以使用usleep函数。除此之外,还有一个timeval结构可以完成微妙级的操作。

 

临时文件

很多情况下,程序会利用一些文件形式的临时存储手段。这些临时文件可能保存着一个计算的中间结果,也可能是关键操作的文件备份。

临时文件的用法很常见,但必须确保应用程序为临时文件选取的文件名是唯一的。GNU C提供了两个函数tmpnametmpfile来创建临时文件。详细情况请参考man手册。

提示:当需要创建临时文件且需要对其进行读写时,请优先考虑使用tmpfile函数。该函数同时创建和打开临时文件,这样就避免了使用tmpnam函数时可能有另一个程序用同样的名字打开文件的风险。

 

用户及主机信息

程序能够通过检查环境变量和读取系统时钟来在很大程度上了解它所处的运行环境。除此之外,程序还可以发现它的使用者的相关信息。

函数getuidgetlogin分别获取程序关联的UID和与该关联ID相应的登录名。

实际上,系统文件/etc/passwd包含了一个用户账户数据库。要获取某个用户的信息,UNIX系统并不推荐直接对该系统文件读写,它定义了一组函数来提供一个标准二又有效地获取用户信息的编程接口getpwuidgetpwnam。它们均返回一个指向与某个用户对应的passwd结构指针。

提示:你可以对程序进行设置,让它们的运行看上去好像是由另一个用户启动的。当一个程序的SUID权限被置位时,它的运行就好像是由该可执行文件的属主启动的。一个典型的例子是su命令。

UNIX环境下,可以使用uname系统调用来获取主机信息。它将主机信息写入一个utsname结构。详细细节请查阅man手册。

 

日志

许多应用程序需要记录它们的活动。系统程序经常需要向控制台或日志文件写消息。这些消息能指示错误、警告或是与系统状态有关的一般信息。

提示:当程序短小时我们经常使用gdb工具来调试;但当程序比较庞大时,一个有效地调试手段就是使用日志功能,在日志中搜索出错信息。

UNIX规范为所有程序提供了一个接口,通过syslog函数来产生日志信息。这些信息往往被记录在日志文件/var/log/message中,部分调试信息可能记录在/var/log/debug中。

通过syslog函数向系统的日志文件发送的每条日志信息都有一个优先级。而且不同程序写入的日志信息并不能明显地区分开来。实际上,UNIX提供了几个函数来改变日志记录行为:openlogcloselogsetlogmask函数。有效地使用这三个函数可以在日志文件中与其他程序写入的日志区分开。关于详细信息请查阅man手册。