linux c编程:系统数据文件和信息

linux系统相关的文件信息包含在/etc/passwd文件和/etc/group中。每次登录linux系统以及每次执行ls -l命令时都要使用口令文件。这些字段都包含在<pwd.h>中定义的passwd结构中。

struct passwd { 

char * pw_name; /* Username. */ 

char * pw_passwd; /* Password. */ 

__uid_t -pw_uid; /* User ID. */ 

__gid_t -pw_gid; /* Group ID. */ 

char * pw_gecos; /* Real name. */ 

char * pw_dir; /* Home directory. -*/ 

char * pw_shell; /* Shell program. */ 

}; 

对应的使用代码如下:

void getpwnam_function(){

const char *name="zhf";

struct passwd *ptr;

ptr=getpwnam(name);

if (!ptr){

printf("%s didn't exist",name);

}

else{

printf("ptr->pw_name=%s ",ptr->pw_name);

printf("ptr->pw_passwd=%s ",ptr->pw_passwd);

printf("ptr->pw_uid=%d ",ptr->pw_uid);

printf("ptr->pw_gid=%d ",ptr->pw_gid);

}

}

也可以通过uid来进行访问:

void getpwuid_function(){

uid_t uid;

struct passwd *ptr;

uid=1000;

ptr=getpwuid(uid);

if (!ptr){

printf("%d didn't exist",uid);

}

else{

printf("ptr->pw_name=%s ",ptr->pw_name);

printf("ptr->pw_passwd=%s ",ptr->pw_passwd);

printf("ptr->pw_uid=%d ",ptr->pw_uid);

printf("ptr->pw_gid=%d ",ptr->pw_gid);

}

}

前面getpwuidgetpwnam都只能获得固定的某个uid或者用户名的信息。如果想获得所有用户的信息都采用getpwent函数。它返回口令文件中的下一个记录项,每次调用此函数的时候都重写该结构。注意。在查看完口令文件后,一定要调用endpwent关闭这些文件。代码如下:

void getpwent_function(){

struct passwd *ptr;

ptr=getpwent();

while((ptr=getpwent())!=0){

printf("ptr->pw_name=%s ",ptr->pw_name);

printf("ptr->pw_passwd=%s ",ptr->pw_passwd);

printf("ptr->pw_uid=%d ",ptr->pw_uid);

printf("ptr->pw_gid=%d ",ptr->pw_gid);

}

endpwent();

}

前面的几个函数都是读取/etc/passwd,但其实在linux系统中还有一个/etc/shadow文件/etc/shadow文件正如他的名字一样,他是passwd文件的一个影子,/etc/shadow文件中的记录行与/etc/passwd中的一一对应,它由pwconv命令根据/etc/passwd中的数据自动产生。但是/etc/shadow文件只有系统管理员才能够进行修改和查看。

如下是root用户在/etc/shadow中的表现形式

root@zhf-maple:/home/zhf# cat /etc/shadow | grep root

root:$6$9SlLESdr$8vsLVsxe35oLk2IdAZm0BSei6usiRdsrVWZ/VAcqM/yQBASSkn2x/ZKpi8A9W.1qr4.w2vkFz3mTB5.kc2o/3.:17496:0:99999:7:::

文件中字段主要含义为:登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志

·  “登录名是与/etc/passwd文件中的登录名相一致的用户账号

·  “口令字段存放的是加密后的用户口令字:

·  如果为空,则对应用户没有口令,登录时不需要口令;

星号代表帐号被锁定;

双叹号表示这个密码已经过期了;

$6$开头的,表明是用SHA-512加密;

$1$表明是用MD5加密;

$2$ 是用Blowfish加密;

$5$ 是用 SHA-256加密;

·  “最后一次修改时间表示的是从某个时刻起,到用户最后一次修改口令时的天数。时间起点对不同的系统可能不一样。例如在SCOLinux中,这个时间起点是197011日。

·  ·  “最小时间间隔指的是两次修改口令之间所需的最小天数。

