UNIX环境编程-第六章系统数据文件和信息

    科技2026-03-19  7

    APUE

    第六章 系统数据文件和信息6.1 口令文件6.2 阴影口令6.3 组文件6.4 附属组ID6.5 实现区别6.6 其它数据文件6.7 登录账户记录6.8 系统标识6.9 时间和日期例程

    第六章 系统数据文件和信息

    6.1 口令文件

    下图包含了UNIX系统口令文件的各个字段。这些字段包含在<pwd.h>中定义的passwd结构中。

    口令文件:/etc/passwd。而且是一个ASCII文件。在POSIX.1定义了两个获取口令文件项的函数。

    函数原型: #include <pwd.h> struct passwd *getpwuid(uid_t uid); struct passwd *getpwnam(const char *name); 两个函数成功返回指针,失败返回NULL。

    getpwuid函数由ls(1)程序使用,它将i节点中的数字用户ID映射为用户登录名。在键入登录时,getpwnam函数由login(1)程序使用。 这两个函数都返回一个指向passwd结构的指针,该结构已由这两个函数在执行时填入信息。passwd结构通常是函数内部的静态变量,只要调用任一相关函数,其内容就会被重写。 如果要查看的只是登录名或用户ID,那么这两个河南省能满足要求,但是也有些程序要查看整个口令文件。下列三个函数则可用于此种目的。

    函数原型: #include <pwd.h> struct passwd *getpwent(uid_t uid); 成功返回指针,出错或到达文件尾端,返回NULL void setpwent(void); void endpwent(void);

    调用getpwent时,返回口令文件的下一个记录项。如同getpwuid和getpwnam两个函数一样返回一个由它填写好的passwd结构的指针。每次调用此函数时都重写该结构。在第一次调用该函数时,它打开它所使用的各个文件。在使用本函数时,对口令文件中各个记录项的安排顺序并无要求。某些系统采用散列算法对/etc/passwd文件中各项排序。 函数setpwent反绕它所使用的文件,endpwent则关闭这些文件。在使用getpwent查看完口令文件后,一定要调用endpwent关闭这些文件。getpwent知道什么时间应当打开它所使用的文件,但是它并不知道何时关闭这些文件。

    6.2 阴影口令

    阴影口令文件(/etc/shadow)不应是一般用户可以读取的。 下列3个函数用于获取阴影口令文件。

    函数原型: #include <shadow.h> struct spwd *getspnam(const char *name); struct spwd *getspent(void); 两个函数成功返回指针,失败返回NULL void setspent(void); void endspent(void);

    6.3 组文件

    组文件/etc/group这些字段包含在<grp.h>中所定义的group结构中。字段gr_name是一个指针数组,其中每个指针指向一个属于该组的用户名。该数组以null指针结尾。可以用下列两个函数来查看组名或组ID。

    函数原型: #include <grp.h> struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); 成功返回指针,失败返回NULL。

    如果需要搜索整个组文件,则必须使用另外几个函数。

    函数原型: #include <grp.h> struct group *getgrent(void); 成功返回指针,失败返回NULL void setgrent(void); void endgrent(void);

    6.4 附属组ID

    使用下列函数获取附属组ID:

    函数原型: #include <unistd.h> int getgroups(int gidsetsize, gid_t grouplist[]); 成功返回附属组ID数量,失败返回-1 #include <unistd.h> #include <grp.h> int setgroups(int ngroups, const gid_t grouplist[]); int initgroups(const char *username, gid_t basegid); 两个函数成功返回0,失败返回-1

    getgroups:将进程所属用户的各附属组ID填写到grouplist中,填写该数组的ID数量最多为gidsetsize个。实际填写的数量由函数返回。如果gidsize为0,则只返回附属组ID数,而对数组grouplist不做修改。 setgroup:可由超级用户调用以便为进程设置附属组ID。grouplist是组ID数组,而ngroups说明了数组中的元素个数,其值不大于NGROUPS_MAX。 initgroups用于初始化附属组ID表。

    6.5 实现区别

    6.6 其它数据文件

    get函数:读取下一个记录set函数:打开相应数据文件end函数,关闭相应数据文件

    6.7 登录账户记录

    大多数UNIX系统提供下列两个数据文件:utmp文件记录当前登录到系统的各个用户,wtmp文件跟踪各个登录和注销事件。

    6.8 系统标识

    uname函数,它返回与主机和操作系统有关的信息

    函数原型: #include <sys/utsname.h> int uname(struct utsname *name); 成功返回非负数,失败返回-1 struct utsname{ char sysname[]; char nodename[]; char release[]; char version[]; char machine; }

    gethostname函数,它返回主机名,该函数通常就是TCP/IP网络上的主机名。

    函数原型: #include <unistd.h> int gethostname(char *name, int namelen); 成功返回0,失败返回-1 它指定的最大主机名长度是HOST_NAME_MAX。

    6.9 时间和日期例程

    由UNIX内核提供的基本时间服务是计算自协调世界时公元1970年1月1日00:00:00这一特定时间以来经过的秒数。这种秒数以数据类型time_t表示的(日历时间)。日历时间包含时间和日期。UNIX在这方面与其他操作系统的区别是:

    以协调统一时间而非本地时间计时可自动进行装换,如变换到夏令时将时间和日期作为一个量保存 time函数返回当前时间和日期

    函数原型: time_t time(time_t *calptr); 成功返回时间值,失败返回-1 如果参数非空calptr也会保存一份时间值。

    POSIX1.1的实时扩展增加了对多个系统时钟的支持,控制这些时钟的接口从可选组被移至基本组。时钟通过clockid_t类型进行标识。如下图: clock_gettime函数用于获取指定时钟的时间,返回timespec结构体,它把时间分为秒和纳秒。

    函数原型: #include <sys/time.h> int clock_gettime(clockid_t clock_id, struct timespec *tsp); 成功返回0,失败返回-1 当时钟ID设置为CLOCK_REALTIE时,clock_gettime函数提供了与time函数类似的功能,不过在系统支持高精度时间值的情况下,clock_gettime可能比time函数更精确。

    clock_getres函数把参数tsp指向的timespec结构初始化为与clock_id参数对应的时钟精度。例如如果精度为1ms,则tv_sec字段就是0,tv_nsec就是100 0000。

    函数原型 #include <sys/time.h> int clock_getres(clockid_t clock_id, struct timespec *tsp);

    要对特定的时钟设置时间,可以调用clock_settie:

    函数原型: #include <sys/time.h> int clock_settime(clockid_t clock_id, const struct timespec *tsp); 成功返回0,失败返回-1 我们需要适当的特权来更改时钟值,但是有些时钟是不能修改的。 对于gettimeofday函数现在已启用。与time相比,gettimeofday提供了更高的精度。 函数原型: #include <sys/time.h> int gettimeofday(struct timeval *restrict tp, void *restrict tzp); 总是返回0 tzp唯一合法值是NULL,其它值将产生不确定结果。

    下图将说明各时间函数之间的关系: localtime和gmtime将日历时间转换成分解的时间,并将这些存放在一个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; }

    函数原型: #include <time.h> struct tm *gmtime(const time_t *calptr); struct tm *localtime(const time_t *calptr); 成功指向分解的tm结构的指针,失败返回NULL。 localtime将日历时间装换成本地时间,而gmtime则将日历时间转换成协调统一时间的年月日时分秒周日分解结构。 函数mktime以本地时间的年、月、日等作为参数,将其变换成time_t值。 函数原型: #include <time.h> time_T mktime(struct tm *tmptr); 成功返回日历时间,失败返回-1。

    函数strftime是一个类似于printf的时间值函数。它非常复杂,可以通过可用的多个参数来定制产生的字符串。

    函数原型: #include <time.h> size_t strftime(char *restrict buf, size_t max_size, const char *restrict format, const struct tm *restrict tmptr); size_t strftime_l(char *restrict buf, size_t max_size, const char *restrict format, const struct tm *restrict tmptr, locale_t local); 两个函数:若有空间,返回存入数组的字符数,否则返回0 strftime_l运行调用者将区域指定为参数,除此之外两个函数是相同的。 tmptr参数是要格式化的时间值。 格式化后的结果存放在maxsize个字符的buf数组中。 下图说明foramt字符串的格式化字符:

    strptime函数将字符串时间转换为分解时间。

    函数原型: #include <time.h> char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tmptr); 返回值指向上次解析的字符的下一个字符指针,否则返回NULL 下图介绍format的格式化字符:

    Processed: 0.021, SQL: 9