UNIX系统的正常运行需要使用大量与系统有关的数据文件,这些文件都是ASCII文本文件,并且使用标准I/O库读这些文件。
UNIX口令文件是/etc/passwd
,每一行包含下图中的各字段,字段之间用冒号分隔,这些字段包含在
两个获取口令文件项的函数:
#include
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
/* The passwd structure. */
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. */
};
下列3个函数用于查看整个口令文件:
#include
struct passwd *getpwent(void);
// 返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL
void setpwent(void);
void endpwent(void);
getpwent
函数返回口令文件中的下一个记录项;setpwent
函数用来将getpwent
函数的读写地址指向密码文件开头;endpwent
函数关闭这些文件,在使用getpwent
函数查看完口令文件后,一定要调用endpwent
关闭这些文件。/etc/shadow
)中,该文件的主要内容包括如下内容,其中用户登录名和加密口令是必需的:下面一组函数用于访问阴影口令文件:
#include
struct spwd *getspnam(const char* name);
struct spwd *getspent(void);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
void setspent(void);
void endspent(void);
UNIX组文件(/etc/group
)包含下图所示字段,这些字段包含在
gr_mem
是一个指针数组,其中每个指针指向一个属于该组的用户名,该数组以null指针结尾。可以用下列两个函数来查看组名或数值组ID:
#include
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
下列3个函数用于搜索整个组文件:
#include
struct group *getgrent(void);
// 返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL
void setgrent(void);
void endgrent(void);
setgrent
函数打开组文件(如若它尚未打开)并反绕它;getgrent
函数从组文件中读下一个记录,如若该文件尚未打开,则先打开它;endgrent
函数关闭组文件。下面3个函数用于获取和设置附属组ID:
#include
int getgroups(int gidsetsize, gid_t grouplist[]);
// 返回值:若成功,返回附属组ID数量;若出错,返回-1
#include /* on Linux */
#include /* on FreeBSD, Mac OS X, and Solaris */
int setgroups(int ngroups, const gid_t grouplist[]);
#include /* on Linux and Solaris */
#include /* on FreeBSD and Mac OS X */
int initgroups(const char *username, gid_t basegid);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
getgroups
将进程所属用户的各附属组ID填写到数值grouplist中,填写入该数组的附属组ID数最多为gidsetsize个;实际填写到数组中的附属组ID数由函数返回,若gidsetsize为0,则函数只返回附属组ID数,而对数组grouplist则不做修改;setgroups
由超级用户调用来为调用进程设置附属组ID表,grouplist是组ID数组,而ngroups说明数组中的元素数,ngroups的值不能大于NGROUPS_MAX;initgroups
函数调用setgroups
,它先读整个组文件,对username确定其组的成员关系;然后,它调用setgroups
,为该用户初始化附属组ID表;除了在组文件中找到username的所有组,initgroups
也在附属组ID表中包含了basegid,basegid是username在口令文件中的组ID。get
函数:读下一个记录,如果需要,还会打开该文件。此种函数通常返回指向一个结构的指针。当已达到文件尾端时返回空指针。大多数get
函数返回指向一个静态存储类结构的指针,如果要保存其内容,则需复制它;set
函数:打开相应数据文件(如果尚未打开),然后反绕该文件。如果希望在相应文件起始处开始处理,则调用此函数;end
函数:关闭相应数据文件。在结束了对相应数据文件的读、写操作后,总应调用此函数以关闭所有相应文件。uname
函数返回与主机和操作系统有关的信息:
#include
int uname(struct utsname *name);
// 返回值:若成功,返回非负值;若出错,返回-1
gethostname
函数只返回主机名,该名字通常就是TCP/IP网络上主机的名字:
#include
int gethostname(char *name, int namelen);
// 返回值:若成功,返回0;若出错,返回-1
UNIX内核提供的基本时间服务是计算自协调世界时(Coordinated Universal Time,UTC)公元1970年1月1日00:00:00这一特定时间以来经过的秒数,这种秒数是以数据类型time_t表示的,称为日历时间,日历时间包括时间和日期。
time
函数返回当前时间和日期:
#include
time_t time(time_t *calptr);
// 返回值:若成功,返回时间值;若出错,返回-1
POSIX.1的实时扩展增加了对多个系统时钟的支持,时钟通过clockid_t类型进行标识,下图给出了标准值:
clock_gettime
函数用于获取指定时钟的时间,返回的时间在timespec结构中,它把时间表示为秒和纳秒:
struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
#include
int clock_gettime(clockid_t clock_id, struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
clock_getres
函数把参数tsp指向的timespec结构初始化为与clock_id参数对应的时钟精度:
#include
int clock_getres(clockid_t clock_id, struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
clock_settime
函数用于对特定的时钟设置时间:
#include
int clock_settime(clockid_t clock_id, const struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
gettimeofday
函数获取当前时间:
#include
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
// 返回值:总是返回0
gettimeofday
函数以距特定时间(1970年1月1日00:00:00)的秒数的方式将当前时间存放在tp指向的timeval结构中,而该结构将当前时间表示为秒和微秒。/* A time value that is accurate to the nearest
microsecond but also has a range of years. */
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
一旦取得这种从上述特定时间经过的秒数的整型时间值后,通常需要调用函数将其转换成分解的时间结构,然后调用另一个函数生成人们可读的时间和日期,各种时间函数之间的关系如下:
localtime
和gmtime
将日历时间转换成分解的时间,并将这些存放在一个tm结构中:
/* ISO C `broken-down time' structure. */
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_MISC
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
};
#include
struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);
// 两个函数的返回值:指向分解的tm结构的指针;若出错,返回NULL
localtime
将日历时间转换成本地时间(考虑到本地时区和夏时令标志);gmtime
将日历时间转换成协调统一时间的年、月、日、时、分、秒、周日分解结构。mktime
以本地时间的年、月、日等作为参数,将其变换成time_t值:
#include
time_t mktime(struct tm *tmptr);
// 返回值:若成功,返回日历时间;若出错,返回-1
strftime
函数通过可用的多个参数来定制产生的字符串:
#include
size_t strftime(char *restrict buf, size_t maxsize,
const char *restrict format,
const struct tm *restrict tmptr);
size_t strftime_l(char *restrict buf, size_t maxsize,
const char *restrict format,
const struct tm *restrict tmptr, locale_t locale);
// 两个函数的返回值:若有空间,返回存入数组的字符数;否则返回0
strftime_l
允许调用者将区域指定为参数,除此之外,strftime
和strftime_l
函数是相同的,strftime
使用通过TZ环境变量指定的区域;strptime
函数是strftime
的反过来版本,把字符串时间转换成分解时间:#include
char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tmptr);
// 返回值:指向上次解析的字符的下一个字符的指针;否则,返回NULL
chapter6