unix系统的运行需要大量的数据文件,可以理解为配置文件,都是ASCII文本文件,可以用标准I/O对这些文件进行操作
口令文件
口令就是用于验证用户权限登录密码等信息的文件,文件地址/etc/passwd。文件里面的内容一行作为一条数据,一条数据里面的各各字段用冒号隔开,各字段对应到<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. */
};
但是各各基于unix下的操作系统对上面的字段支持不一致,支持情况以及字段信息如下:
获取某个用户的口令结构体提供了如下函数:
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
函数返回的结构体是函数内部的静态变量,所以不需要调用者进行回收,并且下一次的调用会导致上一次返回的数据改变。
对于上面的口令文件的操作可以用一下函数:
#include <pwd.h>
struct passwd *getpwent(void); // 返回文件下一项,尾部返回NULL
// 返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL
void setpwent(void); // 将getpwent返回指针指向口令文件开头
void endpwent(void); // getpwent、getpwuid、getpwnam调用后 一定使用此函数将文件关闭
阴影文件
刚刚的口令文件中有个密码字段,密码需要进行加密操作,将加密的密码,保存在/etc/shadow,此文件就是阴影文件,与口令文件类似同样具有各种字段如下:
同样具有操作函数,功能与口令文件类似:
#include <shadow.h>
struct spwd *getspnam(const char* name);
struct spwd *getspent(void);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
void setspent(void);
void endspent(void);
组文件
unix组文件(/etc/group)包含下图所示字段,这些字段包含在<grp.h>中所定义的group结构中:
字段gr_mem是一个指针数组,其中每个指针指向一个属于该组的用户名,该数组以null指针结尾。
与前面的函数一样,也有对应的操作函数,也是返回静态变量,各各函数的功能以及注意事项参考口令文件:
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
// 两个函数返回值:若成功,返回指针;若出错,返回NULL
struct group *getgrent(void);
// 返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL
void setgrent(void);
void endgrent(void);
附属组ID
unix中早期的时候,任何一个用户都只属于一个组,用户登录时,系统按口令文件中的信息,得到组ID。然后可以在任何时候使用newgrp更改组ID,也可以使用newgrp不带参数返回原来的组。组ID用于做文件访问权限检查。后来增加了附属组ID进行权限检查比较。
附属组的目的是避免频繁修改组ID(一个用户可能要参加多个项目,因此使用附属组ID)。
附属组ID相关的操作函数:
#include <unistd.h>
// getgroups将进程所属用户的各附属组ID填写到数值grouplist中,
// 填写入该数组的附属组ID数最多为gidsetsize个;
// 实际填写到数组中的附属组ID数由函数返回,若gidsetsize为0,则函数只返回附属组ID数,
// 而对数组grouplist则不做修改;
int getgroups(int gidsetsize, gid_t grouplist[]);
// 返回值:若成功,返回附属组ID数量;若出错,返回-1
#include <grp.h> /* on Linux */
#include <unistd.h> /* on FreeBSD, Mac OS X, and Solaris */
// setgroups由超级用户调用来为调用进程设置附属组ID表,grouplist是组ID数组,
// 而ngroups说明数组中的元素数,ngroups的值不能大于NGROUPS_MAX;
int setgroups(int ngroups, const gid_t grouplist[]);
#include <grp.h> /* on Linux and Solaris */
#include <unistd.h> /* on FreeBSD and Mac OS X */
// initgroups函数调用setgroups,initgroups先读整个组文件,对username确定其组的成员关系;
// 然后,它调用setgroups,为该用户初始化附属组ID表;
// 除了在组文件中找到username的所有组,initgroups也在附属组ID表中包含了basegid,
// basegid是username在口令文件中的组ID。也需要超级用户才能调用
int initgroups(const char *username, gid_t basegid);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
其他数据文件
前面讲了口令文件和组文件,日常操作中还有其他文件,比如记录网络服务器所提供服务的数据文件/etc/services,记录协议信息的数据文件/etc/protocols等等,所幸的是这些数据文件和上面的接口函数都是类似的
- get函数:读下一个记录,如果需要,还会打开该文件。此种函数通常返回指向一个结构的指针。当已达到文件尾端时返回空指针。大多数get函数返回指向一个静态存储类结构的指针,如果要保存其内容,则需复制它;
- set函数:打开相应数据文件(如果尚未打开),然后反绕该文件。如果希望在相应文件起始处开始处理,则调用此函数;
- end函数:关闭相应数据文件。在结束了对相应数据文件的读、写操作后,总应调用此函数以关闭所有相应文件。
其他接口函数
- 系统标识
#include <sys/utsname.h>
int uname(struct utsname *name);
// 返回值:若成功,返回非负值;若出错,返回-1
// 函数往name结构体中填入信息
结构体中的字符串都是数组,根据不同的操作系统实现定义不同的长度。此结构体如下:
2. 主机名
#include <unistd.h>
int gethostname(char *name, int namelen);
// 返回值:若成功,返回0;若出错,返回-1
- 时间和日期
#include <time.h>
// 如果函数返回值不为空,则时间值也存放在参数中
time_t time(time_t *calptr);
// 返回值:若成功,返回时间值;若出错,返回-1
POSX1.1拓展了对个系统时钟的支持
#include <sys/time.h>
struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
// clock_gettime函数用于获取指定时钟的时间,返回的时间在timespec结构中,它把时间表示为秒和纳秒
// 第一个就是上面表格中的常量,区分时间类型
// 如果系统支持高精度时间值的情况下,此函数可能比time获取的实时系统时间有更高的精度
int clock_gettime(clockid_t clock_id, struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
// 获取指定时间类型的精度,放在第二个参数中
int clock_getres(clockid_t clock_id, struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
// 设置指定时间类型的时间
int clock_settime(clockid_t clock_id, const struct timespec *tsp);
// 返回值:若成功,返回0;若出错,返回-1
/* 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. */
};
// 可能已经弃用,但是比time函数可以获得更高的精度,第二个参数一般为NULL,根据不同的操作系统进行传参
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
// 返回值:总是返回0
/* 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
};
上面的函数得到的值需要转化成为人们可读的日期,各各函数之类的调用关系如图:
身下的函数可以查询其他资料看如何使用