APUE读书笔记(四)
6 系统数据文件和信息
口令文件
UNIX系统口令文件(/etc/passwd
)中的字段包含在<pwd.h>中定义的passwd
结构中,每一行包含一个登录项,各字段间用冒号分隔。
口令文件中通常有用户名为root的登录项,其用户ID是0(超级用户),加密口令字段包含一个占位符(现在将其存放在另一文件中),登录项的某些字段可以为空(加密口令字段为空则无加密口令),shell字段(包含可执行程序名)为空则取系统默认值(通常是/bin/sh
)、为设备则组织任何人以其登录到该系统,用户名nobody
意味着使任何人都可以登录到这个系统(同登录项的用户ID与用户组ID不提供任何特权)。
给出用户登录名或数值用户ID,得到相关项:
#include <pwd.h> struct passwd *getpwuid(uid_t uid);//通过用户ID得到相关项 struct passwd *getpwnam(const char *name);//通过用户登录名得到相关项 //返回值:成功则返回指针,出错则返回NULL
查看整个口令文件:
#include <pwd.h> struct passwd *getpwent(void);//返回口令文件中下一个记录项 //返回值:成功则返回指针,出错或到达文件尾则返回NULL void setpwent(void);//将getpwent()的读写地址绕回开头 void endpwent(void);//关闭口令文件
阴影口令
加密口令是经单向加密算法处理过的用户口令副本。
加密口令存放在称为阴影口令的文件(/ect/shadow)中,不是一般用户可以读取的,文件中用户登录名与加密口令两个字段是必需的。
访问阴影口令文件:
#include <shadow.h> struct spwd *getspnam(const char *name);//通过登录名得到相关项 struct spwd *getspent(void);//返回阴影口令文件中下一个记录 //返回值:成功则返回指针,出错则返回NULL void setspent(void);//将getspent()的读写地址绕回开头 void endspent(void);//关闭阴影口令文件
组文件
UNIX组文件(/etc/group)中的字段包含在<grp.h>中所定义的group
结构中。
查看组名或数值组ID的相关项:
#include <grp.h> struct group *getgrgid(gid_t gid);//通过组ID得到相关项 struct group *getgrnam(const char *name);//通过组名得到相关项 //返回值:成功则返回指针,出错则返回NULL
搜索整个组文件:
#include <grp.h> struct group *getgrent(void);//返回组文件中下一个记录 //返回值:成功则返回指针,出错或到达文件尾则返回NULL void setgrent(void);//将getgrent()的读写地址绕回开头 void endgrent(void);//关闭组文件
附属组ID
附属组概念使得用户不仅可以属于口令文件记录项中组ID所对应的组,也可属于多至16个另外的组。文件访问权限检查时,不仅将进程的有效组ID与文件的组ID相比较,而且也将所有附属组ID与文件的组ID进行比较。
获取和数组附属组ID:
#include <unistd.h> int getgroups(int gidsetsize, gid_t grouplist[]);//获取当前用户附属组 //返回值:成功则返回附属组ID数量,出错则返回-1 #include <grp.h>/* on Linux */ #include <unistd.h>/* on FreeBSD, Mac OS X, and Solaris */ int setgroups(int ngroups, const gid_t grouplist[]);//设置当前用户附属组(特权操作) #include <grp.h>/*on Linux and Solaris*/ #include <unistd.h>/*on FreeBSD and Mac OS X*/ int initgroups(const char *username, gid_t basegid);//为特定用户设置附属组(特权操作) //返回值:成功则返回0,出错则返回-1
其他数据文件
访问系统数据文件的一些例程:
| 说明 | 数据文件 | 头文件 | 结构 | 附加的键搜索函数 |
| - | - | - | - | - |
| 口令 | /etc/passwd | <pwd.h> | passwd
| getpwnam
、getpwuid
|
| 组 | /etc/group | <grp.h> | group
| getgrnam
、getgrgid
|
| 阴影 | /etc/shadow | <shadow.h> | spwd
| getspnam
|
| 主机 | /etc/hosts | <netdb.h> | hostent
| getnameinfo
、getaddrinfo
|
| 网络 | /etc/networks | <netdb.h> | netent
| getnetbyname
、getnetbyaddr
|
| 协议 | /etc/protocols | <netdb.h> | protoent
| Getprotobyname
、getprotobynumber
|
| 服务 | /etc/services | <netdb.h> | servent
| getservbyname
、getservbyport
|
登录账户记录
大多数UNIX系统都提供下列两个数据文件:utmp文件记录当前登录到系统的各个用户;wtmp文件跟踪各个登录和注销事件。
系统标识
获取与主机和操作系统有关的信息:
#include <sys/utsname.h> int uname(struct utsname *name); //返回值:成功则返回非负值,出错则返回-1
获得主机名:
#include <unistd.h> int gethostname(char *name, int namelen); //返回值:成功则返回0,出错则返回-1
时间和日期例程
获取当前时间和日期(time_t
是日历时间,包括时间和日期):
#include <time.h> time_t time(time_t *calptr); //返回值:成功则返回时间值,出错则返回-1 //若calptr非空则时间值也存放于其指向的单元内
获取指定时钟的时间(时钟通过clockid_t
类型进行标识):
#include <sys/time.h> int clock_gettime(clockid_t clock_id, struct timespec *tsp); //返回值:成功则返回0,出错则返回-1 //返回的时间值存放于tsp指向的单元内
获取与时钟同精度的时间:
#include <sys/time.h> int clock_getres(clockid_t clock_id, struct timespec *tsp); //返回值:成功则返回0,出错则返回-1
对特定的时钟设置时间:
#include <sys/time.h> int clock_settime(clockid_t clock_id, const struct timespec *tsp); //返回值:成功则返回0,出错则返回-1 //将tsp指向的单元内的值设置为时间值
获取当前时间(日期与时间,精确到微秒,SUSv4指定其已弃用):
#include <sys/time.h> int gettimeofday(struct timeval *restrict tp, void *restrict tzp); //返回值:总是返回0 //结果存放于tp指向的单元内 //tzp唯一合法值是NULL,某些平台支持用其说明时区
将日历时间转换成分解的时间(结构tm
用来存放分解时间):
#include <time.h> struct tm *gmtime(const time_t *calptr);//转换为协调统一时间 struct tm *localtime(const time_t *calptr);//转换为本地时间(考虑到本地时区和夏令时标志) //返回值:指向分解的tm结构实体,出错则返回NULL
将分解时间变换成日历时间即time_t
值(以本地时间的年、月、日作参数):
#include <time.h> time_t mktime(struct tm *tmptr); //返回值:成功则返回日历时间,出错则返回-1
通过分解时间来产生格式化时间字符串:
#include <time.h> 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
通过格式化时间字符串来产生分解时间:
#include <time.h> char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tmptr); //返回值:指向上次解析的字符的下一个字符的指针,否则返回NULL
7 进程环境
main函数
main
函数的原型(C程序总是从main
函数开始运行):
int main(int argc, char *argv[]); //argc是命令行参数的数目,argv是指向参数的各个指针所构成的数组
进程终止
共8种方式可以使得进程终止(前5种是正常终止):
- 从
main
返回; - 调用
exit
; - 调用
_exit
或_Exit
; - 最后一个线程从其启动例程返回;
- 从最后一个线程调用
pthread_exit
; - 调用
abort
; - 接到一个信号;
- 最后一个线程对取消请求做出响应。
退出函数(_exit
和_Exit
立即进入内核,exit
则先执行一些清理处理):
#include <stdlib.h> void exit(int status);//总是执行一个标准I/O库的清理关闭操作:对于所有打开流调用fclose函数,在main函数中exit(0)即return(0) void _Exit(int status); #include <unistd.h> void _exit(int status); //status是终止状态
一个进程可以登记多至32个函数用来在调用exit
时倒序自动调用:
#include <stdlib.h> int atexit(void (*func)(void)); //返回值:成功则返回0,出错则返回非0
命令行参数
ISO C和POSIX.1都要求argv[argc]
是一个空指针,而argv[0]
即运行的程序名。
环境表
每个程序都接收到一张环境表,是一个字符指针数组,用于存放环境变量。
C程序的存储空间布局
地址 | 分布 | 说明 |
---|---|---|
高地址 | 命令行参数和环境变量 | |
栈 | ||
…………………… | ||
堆 | ||
未初始化的数据(bss) | 由exec 初始化为0 | |
初始化的数据 | 由exec 从程序文件中读入 | |
低地址 | 正文 | 由exec 从程序文件中读入 |
共享库
共享库使得可执行文件中不再需要包含公用的库函数,而只需在所有进程都可引用的存储区中保存这种库例程的一个副本。
程序第一次执行或调用某个库函数时,用动态链接方法将程序与共享库函数相链接,可以减少执行文件长度,不过会增加一些运行时间开销,库函数在版本迭代后无需对使用该库的程序重新连接编辑。
存储空间分配
管理存储空间:
#include <stdlib.h> void *malloc(size_t size);//分配字节数为size大小的存储区(其中初值不确定) void *calloc(size_t nobj, size_t size);//分配数量为nobj、字节数为size大小的存储区(每一位都初始化为0) void *realloc(void *ptr, size_t newsize);//减少或增加之前分配的存储区长度(字节数为newsize) //返回值:成功则返回非空指针,出错则返回NULL //动态分配内存 void free(void *ptr);//释放ptr指向的存储空间
忘记释放存储空间会导致内存泄漏。
环境变量
取环境变量值:
#include <stdlib.h> char *getenv(const char *name); //返回值:指向与name关联的value指针,未找到则返回NULL
设置或删除环境变量:
#include <stdlib.h> int putenv(char *str);//取形式为name=value的字符串,将其放入环境表 //返回值:成功则返回0,出错则返回非0 int setenv(const char *name, const char *value, int rewrite);//将name设置为value,若rewrite非0则删除现有定义、为0则不删除现有定义 int unsetenv(const char *name);//删除name的定义(不存在也不出错) //返回值:成功则返回0,出错则返回-1
函数setjmp和longjmp
跨越函数的跳转:
#include <setjmp.h> int setjmp(jmp_buf env);//设置跳转点(必须先于longjmp运行),返回longjmp中的val值 //返回值:直接调用则返回0,从longjmp返回则为非0 void longjmp(jmp_buf env, int val);//跳转至先前setjmp处,使其返回val值
函数getrlimit和setrlimit
对进程的资源限制进行查询和修改:
#include <sys/resource.h> int getrlimit(int resource, struct rlimit *rlptr);//获取资源限制 int setrlimit(int resource, const struct rlimit *rlptr);//设置资源限制 //返回值:成功则返回0,出错则返回非0