·  “最大时间间隔指的是口令保持有效的最大天数。

·  “警告时间字段表示的是从系统开始警告用户到用户密码正式失效之间的天数。

·  “不活动时间表示的是用户没有登录活动但账号仍能保持有效的最大天数。

·  “失效时间字段给出的是一个绝对的天数,如果使用了这个字段,那么就给出相应账号的生存期。期满后,该账号就不再是一个合法的账号,也就不能再用来登录了

同访问口令文件的一组函数类似,有另一组函数可用于访问阴影文件

#include<shadow.h>

struct spwd *getspnam(const char *name)

struct spwd *getspent(void)

spwd的结构如下:

struct spwd 

{

    char *sp_namp; /* Login name */

    char *sp_pwdp; /* Encrypted password */

    long int sp_lstchg; /* Date of last change */

    long int sp_min; /* Minimum number of days between changes */

    long int sp_max; /* Maximum number of days between changes */

    long int sp_warn; /* Number of days to warn user to change the password */

    long int sp_inact; /* Number of days the account may be inactive */

    long int sp_expire; /* Number of days since 1970-01-01 until account expires */

    unsigned long int sp_flag; /* Reserved */

};

使用方法如下:

void getspnam_function(){

const char *name="root";

struct spwd *ptr;

ptr=getspnam(name);

printf("spwd->sp_namp:%s",ptr->sp_namp);

printf("spwd->sp_pwdp:%s",ptr->sp_pwdp);

}

getspent的方法

void getspent_function(){

struct spwd *ptr;

while((ptr=getspent())!=0){

printf("spwd->sp_namp:%s",ptr->sp_namp);

printf("spwd->sp_pwdp:%s",ptr->sp_pwdp);

}

endspent();

}

 

组文件:

前面介绍了用户信息的获取方式,下面介绍获取用户组信息的方式

struct group

{

    char *gr_name; /* group name */

    char *gr_passwd; /* group password */

    int gr_gid; /* group id */

    char **gr_mem;

};

 

#include <grp.h>

struct group *getgrgid(gid_t gid);

struct group *getgrnam(const char *name);

代码如下:

void getgrnam_function(){

const char *name="root";

struct group *ptr;

ptr=getgrnam(name);

printf("ptr->gr_name:%s",ptr->gr_name);

printf("ptr->gr_passwd:%s",ptr->gr_passwd);

printf("ptr->gr_mem:%s",*ptr->gr_mem);

}

同样的如果想遍历所有的用户组信息,则可以采用另外几个函数

 

#include <grp.h>

struct group *getgrent(void);

void endgrent(void);

 

 

时间和日期例程

time函数原型(time.h中): 


time_t time(time_t *calptr);

参数: 

time_t类型变量的指针。 

返回值: 

time_t类型相当于一个longtime用于取Epoch记年以来到现在经过的秒数(系统当前时间),Epoch记年从197011日开始。把取到的时间存在指针指向的变量中。 

得到的这个值是一个长整数,直观看意义不大。要想转换成我们直观的日期需要用到localtime函数。

struct tm *localtime(const time_t *calptr);

参数: 

time_t类型变量的指针。 

返回值: 

指向tm结构体的指针类型。 

作用是将time_t的值转换为tm结构体。然后可以打印输出。 

tm结构体(time.h中): 

struct tm

{

  int tm_sec; /* Seconds. [0-60] (1 leap second) */

  int tm_min; /* Minutes. [0-59] */

  int tm_hour; /* Hours. [0-23] */

  int tm_mday; /* Day. [1-31] */

  int tm_mon; /* Month. [0-11] */

  int tm_year; /* Year - 1900.  */

  int tm_wday; /* Day of week. [0-6] */

  int tm_yday; /* Days in year.[0-365] */

  int tm_isdst; /* DST. [-1/0/1]*/

 

#ifdef __USE_BSD

  long int tm_gmtoff; /* Seconds east of UTC.  */

