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 getpwnamgetpwuid
/etc/group <grp.h> group getgrnamgetgrgid
阴影 /etc/shadow <shadow.h> spwd getspnam
主机 /etc/hosts <netdb.h> hostent getnameinfogetaddrinfo
网络 /etc/networks <netdb.h> netent getnetbynamegetnetbyaddr
协议 /etc/protocols <netdb.h> protoent Getprotobynamegetprotobynumber
服务 /etc/services <netdb.h> servent getservbynamegetservbyport

登录账户记录

大多数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种是正常终止):

  1. main返回;
  2. 调用exit
  3. 调用_exit_Exit
  4. 最后一个线程从其启动例程返回;
  5. 从最后一个线程调用pthread_exit
  6. 调用abort
  7. 接到一个信号;
  8. 最后一个线程对取消请求做出响应。

退出函数(_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
posted @ 2020-11-27 06:59  SIGMA711  阅读(114)  评论(0编辑  收藏  举报