Linux-系统文件

来源:互联网 发布:sql server2005卸载 编辑:程序博客网 时间:2024/06/01 08:10

1、论述
2、口令文件
3、阴影口令文件
4、组文件
5、添加组ID
6、其它的系统文件
7、日历时间

1、论述操作系统作同样做为一个软件,也需要大量使用数据,而这些很多的数据都是来自于系统自身的数据文件,本章基本只讲解口令/etc/passwd、组文件/etc/group和阴影口令文件这三个系统文件。/etc/passwd:存放了所有用户(包括root用户)的账户信息,如用户名、用户ID、主目录等,/etc/group:存放了用户组组长的信息,就好比我们自己写一个学生管理系统时,会开出文件专门用于进行学生、老师和管理员的账户管理,道理是一样的。阴影口令文件(/etc/shadow)专门用于存放加用户密后密码的,当然除了/etc/passwd、/etc/group和阴影口令文件外,还包含其它非常多的重要的系统文件,但是这里我们之基本介绍这三个。那么本章的最后呢,我们将讲解linux中的日历时间是如何来的,不同的时间格式之间又是如何进行转换转换的。2、口令文件2.1、/etc/passwd前面我们已经接触过了这个文件,ls /etc/passwd-rw-r--r--. 1 root root 2244 Jan 12  2012 /etc/passwd它的所属用户(对应rw-)和所属组(对应-r-)都是root,而其它用户对应的权限是r--,目的就是防止出现本文件被随意修改的可能,但是我们可以通过passwd命令修改此文件,只前提对passwd命令做所属用户的设置位。执行命令vi /etc/passwd
root:x:0:0:root:/root:/bin/bash......radvd:x:75:75:radvd user:/:/sbin/nologinpulse:x:496:494:PulseAudio System Daemon:/var/run/pulse:/sbin/nologingdm:x:42:42::/var/lib/gdm:/sbin/nologinlinux:x:500:500:linux:/home/linux:/bin/bashsmb:x:501:501::/home/smb:/bin/bashdhcpd:x:177:177:DHCP server:/:/sbin/nologin......
上面结果中的下划线条目,一个root用户的,另一个是我的普通用户linux的,其余的是其它用户,这个文件中包含了所有用户的账户信息。每个用户口令信息分为如下7个字段,字段之间用:分隔。用户名:加密密码:数值用户ID:数值组ID:注释字段:初始工作目录(主目录):默认shell以root用户为例:root:x:0:0:root:/root:/bin/bash2.2、注意点1)、root是超级用户,其对应数值ID是0。2)、加密密码字段,我们看到的基本都是x,这个并不是经过加密后的密码,仅仅代表有该用户有密码如果用户没有密码的话,这个字段为空,实际上我们。·一般来说破解密码的方法如下两种:a)、利用算法反推,从加密后的密码反推出原有密码。b)、猜测法,利用生日、配偶生日,街道编号等猜测,再将猜测的密码密码,最后拿去 与真实的原密码的加密密码进行比较,如果相等就说明呢我们猜对了。·linux中防止密码被破的方法:a)、采用单向加密算法,所谓单向加密算法就是算法不可逆推,防止别人加密密码逆推 出原密码 。b)、真正被加密后的加密密码存放在另一个阴影口令文件中(这个文件后面提到),而 阴影口令文件对于普通用户来说是无法访问的,在我们的/etc/passwd文件中的密码字段    只表示有无密码,如果字段内容是X代表该用户有密码,字段为空代表该用户无密码。由于普通用户无权查看/etc/passwd文件,因此无法将猜测密码的加密密码和原密码的加密密码做比较,这样就防止了别人用猜测的方法来破解密码的可能。3)、其中某些字段可以为空a)、密码字段为空表示该用户没有密码。b)、注释字段可以为空。c)、shell字段可以为空,为空的话表示默认使用/bin/bash。4)、注释字段支持附加信息,这些附加信息之间用’,’隔开,如座机电话,办公地址,家庭  电话,邮箱地址等等,如果里面包含一个&,它会被替换为登陆用户名。如下例:linux:x:500:500:linux &, 18312578357, zxf17@163.com:/home/linux:/bin/bash2.3、存取口令文件的函数我们有时候需要读或写这个文件,比如要读里面的用户ID和组ID用于设置进程实际ID和有效ID等,当然我们可以自己在调用open、read、write等函数实现(需要做设置或在root全下操作),但是linux内核提供了专门的存取口令文件的函数,但是这些都是库函数,向下封装的也是open、read等函数。2.3.1、getpwuid函数2.3.1.1函数原型和所需头文件#include <sys/types.h>#include <pwd.h>struct passwd *getpwuid(uid_t uid);struct passwd *getpwnam(const char *name);2.3.1.2、函数功能:·getpwuid函数:利用数值ID搜索口令文件,返回数值ID对应用户的账户信息。·getgrnam函数:功能与getpwuid同,但它利用用户名搜索口令文件,返回数用户的账户信息。2.3.1.3、函数参数·getpwuid函数的uid_t uid:用户的数值ID·getgrnam函数的const char *name:用户名(字符串)。2.3.1.4、函数返回值这两个函数调用成功都返回指向struct passwd机结构空间的指针,如果则失败返回NULL,errno被设置2.3.1.5、注意1)、struct passwd结构形式如下 struct passwd {      char   *pw_name;       /* 用户名,字符串形式 */      char   *pw_passwd;     /* 是否有密码 */      uid_t   pw_uid;        /* user ID ,用户的数字ID*/      gid_t   pw_gid;        /* group ID ,组的数值ID*/      char   *pw_gecos;      /* 注释字段 */      char   *pw_dir;        /* 主目录 */      char   *pw_shell;      /* 默认使用的shell */ };上面结构体的7个成员项直接对应口令文件中账户信息的7个字段,字符串的用户名和用户数值ID之间有着唯一对应的关系,用户名那是给人看的,因为人对数字不敏感,而数值ID是给内核进行管理用的,对内核来说整形数值管理起来更为方便。这个函数帮我们在开出一个struct passwd结构的空间,然后再利用我们传递给它的用户数值ID到口令文件中搜寻数值ID对应的账户信息,读到struct passwd结构的空间中,并将这个空间的地址返回。2)、什么情况下使用该函数当我们知道数值ID,如用户linux对用的数值ID是500,希望通过这个数字ID获取到用户的账户信息,就需要用到这个函数。我们上一章中讲了如何自己写一个简单的ls命令,需要用到stat函数,这个函数将文件属性信息读到struct stat机构体中,里面包含一项st_uid,这一项说明了文件的所属用户是谁,但是它是一个数值ID,我们就需要用到这个函数,将数值ID转换为用户名(struct passwd 中的pw_name是字符串形式的用户名)。3)、这是一个库函数,不是系统调用函数。2.3.1.6、测试用例利用root数值ID(= =0),查询/etc/passwd口令文件,并且按照:分隔的格式打印出root用户的账户信息,要求7个字段全部打印出来。
int main(void){            struct passwd *p = NULL;        /* 根据用户的数值ID搜索/etc/passwd文件,读出root用户的账户信息 */        p = getpwuid(0);//判错省略        /* 用户名:加密密码:数值用户ID:数值组ID:注释字段:初始工作目录(主目录):默认shell */        printf("%s:%s:%d:%d:%s:%s:%s\n", p->pw_name, p->pw_passwd, \        p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);         /* 根据用户的数值ID搜索/etc/passwd文件,读出root用户的账户信息 */        p = getpwname(“root”);//判错省略        /* 用户名:加密密码:数值用户ID:数值组ID:注释字段:初始工作目录(主目录):默认shell */printf("%s:%s:%d:%d:%s:%s:%s\n", p->pw_name, p->pw_passwd, \        p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);return 0;} 
程序输出结果如下:root:x:0:0:root:/root:/bin/bash着和我们在/etc/passwd文件中直接看到的root:x:0:0:root:/root:/bin/bash是一样的。2.3.3、getpwuid、setpwent和endpwent函数2.3.3.1函数原型和所需头文件#include <sys/types.h>#include <pwd.h>struct passwd *getpwent(void);void setpwent(void);void endpwent(void);2.3.3.2、函数功能·getpwent:从口令文件读写指针指向的最开始处读,随着读的继续,口令文件的读写指针也随之向后移动,通过一个循环的控制,我们就可以读出口令文件中保存的所有用户的账户信息。·setpwent:将口令文件的文件读写指针移到口令文件的最开始位置。·endpwent:将打开的口令文件关闭。2.3.3.3、函数参数:无2.3.2.4、函数返回值·getpwent函数:调用成功返回指向struct passwd机构体的指针,如果返回NULL有   如下两种情况:a)、函数调用失败NULL,errno被设置。b)、函数调用成功(errno= =0),但是读到了文件的末尾,也返回NULL。·setpwent:无返回值·endpwent:无返回值2.3.3.5、测试用例
int main(void){            struct passwd *p = NULL;        setpwent();//现将口令文件的读写指针调到文件开始位置        while(1) {                   p = getpwent();                if((NULL == p) && (0 == errno)){ //这里使用errno,需要包含<errno.h>                        printf("读到了口令文件末尾,读口令另文件结束\n");                        exit(-1);                }                   else if(0 != errno){                        perror("getpwent is fail");                        exit(-1);                }                /* 用户名:加密密码:数值用户ID:数值组ID:注释字段:\* 初始工作目录(主目录):默认shell*/                printf("%s:%s:%d:%d:%s:%s:%s\n", p->pw_name, p->pw_passwd, \                p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);        }        endpwent();//关闭打开的口令文件        return 0;} 
在调用循环调用getpwent();函数前,我们应该事先调用setpwent函数将口令文件的读写指针移到文件的起始位置,因为如果前面有人如果打开过这个文件且读到了稳健中间就并没有读了,但是读结束后又没有关闭这个文件,那么这一次的操作就会沿用前面的操作,所以这一次就会从上一次遗留的位置处开始读取,导致读取数据不全。在整个循环的getpwent的操作结束后,养成良好习惯,关闭打开的文件。2.3.6、对以上的几个函数功能的一个总结1)、getpwuid函数:根据用户的数值ID从口令文件中获取用户的账户信息。2)、getpwnam函数:根据用户名从口令文件中获取用户的账户信息。3)、getpwent函数:通过循环控制,能够读出整个口令文件中所有用户的账户信息。4)、setpwent函数:将口令文件的文件读写指针移到文件的起始位置。5)、endpwent函数:关闭口令文件。3、阴影口令文件3.1、/etc/shadow前面说到为了防止密码被破获,加密后的密码单独存放在了阴影口令文件(/etc/shadow)中,而在、etc/passwd文件中只是在密码字段存放了一个X。但是对于这个文件来说只有超级用才能访问它,所以我们想要vi 这个文件的话,必须先切换到root权限下:首先,切换到root然后vi /etc/shadow  //文件内容如下:
。。。。。。gdm:!!:15233::::::linux:$6$0dy7yyX82TfH11Kg$O/.1bowyoBswuLBhxlzvPfax/63Gl7mPR9.aI1ArknYXQHT1nFi8DAV2qA9QPU2Z0IarTgyKKPJVpufJQK4RX0:15233:0:99999:7:::。。。。。。
阴影口令文件分为如下8个字段,字段之间用:隔开。1)、用户名2)、加密后的密码3)、上次修改密码时的日期4)、本次修改密码后,允许多少天后再次修改密码,如果为0的话,表示本次修改后, 再改密码的话不用等待,立即可再改。5)、如果我们要求固定地隔一段时间就是必须修改一次密码的话,第5个字段说明目前距离要求修改密码之间天数。6)、密码到期但却为修改,允许提醒到期警告的天数,一般为空7)、账户目前距离到期期限的天数8)、账户有效期天数,一般为空,表示永远有效一般情况下我们只关心第一个(用户名)和第二个字段(加密密码),以我的linux这个普通用户来说,它的原密码是123456,但是经过单向加密后的密码如下:$6$0dy7yyX82TfH11Kg$O/.1bowyoBswuLBhxlzvPfax/63Gl7mPR9.aI1ArknYXQHT1nFi8DAV2qA9QPU2Z0IarTgyKKPJVpufJQK4RX0其中的$6$0dy7yyX82TfH11Kg$又被称为加密盐巴,单向加密算法会用盐巴对原密码进行加密。3.2、操作阴影口令文件的函数,getspnam、getspent、setspent和endspent函数3.2.1函数原型和所需头文件#include <shadow.h>struct spwd *getspnam(const char *name);struct spwd *getspent(void);void setspent(void);void endspent(void);3.2.2、函数功能:·getspnam:利用用户名搜寻阴影口令文件,读取用户对应的8个字段的信息。·getspent:从阴影口令文件中读写指针指向的最开始处读,随着读的继续,文件的读写指针也随之向后移动,通过一个循环的控制,我们可以读出阴影口令文件中保存的所有的信息。·setpsent:将阴影口令文件的文件读写指针移到文件的最开始位置。·endspent:将打开的阴影口令文件关闭。3.2.3、函数参数·getspnam函数的const char *name:用户名。·getspent、setpsent和endspent函数:无参数3.2.4、函数返回值·getspent、getspent函数:调用成功指向struct spwd的指针,返回NULL有如下两种情况:a)、函数调用失败则NULL,errno被设置。b)、函数调用成功,但是读到了文件的末尾,也返回NULL。·setpsent和endspent函数:无返回值3.2.5、注意1)、必须在root权限下才能对/etc/shadow进行操作2)、struct spwd结构体,结构体中成员项直接对应阴影口令文件中的8个字段。     struct spwd      {        char *sp_namp;  /* 用户名 */        char *sp_pwdp;  /* 加密密码 */        long  sp_lstchg; /* 上次修改密码是的日期 */        long  sp_min;   /* 本次密码修改完后,多少天内不允许修改密码*/        long  sp_max;   /* 距离再次要求修改密码前的时间 */        long  sp_warn;  /*密码期限到期未修改,允许提醒警告的天数 */        long  sp_inact;  /*账户目前举距离到期时间的天数 */        long  sp_expire; /* 账户有效期天数,一般来说此项无效 */        unsigned long sp_flag;  /* 保留项 */      };3)、调用这个函数时,这个函数会开出struct spwd的结构体空间,然后读出阴影口令文件的中的数据并写入struct spwd的空间,然后返回这个空间的地址。3.2.6、测试用例
int main(void){            struct spwd *p = NULL;        /* 打印出用户linux在阴影口令文件中字段信息 */        p = getspnam("linux");        printf("%s:%s:%ld:%ld:%ld:%ld:%ld\n", p->sp_namp, p->sp_pwdp, \        p->sp_lstchg, p->sp_min, p->sp_max, p->sp_warn, p->sp_inact);        printf("--------------------------------------------------\n");        /* 循环读出阴影口令文件中所有的用户加密密码字段信息 */            setspent();        while(1)            {                   p = getspent();                if((NULL == p) && (0 == errno)) //这里使用errno,需要包含<errno.h>                {                           printf("读到了阴影口令文件末尾,读口阴影令另文件结束\n");                        exit(-1);                }                   else if(0 != errno)                {                           perror("getpwent is fail");                        exit(-1);                }                   printf("%s:%s:%ld:%ld:%ld:%ld:%ld\n", p->sp_namp, p->sp_pwdp, \                p->sp_lstchg, p->sp_min, p->sp_max, p->sp_warn, p->sp_inact);        }           endspent();        return 0;}
ls -al /etc/shadow----------. 1 root root 1401 Feb 18 11:08 /etc/shadow通过 /etc/shadow的文件权限可以看到,必须在root用户下调用getspnam、getspent、setspent和endspent函数才能够操作/etc/shadow文件,否则文件权限检测是不会通过的。4、组文件4.1、/etc/group文件我们之前说过,多个用户在一起可以组成一组,组长用户ID亲自担任组长ID,一般来说,正常情况下用户自己跟自己一组,组长是自己,组员也是自己。组的作用就是为了让同一组里面的多个用户之间能够按照组权限共享组里各用户的文件,与组相关的组信息存在了组文件/etc/group中。执行vi /etc/group结果如下:
。。。。。。gdm:x:42:linux:x:500:smb:x:501:。。。。。。
上面被加了下划线的是我的普通用户linux一组的字段信息,与组相关的信息字段是4个,各字段之间同样用:隔开,各字段的含义如下:组名:其实就是组长用户的用户名。是否有密码:为X代表有密码,其实就是组长用户用户密码,空代表无密码。数值组ID:其实就是组长用户的数值用户ID。指向组里各成员的用户名的指针数组:在/etc/group、文件中的这个字段里会罗列出组里面的  各成员用户名,一般有如下两种情况,。a)、该字段为空:组中成员只有组长一人b)、该字段不为空:组中成员除了组长之外,还有其它用户也属于改组。以我的普通用户对应自己一组的组字段信息linux:x:500:为例:linux:组名,就是普通linux的用户名X:有秘密,就是linux用户的密码500:linux唯一对应的数值用户ID指向组里各成员用户名的指针数组:为空4.2对组文件的操作函数4.2.1、getgrnam、getgrgid函数4.2.1.1函数原型和所需头文件   #include <sys/types.h>#include <grp.h>struct group *getgrnam(const char *name);struct group *getgrgid(gid_t gid);4.2.1.2、函数功能·getgrnam函数:利用组名搜索组文件,获取对应组的字段信息。·getgrgid函数:利用数值组ID搜索组文件,获取对应组的字段信息。4.2.1.3、函数参数·getgrnam函数的const char *name:组名·getgrgid函数函数的gid_t gid:数值组ID4.2.1.4、函数返回值这两个函数调用成功,则返回指向struct group结构体空间的指针,失败则返回NULL,errno被设置。4.2.1.5、注意1)、struct group结构,各个成员项字节对应组的4个字段中的每个字段.struct group{char   *gr_name;       /* 组名 */    char   *gr_passwd;     /* 是否有组密码 */    gid_t   gr_gid;         /* 组ID */    char  **gr_mem;        /* 指向组成员用户名的的指针数组 */}; 2)、调用这个函数时,函数会开出struct group结构的空间,然后将从组文件中读出的组信息写入该空间中,最后再把这个空间的地址返回。4.2.1.6、测试用例
void print_fun(struct group *p){               int i = 0;/* 打印出组名,是否有密码的提示,组ID */        printf("%s:%s:%d:", p->gr_name, p->gr_passwd, p->gr_gid);        /* 打印出组中成员用户的名字 */for(i=0; NULL != p->gr_mem[i]; i++)         {                       printf("%s", p->gr_mem[i]);                if(NULL != p->gr_mem[i+1]) printf(",");        }               printf("\n");}       int main(void){                   struct group *p = NULL;        p = getgrnam("linux");        print_fun(p);        p = getgrgid(500);        print_fun(p);        return 0;}           
程序输出结果如下:linux:x:500:linux:x:500:4.2.1、getgrent、setgrent、endgrent函数4.2.1.1函数原型和所需头文件   #include <sys/types.h>#include <grp.h>struct group *getgrent(void);void setgrent(void);void endgrent(void);4.2.1.2、函数功能·getgrent:从组文件的读写指针指向的最开始处读,随着读的继续,文件的读写指针也随之向后移动,通过循环地控制,我们可以读出组文件中保存的所有组的信息。·setgrent:将组文件的文件读写指针调到文件的最开始位置。·endgrent:将打开的组文件关闭。4.2.1.3、函数参数:均无参数4.2.1.4、函数返回值·getgrent:这两个函数调用成功,则返回指向struct group结构体空间的指针,失败    则返回NULL,errno被设置。·setgrent、endgrent函数:无返回值。4.2.1.5、注意:无4.2.1.6、测试用例
void print_fun(struct group *p){               int i = 0;/* 打印出组名,是否有密码的提示,组ID */        printf("%s:%s:%d:", p->gr_name, p->gr_passwd, p->gr_gid);        /* 打印出组中成员用户的名字 */for(i=0; NULL != p->gr_mem[i]; i++) {                       printf("%s", p->gr_mem[i]);                if(NULL != p->gr_mem[i+1]) printf(",");        }               printf("\n");}       int main(void){                   struct group *p = NULL;        setgrent();        while(1){                p = getgrent();                if(NULL == p) break;                print_fun(p);        }        endgrent();        return 0;}
5、添加组ID添加组ID的目的就是可以让一个用户同时属于不同的组的成员,不同组完成不同工程项目,各个组拥有属于自己的文件,但是做了添加ID后一个用户就可以访问不同组的文件,这样用户可以参加不同组的工程项目的设计了。至于如何添加组ID的讲解这里省略,有兴趣的同学参考unix高级环境编程的第6章的6.5小节。6、其它的系统文件我们前面介绍了/etc/passwd、/etc/shadow、/etc/group系统文件,但是除此外实际上还有很多的系统文件,如:/etc/setvices:记录了各种网络服务器提供的服务。/etc/protocols:记录了各种的协议。/etc/networks:记录网络信息等等。其操作的函数也分为如下几种:1)、get打头的函数:这个样子的函数分如下三种情况。a)、根据某已知条件,直接获取某个具体的记录b)、没有任何已知条件,通过一个循环可以获取文件中所有的记录。一般来说get打头的函数会开出相应的结构体空间,存放从文件中读出的记录写入开出的空间,最后再将这个空间的地址返回。2)、set打头函数:如果事先没打开文件则打开文件,但是如果事先文件已经被打开,则将  文件的读写指针移到文件的起始位置。3)、end打头函数:结束对文件的操作后关闭文件。对于其它的系统文件的情况这里就不在叙述,有兴趣的同学请查看其它文件资料。7、日历时间我们在前面提到过,linux内核中有两种时间,一是日历时间,二是日历时间,我们这里将会对日历时间和对日历时间的转换函数做一个比较详细的叙述。7.1、日历时间的概念和特点Linux内核提供的日历时间是从公元1970年1月1日00:00:00以来所经过的总秒数。这个日历时间有如下特点:1)、以国际时间而非本地时间进行计时。2)、具备多种的时间格式的转换。3)、日期和时间被作为一个量值(即总秒数)进行保存。time系统调用函数返回总秒数,返回值的类型是time_t,之前说过这是一个系统原始数据类型,是经过typedef后得到的,本质上它就是long int型,我们后面再对这个函数做具体的讲解。7.2、各种时间格式之间的转换