  __const char *tm_zone; /* Timezone abbreviation.  */

#else

  long int __tm_gmtoff; /* Seconds east of UTC.  */

  __const char *__tm_zone; /* Timezone abbreviation.  */

#endif

};

代码使用方法如下:

void time_function(){

time_t curr;

struct tm *Tm;

time(&curr);

Tm=localtime(&curr);

printf("%4d-%02d-%02d %02d:%02d:%02d ", Tm->tm_year + 1900, Tm->tm_mon + 1, Tm->tm_mday, Tm->tm_hour, Tm->tm_min, Tm->tm_sec);

}

运行结果如下:

2018-04-28 20:42:18

下面来看下时间格式转化函数

strftime函数:

函数原型:size_t strftime(char *s,size_t maxsize,char *format,conststruct tm *timeptr)
strftime函数对timeptr指向的tm结构所代表的时间和日期进行格式编排,其结果放在字符串s中。该字符串的长度被设置为(最少)maxsize个字符。格式字符串format用来对写入字符串的字符进行控制,它包含着将被传送到字符串里去的普通字符以及编排时间和日期格式的转换控制符。

strptime函数:

函数原型: char *strptime(const char *buf,const char*format,struct tm *timeptr)

format字符串的构建方式和strftime的format字符串完全一样,strptime返回一个指针,指向转换过程处理的最后一个字符后面的那个字符,

输入:const char *buf,const char *format

输出:struct tm *timeptr

转换格式如下:

 

转换控制符

说明

%a

星期几的简写形式

%A

星期几的全称

%b

月份的简写形式

%B

月份的全称

%c

日期和时间

%d

月份中的日期,0-31

%H

小时,00-23

%I

12进制小时钟点,01-12

%j

年份中的日期,001-366

%m

年份中的月份,01-12

%M

,00-59

%p

上午或下午

%S

,00-60

%u

星期几,1-7

%w

星期几,0-6

%x

当地格式的日期

%X

当地格式的时间

%y

年份中的最后两位数,00-99

%Y

%Z

地理时区名称

 

代码如下:

void strftime_function(){

int ret;

char buffer[255];

struct tm Tm;

strptime("28/April/2018:20:57:35", "%d/%b/%Y:%H:%M:%S", &Tm);

strftime(buffer, sizeof(buffer), "%d %b %Y %H:%M", &Tm);

printf("%s",buffer);

}

或者是通过mktime的方式:

void strftime_function(){

int ret;

struct tm info;

char buffer[255];

info.tm_year=2018-1900;

info.tm_mon=4-1;

info.tm_mday=28;

info.tm_hour=20;

info.tm_min=55;

info.tm_sec=00;

ret=mktime(&info);

if(ret == -1){

printf("can not make time");

}

else{

strftime(buffer, sizeof(buffer), "%d %b %Y %H:%M", &info);

printf("%s ",buffer);

}

}

时间差计算:

在评估程序的时候经常要计算程序的运行时间,有两种方法计算时间差,分别是difftime函数和clock函数

double difftime(time_t time1, time_t time2)

该函数返回以双精度浮点型 double 值表示的两个时间之间相差的秒数 (time2 - time1)。

这里得到的结果就是3秒

void different_time(){

time_t start,end;

double diff_t;

time(&start);

sleep(3);

time(&end);

diff_t=difftime(end,start);

printf("the gap is %f ",diff_t);

}

但是秒级的粒度太粗了,我们需要评估CPU的运行时间的话就必须采用clock函数

clock_t clock(void) 返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。为了获取 CPU 所使用的秒数,您需要除以 CLOCKS_PER_SEC。

在 32 位系统中,CLOCKS_PER_SEC 等于 1000000,该函数大约每 72 分钟会返回相同的值。

void clock_function(){

clock_t start,end;

double total;

start=clock();

for(int i=0;i<=1000000;i++){

}

end=clock();

total=(double)(end-start)/CLOCKS_PER_SEC;

printf("the total time is:%f ",total);

}