|
| « | August 2008 | » | | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 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 | | | | | | | |
|
| 统 计 |
博客名称:NOTE 日志总数:14 评论数量:12 访问次数:13871 建立时间:2007年3月14日 |
 |
|
| [LINUX]Linux应用程序开发(转) |
| 毅 发表于 2008-5-6 15:46:00 |
Linux是使用C语言开发的,基于Linux平台的应用程序开发,C语言是首选的开发语言。本章记录C语言的基本概念和基础知识。
整数类型(int),
各种整数数制表示法:
-
ddd,十进制表示法,d为0--9的整数,但不能以0开头。如:123,345。
-
0ooo,八进制表示法,以0(数字0)开头,o为0--7的整数。如:010(八进制)=8(十进制),014(八进制)=12(十进制)。
-
0xhhh,十六进制表示法,以0x或0X开头,h为0--9、A、B、C、D、E、F。如:0x10(十六进制)=16(十进制),0xA(十六进制)=10(十进制)。
-
以L或l结尾的数表示长整数(long int),编译器会以32位空间存放此数字,但GCC默认是以32位存放整数,所以此表示法在Linux下没什么作用。
关键字是C语言本身保留使用的,不能用于变量和函数名。 auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
-
auto,内部变量,在函数内部声明。只能在函数内部使用,它的生命周期从调用函数开始,到函数执行完时消失。内部变量以堆栈存放,必须在函数执行时才会存在,这种方式称为声明。auto可省略。如: auto int i = 0;
/* 可写成int i = 0; */
内部变量的优缺点:
-
static auto,内部静态变量,在函数内部定义,auto也可省略。内部静态变量以固定地址存放,编译时就已分配置内在空间,这种方式称为定义。由于有固定地址,函静态变量不会随函数的结束而消失。static变量会一直保存在内存空间中,当函数再次执行时,上次保留的使用静态变量可以继续使用。如: static int i = 0;
-
extern,外部变量,是在函数外定义的变量,可被多个函数存取。在外部变量定义覆盖范围之内的函数内可以自由使用外部变量。不在外部变量定义覆盖范围之内的函数要使用外部变量就要先使用extern关健字来声明外部变量。 int i; /* 外部变量定义,在main函数外 */
int main(void)
{
i = 1; /* main()函数位于外部变量i定义的下面,不用声明可直接使用 */
printf("%d\n", i);
}
不在外部变量定义覆盖范围之内的函数要使用外部变量就要先使用extern关健字来声明外部变量。 int main(void)
{
extern int i; /* 外部变量i在main()函数之后定义,需用extern关键字声明后才能使用 */
i = 1;
printf("%d\n",i);
}
int i;
...
在另外的程序文件中我们也可以通过扩展声明使用其它程序文件中的外部变量。 程序1 hello.c
#include <stdio.h>
int main(void)
{
extern int i; //扩展声明外部变量
i = 333;
printf("%d\n", i);
extern des(void); //扩展声明外部函数
des();
}
int i; //外部变量定义
程序2 hello1.c
#include <stdio.h>
extern int i; //扩展声明其它程序文件中的外部变量
void des()
{
i++;
printf("%d\n",i);
}
编译 debian:~/c# gcc hello.c hello1.c
debian:~/c# ./a.out
333
334
外部变量有效范围总结:
外部变量的优点是生命周期长,可在函数间共享数据和传输数据。缺点是变量安全性较低,但可通过合理设置外部变量的有效范围提高安全性。
-
static extern,外部静态变量,在函数外部定义,只供单一程序文件使用,即使其它程序文件定义了同样名称的变量,编译器也把它当成另外一个变量处理。外部静态变量能有效隔离变量在一个程序文件中。 static int i;
-
register,register变量是以寄存器(register)来存放变量,而不是一般内存。只有内部变量才能使用register类型变量。使用这种变量能加快变量的处理速度。但缺点是要占用CPU寄存器。如: register int i;
register int j;
变量等级的概念也同样适用于函数。若想调用不在有效范围内的函数,则要用extern扩展声明函数的有效范围。
内部变量是以堆栈方式存放的,必须在函数执行时才会存在,所以称为声明(Declaration)。其它如static auto、extern和static extern等级的变量,都是以固定的地址来存放的,而不是以堆栈方式存放的,在程序编译时就已分配了空间,所以称之为定义(Definition)。
Table 1.1. 特殊字符的表示方法
| 符号 |
ASCII字符(十六进制) |
句柄符号 |
作用 |
| \a |
07 |
BEL |
响铃 |
| \b |
08 |
BS |
回格 |
| \f |
0C |
FF |
换页 |
| \n |
0A |
LF |
换行 |
| \r |
0D |
CR |
回车键 |
| \t |
09 |
HT |
[tab]键 |
| \v |
0B |
VT |
空行 |
| \0 |
00 |
NUL |
空字符 |
| \\ |
5C |
\ |
反斜杠 |
| \' |
2C |
' |
单引号 |
| \" |
22 |
" |
双引号 |
| \? |
3F |
? |
问号 |
-
%c,表示字符变量。
-
%s,表示字符串变量。
-
%f,表示浮点数变量。
-
%d,表示整数变量。
-
%x,表示十六进制变量。
-
%o,表示八进制变量。
数组是内存中的连续区间,可根据声明类型存放多种数值类型。如: int a[10]; 声明一个有10个int元素的数组
char b[20]; 声明一个有20个char元素的数组
指针示例: int *p; /* p是一个指针,p的内容是内存的地址,在这个地址中将存放一个整数。
数组名和指针都是用来存放内存地址的,不过数组名具有固定长度,不可变。而指针与一般变量一样,其值是可变的。
结构体是用户定义的由基本数据类型组成的复合式数据类型。数组也是复合式数据类型,但二者是不同的,数组是相同类型数据的集合,而结构体是不同类型数据的集合。如我们可以把一个人的姓名、性别,年龄组成一个单一结构体。这样在程序处理时就把它当成一个独立对象进行处理。
结构体声明方法有两种,一种是分离式声明,一种是结合式声明。分离式声明是先把声明结构体,在程序中再声明结构体变量。结合式声明是把结构体声明和变量声明同时完成。 分离式声明示例
struct person{
char name;
char sex;
int age;
};
main(void){
struct person worker;
...
}
结合式声明示例
struct person{
char name;
char sex;
int age;
}worker;
每个结构体可以表示一个工人的信息,如果要表示多个工人的信息,则可以用结构体数组。 struct person{
char name;
char sex;
int age;
};
main(void){
struct person worker[20]; //表示20个工人
...
}
结构体初始设置。 struct person{
char name;
char sex;
int age;
}worker={"jims","male",30};
用"."和"->"运算符存取结构体中的数据。"."是直接存取法,"->"为间接存取法,用于结构体指针。如果p是一个指向person结构体的指针,则p->name和(*p).name的结果是一样的。
结构体可以自定义数据类型,而typedef可以自定义新的类型名。如: #include <stdio.h>
typedef char *STRING; //定义一个新的字符指针类型名STRING
main(void){
STRING a;
a = "abc";
printf("the a value is %s.\n",a);
}
a为字符指针类型,自定义类型名通常以大写方式表示,以示区别。
#define与typedef的区别是:#define只是单纯地进行变量替换,而typedef是创建新的类型名。typedef的一个主要作用是简化声明,提高程序的可读性。如: typedef struct person{
char name;
char sex;
int age;
} p
这样我们就定义一个新的结构体类型名p,在程序中我们可以使用它来声明变量。如 main(void){
p worker;
worker = {"jims","male",30};
}
函数是C代码的集合,每个C程序由一个或多个函数组成,main()是一个特殊的函数,是C程序的入口,每个C程序必须有且只能有一个mian()函数。
ANSI函数定义: 类型 函数名(类型 参数1,类型 参数2, ...)
{
函数代码;
}
示例:
int func(int i, char c)
{
...
}
在程序中要使用我们设计开发的函数,需要先进行声明,函数声明的作用是把函数类型告诉编译器。函数声明与定义差不多,只是不包括程序主体。上面示例的函数在主程序中的声明方式如下: void main()
{
int total;
int func(int i, char c); //函数声明
total = int(xxx,xxx); //声明后才能调用该函数
}
![点击在新窗口查看原始图片 [Note]](http://www.ringkee.com/note/opensource/linuxdev/images/note.png) |
|
| 定义和声明中的参数类型(int,char)要相同,但名称(i,c)可以不同。 |
当函数没有返回值时,需声定义成void类型,调用者也要做void声明。
一般我们把函数的声明放在一个统一的文件中,这个文件叫头文件。在程序中用#include命令把头文件包含进来。在程序中调用函数前就不用再进行函数声明了。头文件简化了函数声明的管理并使头文件可被多个程序重复使用。大大提高C程序的开发效率。例如:我们最常使用的printf()函数,在使用前我们不需每次都进行声明操作,直接使用就可以啦。但前提是我们要把stdio.h头文件包含进来。printf()函数声明在stdio.h文件中已进行了声明。
Linux系统头文件位于/usr/include中。默认情况下编译器只在该目录下搜索头文件。
C语言在程序进行编译之前,会先将程序中以"#"标记的部份进行处理。这种处理叫做预处理。预处理主要的完成以下三个内容:宏处理、头文件和条件式编译。
-
宏处理指令语法如下: #define 宏名 字符串
示例:
#define MAX 200
![点击在新窗口查看原始图片 [Note]](http://www.ringkee.com/note/opensource/linuxdev/images/note.png) |
|
| 宏指令语句尾不用加分号(;) |
宏定义可以用#undef命令取消,我们可以用该功能进行程序调试。
-
头文件处理是把头文件中的函数声明插入程序中。
-
条件式编译,编译器可根据条件式编译语句有选择地进代码块进行编译。选择式编译指令如下: #if 表达式 如果表示式结果不为0,则编译下面的程序
#ifdef 宏名 若宏名已被定义,则编译下面的程序
#ifndef 宏名 若宏名未定义,则编译下面的程序
#else 前面条件不成立时,则编译下面的程序
#endif 结束上列各种条件式编译
直接生成a.out可执行文件 debian:~/c# gcc hello.c
编译hello.c程序,生成hello可执行文件: debian:~/c# gcc -o hello hello.c
生成.s的汇编代码文件。 debian:~/c# gcc -S hello.c
如果想利用gdb工具来调试程序,在编译程序时要使用-g选项。如: debian:~/c# gcc -g serial.c -o serial
调试serial程序。 debian:~/c# gdb serial
GNU gdb 6.5-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) list
8 #include <errno.h> /*错误号定义*/
9
10 int main(void)
11 {
12 int fd,n,status,buffsize;
13 struct termios a;
14 struct termios *oldtio;
15 char m[255],*comm;
16
17 fd = open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
(gdb)
gdb的list命令是列出程序源码。下面介绍gdb下的各种操作。
-
list,列出程序源代码,一次只列出10行的内容。list命令可以指定范围。如:list 5,10可列出第5行到第10行的内容。
-
run,执行程序。按Ctrl+c可中断程序的执行。
-
shell,暂时退出gdb回到shell环境。在shell环境用exit命令可以返回gdb。
-
break,设置断点,后跟行号则把断点设置在指定的行号,后跟函数名则把断点设置在函数。如break 6,break function。还可根据条件设置断点,如:break 9 if result > 50。这条命令的意思是,当运行到第9行时,如果result变量的值大于50,则中断程序。 (gdb) break 6
Breakpoint 1 at 0x8048634: file serial.c, line 6.
-
watch,指定条件,如果成立则中断。如:watch result >50。当result的变量大于50时,马上中断程序。
-
print,打印变量值,如:print result。
-
whatis,查看变量类型,如:whatis result。
-
continue,从中断点继续运行程序。
-
step,从中断点开始单步运行,如果遇到函数,则进入函数单步运行。
-
next,从中断点开始单步运行,如果遇到函数,则运行函数,该命令不会进入函数单步运行,而是运行整个函数。
-
info breakpoints,查看程序中所设置的所有中断点信息。 (gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048634 in main at serial.c:6
Enb字段是"y",表示断点1现正生效。
-
disable/enable,控制中断点失效和启用。如:disable 1。如果disable/enable命令后没有指定断点号,则该命令作用于所有已设置的断点。 (gdb) disable 1
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep n 0x08048634 in main at serial.c:6
Enb字段由"y"变成"n",断点1暂时被禁止。
-
enable once,使断点生效一次。
-
delete,删除断点。如:delete 1。delete要指定断点号。
-
clear,删除断点。如:clear 6。clear要指定设置断点的行号或函数名。
-
help all,显示所有gdb环境的命令。
![点击在新窗口查看原始图片 [Note]](http://www.ringkee.com/note/opensource/linuxdev/images/note.png) |
|
| 在gdb环境下,按tab键可自动补全命令。直接按回车键可重复执行上一个操作。按上下光标键可显示历史命令。 |
在设置Linux的系统路径时,使用冒号分隔每个路径名。如: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11"
在Linux中的程序有两种,一种是可执行程序,与Windows下的.exe文件类似,一种是脚本,与Windows下的.bat文件类似。
Linux中常用的程序存放路径有以下几个:
-
/bin,该路径存放系统启动时需要使用的程序。
-
/usr/bin,该路径存放用户需使用的标准程序。
-
/usr/local/bin,该路径存放本地安装的程序。
-
Linux使用斜杠"/"分隔路径名,而不是Windows的反斜杠"\"。
-
Linux下的C编译器使用GCC,由于历史的原因,在POSIX兼容的操作系统中,C编译器都叫cc,所以Linux下也有一个cc命令,它是一个到gcc的软链接。
开发工具,多数位于/usr/bin或/usr/local/bin目录下。
头文件,位于/usr/include目录。头文件包含有常量定义、系统调用和库函数调用的声明。这是系统默认的头文件存放路径,在编译程序时,编译器会自动查找该目录。gcc编译器在编译程序时也可用-I参数指定另外的头文件路径。如: gcc -I/usr/local/myinclude test.c。
库文件,库是一组已编译的函数集合,可方便我们重用代码。默认存放在/lib和/usr/lib目录。库文件可分为静态和共享两类。
在编译时可用包含路径的库文件名或用-l参数指定使用的库文件,/usr/lib/libm.a等价于-lm。如: gcc -o hello hello.c /usr/lib/libm.a
或用-l参数写成
gcc -o hello hello.c -lm
如果我们要使用的库文件不在默认位置,在编译程序时可用-L参数指定库文件的路径。下面例子使用了/usr/hello/lib目录下的libhello库文件: gcc -o hello -L/usr/hello/lib hello.c -lhello
创建和使用静态库。
-
分别创建两个函数,函数a的内容如下: #include <stdio.h>
void a(char *arg)
{
printf("function a,hello world %s\n",arg);
}
函数b的内容如下: #include <stdio.h>
void b(int arg)
{
printf("function b,hello world %d\n",arg);
}
-
接着,生成两个对象文件。 debian:~/c# gcc -c a.c b.c
debian:~/c# ls *.o
a.o b.o
-
最后,用ar归档命令把生成的对象文件打包成一个静态库libhello.a。 debian:~/c# ar crv libhello.a a.o b.o
r - a.o
r - b.o
-
为我们的静态库定义一个头文件lib.h,包含这两个函数的定义。 /*
* this is a header file.
*/
void a(char *arg);
void b(int arg);
}}}
* 创建jims.c程序,内容如下。{{{#!cplusplus
#include "lib.h"
int main()
{
a("jims.yang");
b(3);
exit(0);
}
-
利用静态链接库编译程序。 debian:~/c# gcc -c jims.c
debian:~/c# gcc -o jims jims.o libhello.a
debian:~/c# ./jims
function a,hello world jims.yang
function b,hello world 3
debian:~/c#
![点击在新窗口查看原始图片 [Note]](http://www.ringkee.com/note/opensource/linuxdev/images/note.png) |
|
| gcc -o jims jims.o libhello.a也可以写成gcc -o jims jims.o -L. -lhello。 |
共享库比静态库具有以下的优点:
ldconfig程序用来安装一个共享库,。
只有在为系统库安装一个库的时候,才需要在/etc/ld.so.conf中创建记录,并运行ldconfig更新共享库的缓存。
LD_LIBRARY_PATH环境变量用来指定附加的库文件路径。系统默认的库文件路径位于/usr/lib和/lib目录下。
LD_PRELOAD环境变量指定提前载入的库,用于替代系统库。
预处理,在程序开头以“#”开头的命令就是预处理命令,它在语法扫描和分析法时被预处理程序处理。预处理有以下几类:
-
宏定义,用#define指令定义。如:#define BUFFER 1024。取消宏定义用#undef指令。宏还可带参数,如: #define BUF(x) x*3
-
包含头文件,用#include指令,可把包含的文件代码插入当前位置。如: <#include <stdio.h>。
包含的文件可以用尖括号,也可用双引号,如: #include "stdio.h"。
不同之处是,使用尖括号表示在系统的包含目录(/usr/include)下查找该文件,而双引号表示在当前目录下查找包含文件。每行只能包含一个包含文件,要包含多个文件要用多个#include指令。
-
条件编译,格式如下: 格式一,如果定义了标识符,则编译程序段1,否则编译程序段2:
#ifdef 标识符
程序段1
#else
程序段2
#endif
格式二,如果定义了标识符,则编译程序段2,否则编译程序段1,与格式一相反:
#ifndef 标识符
程序段1
#else
程序段2
#endif
格式三,常量表达式为真则编译程序段1,否则编译程序段2:
#if 常量表达式
程序段1
#else
程序段2
#endif
使用gcc编译程序时,要经过四个步骤。
GCC默认将.i文件看成是预处理后的C语言源代码,所以我们可以这样把.i文件编译成目标文件。 debian:~# gcc -c hello.i -o hello.o}}}
在GCC中使用-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。
要理解系统调用就要先理解程序代码运行的两种模式,一种是用户模式,一种是内核模式。我们编写的应用程序运行在用户模式下,而设备驱动程序和文件系统运行在内核模式。在用户模式下运行的程序受到严格的管理,不会破坏系统级应用。而在内核模式下运行的程序可以对电脑有完全的访问权。系统调用就是运行在内核模式下的代码为运行在用户模式下的代码提供服务。
系统调用的错误返回码是负数,定义在>errno.h<文件中。在系统调用中发生错误,C函数库就会用错误码填充全局变量errno。用perror()和strerror()函数可以输出错误信息。
系统调用多数在>unistd.h<中定义。
在Linux系统内所有东西都是以文件的形式来表示的,除一般的磁盘文件外,还有设备文件,如硬盘、声卡、串口、打印机等。设备文件又可分为字符设备文件(character devices)和块设备文件(block devices)。使用man hier命令可以查看Linux文件系统的分层结构。文件的处理方法一般有五种,分别是:
-
open,打开一个文件或设备。
-
close,关闭一个打开的文件或设备。
-
read,从一个打开的文件或者设备中读取信息。
-
write,写入一个文件或设备。
-
ioctl,把控制信息传递给设备驱动程序。
open,close,read,write和ioctl都是低级的,没有缓冲的文件操作函数,在实际程序开发中较少使用,一般我们使用标准I/O函数库来处理文件操作。如:fopen,fclose,fread,fwrite,fflush等。在使用标准I/O库时,需用到stdio.h头文件。
-
fopen()这个标准I/O库函数用于打开文件,在Linux中文件要先打开后才能进行读写操作。 #include <stdio.h>
FILE *fopen(const char *filename, const char *mode);
*mode选项:
"r" 或o"rb" 为读打开文件
"w" 或 "wb" b为写打开文件,如果文件不存在则创建,如果存在则覆盖
"a" 或 "ab" b为追加内容而打开文件
"r+" 或o"rb+" 或 "r+b"r 为更新打开文件,不会覆盖旧文件
"w+" 或 "wb+"b或 "w+b"w 为更新打开文件,会覆盖旧文件
"a+"a或 "ab+" 或 "a+b" 为更新打开文件,更新内容追加到文件末尾
一些常用的文件和目录维护函数:chmod、chown、unlink、link、symlink、mkdir、rmdir、chdir、getcwd、opendir,closedir、readdir、telldir、seekdir等。
fcntl用于维护文件描述符,mmap用于分享内存。
创建文档并输入信息的示例代码: #include <stdio.h>
main(void)
{
FILE *fp1;
char c;
fp1 = fopen("text.txt","w");
while ((c = getchar())!= '\n')
putc(c,fp1);
fclose(fp1);
}
显示路径的示例代码 #include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *topdir = ".";
if (argc >= 2)
topdir = argv[1];
printf("Directory scan of %s\n", topdir);
printdir(topdir,0);
printf("done.\n");
exit(0);
}
printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL)
{
fprintf(stderr,"cannot open directory:%s\n",dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL)
{
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode))
{
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",depth,"",entry->d_name);
printdir(entry->d_name,depth+4);
}
else printf("%*s%s\n",depth,"",entry->d_name);
}
chdir("..");
closedir(dp);
}
void main()表示程序没有参数,int main(int argc, char *argv[])表示程序要带参数,argc保存着参数的个数,argv[]数组保存着参数列表。如: debian:~# mytest a b c
argc: 4
argv: ["mytest","a","b","c"]
getopt()函数和getopt_long()用来处理程序选项。getopt_long()函数可以处理以"--"开头的选项。Gnu官方手册页:http://www.gnu.org/software/libc/manual/html_node/Getopt.html
获取命令行参数的示例代码: #include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int opt;
while((opt = getopt(argc,argv,"if:lr")) != -1) /* 返回“-1”表示已没选项需要处理。*/
{
switch(opt){
case 'i':
case 'l':
case 'r':
printf("option: %c\n", opt);
break;
case 'f':
printf("filename: %s\n", optarg); /*如果选项需要一个参数,则参数存放在外部变量optarg中。*/
break;
case ':':
printf("option needs a value \n"); /*“:”表示选项需要参数*/
break;
case '?':
printf("unknown option: %c\n", optopt); /*返回“?”表示无效的选项,并把无效的选项存放在外部变量optopt中。*/
break;
}
}
for(; optind < argc; optind++) /*外部变量optind指向下一个要处理的选项索引值。*/
printf("argument: %s\n", argv[optind]);
}
在bash shell中使用set命令可以列出Linux系统的环境变量,在C程序中我们也可以用putenv()和getenv()函数来获取Linux系统的环境变量。这两个函数的声明如下: char *getenv(const char *name);
int putenv(const char *string);
系统有一个environ变量记录了所有的系统变量。下面的示例代码可把environ的值显示同来。 #include <stdlib.h>
#include <stdio.h>
extern char **environ;
int main()
{
char **env = environ;
while(*env)
{
printf("%s\n",*env);
env++;
}
}
linux和其它unix一样,使用GMT1970年1月1日子夜作为系统时间的开始,也叫UNIX纪元的开始。现在的时间表示为UNIX纪元至今经过的秒数。 #include <time.h>
time_t time(time_t *t);
显示系统时间的示例代码:
#include <time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int i;
time_t the_time;
for(i = 1; i <= 10; i++){
the_time = time((time_t *)0);
printf("%d the time is %ld\n", i, the_time);
sleep(2);
}
}
用ctime()函数以友好方式返回当前时间,它的函数声明格式: #include <time.h>
char *ctime(const time_t *timeval);
示例:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t time1;
(void)time(&time1);
printf("The date is: %s\n",ctime(&time1));
}
程序输出:
The date is: Thu Dec 7 09:58:23 2006
用localtime()函数可以返回本地时间,它是一个tm结构,tm结构体的内容如下: struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
int tm_sec Seconds, 0-61
int tm_min Minutes, 0-59
int tm_hour Hours, 0-23
int tm_mday Day in the month, 1-31
int tm_mon Month in the year, 0-11(January= 0)
int tm_year Years since 1900
int tm_wday Day in the week, 0-6. (Sunday = 0)
int tm_yday Day in the year, 0-365
int tm_isdst Daylight savings in effect
localtime()函数的使用方法如下: 函数声明:
#include <time.h>
struct tm *localtime(const time_t *timeval);
示例代码:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t time1;
struct tm *p;
time1 = time(NULL);
printf("The ctime is: %s\n",ctime(&time1));
p = localtime(&time1);
printf("The localtime is:\n tm_year+1900 = %d年\n tm_mon = %d月\n tm_mday = %d日\n wday = %d\n hour = %d 时\n min = %d分\n sec = %d秒\n",p->tm_year+1900,p->tm_mon,p->tm_mday,p->tm_wday,p->tm_hour,p->tm_min,p->tm_sec);
}
运行结果:
The ctime is: Thu Dec 7 10:31:36 2006
The localtime is:
tm_year+1900 = 2006年
tm_mon = 11月
tm_mday = 7日
wday = 4
hour = 10 时
min = 31分
sec = 36秒
用mkstemp()函数创建临时文件。 #include<stdlib.h>
int mkstemp(char * template);
示例:
#include <stdio.h>
int main(void)
{
char template[] = "template-XXXXXX";
int fp;
fp = mkstemp(template);
printf("template = %s\n", template);
close(fp);
}
获取用户信息。 声明:
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid); /* 根据uid返回用户信息 */
struct passwd *getpwnam(const char *name); /* 根据用户名返回用户信息 */
passwd结构体说明:
passwd Member Description
char *pw_name The user's login name
uid_t pw_uid The UID number
gid_t pw_gid The GID number
char *pw_dir The user's home directory
char *pw_gecos The user's full name
char *pw_shell The user's default shell
示例代码:
#include <stdio.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
int main(void)
{
uid_t uid;
gid_t gid;
struct passwd *pw;
uid = getuid();
gid = getgid();
pw = getpwuid(uid);
printf("User is %s\n", getlogin());
printf("The uid is:%d\n", uid);
printf("The gid is:%d\n",gid);
printf("The pw struct:\n name=%s, uid=%d, gid=%d, home=%s,shell=%s\n", pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell);
}
用gethostname()函数获取主机名。 函数声明:
#include <unistd.h>
int gethostname(char *name, size_t namelen); /* 主机名返回给name变量 */
示例代码:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char computer[100];
int status;
status = gethostname(computer, 100);
printf("The status is %d\n", status);
printf("The hostname is: %s\n", computer);
}
用uname()函数获取主机详细信息,就像shell的uname命令返回的信息一样。 函数声明:
#include <sys/utsname.h>
int uname(struct utsname *name);
utsname结构体说明:
utsname Member Description
char sysname[] The operating system name
char nodename[] The host name
char release[] The release level of the system
char version[] The version number of the system
char machine[] The hardware type
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/utsname.h>
int main(void)
{
char computer[100];
int status;
struct utsname uts;
status = gethostname(computer,100);
printf("The computer's size is %d\n",sizeof(computer));
printf("The status is %d\n", status);
printf("The hostname is: %s\n", computer);
uname(&uts);
printf("The uname's information.\n uts.sysname=%s\n uts.machine=%s\n uts.nodename=%s\n uts.release=%s\n uts.version=%s\n", uts.sysname,uts.machine,uts.nodename,uts.release,uts.version);
}
使用syslog()函数处理日志信息。 函数声明:
#include <syslog.h>
void syslog(int priority, const char *message, arguments...);
priority参数的格式(severity level|facility code)
示例:
LOG_ERR|LOG_USER
severity level:
Priority Level Description
LOG_EMERG An emergency situation
LOG_ALERT High-priority problem, such as database corruption
LOG_CRIT Critical error, such as hardware failure
LOG_ERR Errors
LOG_WARNING Warning
LOG_NOTICE Special conditions requiring attention
LOG_INFO Informational messages
LOG_DEBUG Debug messages
facility value(转自syslog.h头文件):
/* facility codes */
#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* random user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* network news subsystem */
#define LOG_UUCP (8<<3) /* UUCP subsystem */
#define LOG_CRON (9<<3) /* clock daemon */
#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
#define LOG_FTP (11<<3) /* ftp daemon */
示例代码:
#include <syslog.h>
#include <stdio.h>
int main(void)
{
FILE *f;
f = fopen("abc","r");
if(!f)
syslog(LOG_ERR|LOG_USER,"test - %m\n");
}
上面的日志信息由系统自动给出,我们也可过滤日志信息。用到以下函数: #include <syslog.h>
void closelog(void);
void openlog(const char *ident, int logopt, int facility);
int setlogmask(int maskpri);
logopt参数的选项:
logopt Parameter Description
LOG_PID Includes the process identifier, a unique number allocated to each process by the system, in the messages.
LOG_CONS Sends messages to the console if they can’t be logged.
LOG_ODELAY Opens the log facility at first call to .
LOG_NDELAY Opens the log facility immediately, rather than at first log.
示例代码:
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int logmask;
openlog("logmask", LOG_PID|LOG_CONS, LOG_USER); /*日志信息会包含进程id。*/
syslog(LOG_INFO, "informative message, pid=%d", getpid());
syslog(LOG_DEBUG,"debug message, should appear"); /*记录该日志信息。*/
logmask = setlogmask(LOG_UPTO(LOG_NOTICE)); /*设置屏蔽低于NOTICE级别的日志信息。*/
syslog(LOG_DEBUG, "debug message, should not appear"); /*该日志信息被屏蔽,不记录。*/
}
不同安全级别的日志信息存放在/var/log目录下的哪个文件中是由/etc/syslog.conf文件控制的,下面是我系统中syslog.conf文件的内容: # /etc/syslog.conf Configuration file for syslogd.
#
# For more information see syslog.conf(5)
# manpage.
#
# First some standard logfiles. Log by facility.
#
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
#cron.* /var/log/cron.log
daemon.* -/var/log/daemon.log
kern.* -/var/log/kern.log
lpr.* -/var/log/lpr.log
mail.* -/var/log/mail.log
user.* -/var/log/user.log
uucp.* /var/log/uucp.log
#
# Logging for the mail system. Split it up so that
# it is easy to write scripts to parse these files.
#
mail.info -/var/log/mail.info
mail.warn -/var/log/mail.warn
mail.err /var/log/mail.err
# Logging for INN news system
#
news.crit /var/log/news/news.crit
news.err /var/log/news/news.err
news.notice -/var/log/news/news.notice
#
# Some `catch-all' logfiles.
#
*.=debug;\
auth,authpriv.none;\
news.none;mail.none -/var/log/debug
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none -/var/log/messages
#
# Emergencies are sent to everybody logged in.
#
*.emerg *
#
# I like to have messages displayed on the console, but only on a virtual
# console I usually leave idle.
#
#daemon,mail.*;\
# news.=crit;news.=err;news.=notice;\
# *.=debug;*.=info;\
# *.=notice;*.=warn /dev/tty8
# The named pipe /dev/xconsole is for the `xconsole' utility. To use it,
# you must invoke `xconsole' with the `-file' option:
#
# $ xconsole -file /dev/xconsole [...]
#
# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
# busy site..
#
daemon.*;mail.*;\
news.crit;news.err;news.notice;\
*.=debug;*.=info;\
*.=notice;*.=warn |/dev/xconsole
进程是任何正在运行的程序代码,它是操作系统的基本调度单位,只有它能在CPU上运行。对于一个进程,内核记录以下信息:
-
进程运行的当前位置。
-
进程正在访问的文件。
-
进程的所属的用户和组。
-
进程的当前目录。
-
进程访问的内存空间状况。
pid是进程的标识符,存放在pid_t结构的变量中。在一个进程中创建另一个进程时,这个新进程就是子进程,原来的进程就是父进程。子进程结束时会通知父进程。如果父进程结束而子进程没有结束,则子进程会成为孤儿进程。所有孤儿进程都会变成init进程的子进程。init进程是系统启动的第一个进程,它其中一个主要功能就是收集孤儿进程,以便内核将子进程从进程表中删除。通过getpid()和getppid()函数可以获得进程的pid。
使用open()函数打开串口,open()函数有两个参数,第一个是要打开的设备名(如:/dev/ttyS0)。第二个是打开的方式。打开方式有以下三种:
成功打开串口则会返回文件描述符,打开失败则返回-1。下面是一个打开串口的示例: fd = open("/dev/ttyS0",O_RDWR|O_NDELAY|O_NDELAY);
使用close()关闭打开的串口,唯一的参数是打开串口的文件描述符。下面是一个关闭串口的示例: close(fd); //fd是打开串口返回的文件描述符
用write()函数向串口写数据。下面是一个向串口写数据的示例: n = write(fd,buff,len);
/* n表示成功写到串口的字节数,如果写入失败则返回-1
fd是打开串口返回的文件描述符
buff表示写入的内容
len表示写入信息的长度。
*/
用read()函数从串口读取数据。下面是一个从串口读数据的示例: n = read(fd,buff,len);
/* n表示从串口读到字节数
fd是文件描述符
buff是读入字节存放的缓冲区
len表示读入的字节数
*/
通过fcntl()函数可以操作文件描述符,用以控制读取数据的状态。fcntl(fd,F_SETFL,0)表示没有数据则阻塞,处于等待状态,直到有数据到来;fcntl(fd,F_SETFL,FNDELAY)表示当端口没有数据时马上返回0。
所有的串口属性都在一个名为termios的结构体中,要使用该结构体要包含termios.h头文件。在该头文件中还定义两个重要的函数tcgetattr()和tcsetattr(),分别用以获取和设置串口的属性。如:tcgetattr(fd,&old_termios),tcsetattr(fd,TCSANOW,&new_termios)。old_termios是旧的串口属性,new_termios是重新设置的新串口属性。tcsetattr()函数中常量的意义是:
termios结构体内容: 成员 描述
------------------------------- |
| 阅读全文(355) | 回复(0) |
|
|