这里写图片描述

7.2、各个时间函数7.2.1、time系统调用函数7.2.1.1函数原型和所需头文件   #include <time.h>time_t time(time_t *t);7.2.1.2、函数功能:返回日历时间的总秒数。7.2.1.3、函数参数·time_t *t:一个指针,指向用于存放总秒数的time_t类型的空间。7.2.1.4、函数返回:函数调用成功返回总秒数,失败则返回(time_t)-1 ,errno被设置。7.2.1.6、注意通过该函数获取内核返回的总秒数,有如下两种方法:1)、返回值法:time_t tim1 = time(NULL);这时参数一般填为NULL;2)、利用传参数地址法:time(&tim2);当然像tim1 = time(&tim2);这么写也是可以的,只是没有太大的必要。7.2.1.5、测试用例
int main(void){        time_t tim1, tim2;        tim1 = time(NULL);        printf("tim1 = %ld\n", tim1);        time(&tim2);        printf("tim2 = %ld\n", tim2);        tim1 = time(&tim2);        printf("tim1 = %d, tim2 = %ld\n", tim1, tim2);        return 0;}
7.3、进行时间格式转换的函数7.3.1、getgrnam、getgrgid函数7.3.1.1函数原型和所需头文件   #include <time.h>struct tm *gmtime(const time_t *timep);struct tm *localtime(const time_t *timep);time_t mktime(struct tm *tm);char *ctime(const time_t *timep);char *asctime(const struct tm *tm);size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);7.3.1.2、函数功能·gmtime函数:将国际时间的总秒数分解为的年、月、时、分、秒。·localtime函数:将本地时间的总秒数分解为年、月、时、分、秒。·mkdtime函 数:将年月日时分秒反转为总秒数。·ctime函数:将内核返回的总秒数直接转为固定格式的本地时间。·astime函数:将分解后的年、月、时、分、秒转为固定格式的时间。·strftime函数:将分解后的年、月、时、分、秒转为我们需要格式的时间。7.3.1.3、函数参数·gmtime,localtime,ctime函数:用来存放总秒数的time_t类型空间的地址。·asctime函数:struct tm空间的地址,该空间用来存放分解后的年月日时分秒。·size_t strftime函数char *s:数组,存放要求格式的时间字符串。size_t max:数组s的空间大小。const char *format:对要求的时间格式进行说明的字符串。const struct tm *tm:struct tm空间的地址,用来存放分解后的年月日时分秒。·mktime函数:成功返回总秒数,失败返回-1,errno被设置。7.3.1.4、函数返回值·gmtime,localtime函数:返回一个指针,指向函数开出的struct tm空间,失败    返回NULL,errno被设置。·actime,asctime函数:返回转换后的时间格式的字符串,失败返回NULL,errno被    设置。·mktime函数:成功返回总秒数,失败返回-1,errno被设置。·size_t strftime函数:成功返回转换后的时间字符串的字符的个数,但是不包含’\0’,失败返回-1,errno被设置。7.3.1.5、注意1)、struct 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;       /* 夏时令设置 */  };所谓夏时令就是在日光充足时,将时间拨前一个小时,是一种为节约能源而人为规定的地方时间制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。2)、strftime函数中format的自定义格式如%a:缩写周日名                TUE%A:全周日名                 Tuesday%b:缩写月名                 Jan%B:月全名                      January%c:固定格式的日期和时间       Tue Jan 14 19:40:30 1992%d:月日                       26%H:24小时制                    23%I:小时(上下午12小时制) 11%j:年日                       089%m:月份                       08%M:分                            55%p:AM/PM(上下午指示)     PM%s:秒                            30                  %w:(周天到周6用0~6 表示)0%x:固定格式日期               01/14/92%X:固定格式时间               19:40:30%y:不带公园的年               04%Y:带公元的年                2004%z:时区名                      MST、DST、WET、......7.3.1.6、测试用例
int main(void){        time_t tim1 = -1;         char *strtim = NULL;        struct tm *tm = NULL;        char strtim_buf[100] = {0};        tim1 = time(NULL);        strtim = ctime(&tim1);        printf("固定格式的本地时间= %s", strtim);        tm = gmtime(&tim1);        strtim = asctime(tm);        printf("固定格式国际时间 %s", strtim);        strftime(strtim_buf, sizeof(strtim_buf), "%Y:%m:%d:%H:%M:%S\n", tm);        printf("自定义格式国际时间%s", strtim_buf);                 tm = localtime(&tim1);        strtim = asctime(tm);        printf("固定格式的本地时间= %s", strtim);        strftime(strtim_buf, sizeof(strtim_buf), "%Y:%m:%d:%H:%M:%S\n", tm);        printf("自定义格式本地时间%s", strtim_buf);                 tim1 = mktime(tm);            printf("总秒数= %d\n", tim1);           return 0;}