UNIX环境高级编程 第6章 系统数据文件和信息
UNIX系统的正常运作需要用到大量与系统有关的数据文件,例如系统用户账号、用户密码、用户组等文件。出于历史原因,这些数据文件都是ASCII文本文件,并且使用标准I/O库函数来读取。
口令文件
/etc/passwd文件是UNIX安全的关键文件之一。该文件用于用户登录时校验用户的口令,文件中每行的一般格式为:
用户名:x:用户ID:用户组ID:说明信息:个人主目录:SHELL
对于第二项x来说是密码,但由于安全原因密码已经被移至其他文件,因此使用x来代替。
UNIX系统提供了两个用于获取passwd文件中条目的函数,在给出用户名或者用户ID之后,这两个函数即可查看相关信息。其头文件及函数原型如下:
#include <pwd.h> struct passwd* getpwuid(uid_t __uid);
struct passwd* getpwnam(const char *__name);
函数成功是返回相应指针,出错返回NULL。第一个函数使用用户UID,第二个函数使用用户名。返回的指针指向一个静态变量,因此只要再调用任一相关的函数就会改写指针所指向的变量内容。
上面两个函数一次只能查看一个,而且必须提供用户名或者用户UID,如果想要查看所有账户信息或者事先不了解用户名及用户UID,则可以通过下面几个函数来查看。其头文件及函数原型如下:
#include <pwd.h> struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);
getpwent( )函数用于读取passwd文件,setpwent( )用于设置从passwd文件头部开始读,endpwent( )用于关闭文件。
阴影口令
阴影口令文件/etc/shadow用于存储加密后的密码,该密码加密方式是单向不可逆。shadow文件普通用户不允许读取,访问shadow文件的一组函数与访问passwd文件函数类似。其头文件及函数原型如下:
#include <shadow.h> struct spwd *getspnam(const char *__name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
函数成功是返回相应指针,出错返回NULL。
组文件
组文件/etc/group包含了系统用户组相关的信息,UNIX系统提供了两个函数来查看。其头文件及函数原型如下:
#include <grp.h> struct group *getgrgid (gid_t __gid);
struct group *getgrnam (const char *__name);
函数成功是返回相应指针,出错返回NULL。
如果需要搜索整个组文件,则类似passwd文件函数。其头文件及函数原型如下:
#include <grp.h> struct group *getgrent(void);
extern void setgrent(void);
extern void endgrent(void);
附属组ID
现有的UNIX都支持一个用户加入多个组,对于除了创建用户时产生的自带用户组之外,用户额外加入的其他组则为附属组。为了获取和设置附加组ID,UNIX提供了3个函数。其头文件及函数原型如下:
#include <unistd.h>
#include <grp.h> int getgroups (int __size, gid_t __list[]);
int setgroups (size_t __n, const gid_t *__groups);
int initgroups (const char *__user, gid_t __group);
登录账户记录
UNIX系统还提供了一些事件簿记功能,该功能可以查看当前登录到系统的各个用户,还能跟踪登录注销等事件。UNIX中利用一个二进制结构来填写记录。登录时,login程序将信息写入到utmp文件和wtmp文件中。注销时,init进程将utmp文件中的记录移除,并添加一个新记录到wtmp文件中。
系统标识
UNIX系统提供了一个函数用于返回与主机和操作系统有关的信息。其头文件及函数原型如下:
#include <sys/utsname.h> int uname(struct utsname *__name);
函数成功时返回非负值,失败返回-1。
时间和日期例程
正如第一章所述,UNIX为运行在它之上的程序提供各种服务,其中就包括提供时间这一服务。UNIX系统提供的时间服务是计算自协调世界时1970年1月1日以来所经过的秒数,该秒数使用time_t类型表示。在UNIX系统中,可以使用time( )函数来返回当前日期和时间。其头文件及函数原型如下:
#include <time.h> time_t time (time_t *__timer);
函数成功返回时间值,失败返回-1。如果__timer指针非空,那么__timer指针所指的对象也被设置为相应的值。
time( )函数只能用来获取当前时间,UNIX系统还提供了一个函数用于获取指定时钟开始之后的秒数。其头文件及函数原型如下:
#include <time.h> int clock_gettime (clockid_t __clock_id, struct timespec *__tp);
函数成功时返回0,失败返回-1。该函数可以用来计时,比如从当前程序开始执行开始到程序停止执行为止统计总计花费了多少时间。
当我们得到时间之后,这个时间是一个自1970年以来经过的秒数,对人来说它无意义,必须转换为人类可读的时间。转换又分为两步,第一步是转换为年月日时分秒,第二步是格式化输出。如果仅仅用于程序的计算,那么第一步转换已经满足,但为了打印输出让人可读,还必须进行格式化输出。UNIX系统提供了两个函数用于从time_t转换为年月日时分秒的时间结构类型,提供了一个逆向从年月日时分秒的时间结构类型转到time_t的函数,还提供了两个格式化输出的函数。其头文件及函数原型如下:
#include <time.h> struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
time_t mktime(struct tm *tp);
size_t strftime(char* s, size_t maxsize, const char* format, const struct tm* tp);
size_t strftime_l(char* s, size_t maxsize, const char* format, const struct tm* tp, locale_t loc);
对于前两个函数,成功返回相应指针,失败返回NULL;对于第三个函数,成功返回时间,失败返回-1;对于最后两个函数,成功返回字符数,失败返回0。
习题
6.1 如果系统使用阴影文件,那么如何取得加密口令?
无法获取,因为shadow文件只允许root用户访问,普通用户无法取得。
6.2 假设你有超级用户权限,并且系统使用了阴影口令,重新考虑上一道习题。
#include <iostream>
#include <shadow.h> int main (int argc, char *argv[])
{
string name;
cout << "input your user name: ";
cin >> name;
struct spwd *ptr = nullptr;
ptr = getspnam(name.c_str()); if (ptr)
{
cout << ptr->sp_pwdp << endl;
} return ;
}
代码编译后,使用root权限运行。
6.3 编写一程序,它调用uname并输出utsname结构中的所有字段,将该输出与uname(1)命令的输出结果进行比较。
#include <sys/utsname.h>
#include <iostream> using namespace std; int main (int argc, char *argv[])
{
utsname p;
if (uname(&p) >= )
{
cout << p.sysname << ' '
<< p.nodename << ' '
<< p.release << ' '
<< p.version << ' '
<< p.machine << endl;
}
return ;
}
与uname(1)命令的输出结果相比少三个输出:处理器类型、硬件平台、操作系统。因为UNIX系统并不负责定义硬件及具体实现,UNIX只定义标准规范。
6.4 计算可由time_t数据类型表示的最近时间。如果超出了这一时间将会如何?
C++11标准规定long类型最少占32位,在我的计算机上,系统使用long int来实现time_t,实际使用64位来表示long类型,因此其取值值范围为 -9223372036854775808~9223372036854775807,由于该值特别大,2900亿年后才会溢出,此时宇宙可能都不存在了。对于某些32位系统或者旧的程序,它们的time_t类型是使用32位int来实现的,而int取值范围为-2147483648~2147483647,我们可以利用localtime( )函数来分解该值,并用strftime( )函数来打印,程序如下:
#include <iostream>
#include <climits>
#include <ctime> int main (int argc, char *argv[])
{
time_t tm_t = INT_MAX;
tm* tmp = nullptr;
char buf[] = {};
if (tmp = localtime(&tm_t))
{
strftime(buf, , "%Y/%m/%e %H:%M:%S \n", tmp);
}
std::cout << buf << std::endl; return ;
}
程序运行结果如下:

由于我的本地北京时间有8个小时的时差提前,因此真正的溢出时间为2038年1月19日03:14:07。如果到这一天将会溢出,由于int是有符号数,而C和C++标准都对有符号数溢出行为未定义,因此程序的行为无法预计,可能崩溃,可能复位,可能溢出后是个无法估计的值。下面是个动态演示图:

2038年问题演示:第一行是“time_t”数字的二进制表示;第二行是其十进制表示;第三行是受影响的计算机理解的时间;第四行是实际的时间。
上面图片来自wikipedia.org。
6.5 编写一程序,获取当前时间,并使用strftime将输出结果转换为类似于date(1)命令的默认输出。将环境变量TZ设置为不同值,观察输出结果。
#include <iostream>
#include <climits>
#include <ctime> int main (int argc, char *argv[])
{
time_t tm_t = time(nullptr);
tm* tmp = nullptr;
char buf[] = {};
if (tmp = localtime(&tm_t))
{
strftime(buf, , "%Y年 %m月 %e日 星期%u %H:%M:%S %Z\n", tmp);
}
std::cout << "\n个人程序:";
std::cout << '\n' << buf << std::endl; return ;
}
编译代码后在不同时区执行结果如下:
Africa Abidjan:

America Adak:

Asia Shanghai:

UNIX环境高级编程 第6章 系统数据文件和信息的更多相关文章
- UNIX系统高级编程——第六章-系统数据文件和信息-总结
口令文件: /* The passwd structure. */ struct passwd { char *pw_name; /* Username. */ char *pw_passwd; /* ...
- apue学习笔记(第六章 系统数据文件和信息)
UNIX系统的正常运作需要使用大量与系统有关的数据文件,例如,口令文件/etc/passwd和组文件/etc/group就是经常被多个程序频繁使用的两个文件. 口令文件 UNIX系统口令文件包含如下字 ...
- UNIX环境高级编程 第13章 守护进程
守护进程daemon是一种生存周期很长的进程.它们通常在系统引导时启动,在系统关闭时终止.守护进程是没有终端的,它们一直在后台运行. 守护进程的特征 在Linux系统中,可以通过命令 ps -efj ...
- UNIX环境高级编程 第5章 标准I/O库
本章是关于C语言标准I/O库的,之所以在UNIX类系统的编程中会介绍C语言标准库,主要是因为UNIX和C之间具有密不可分的关系.由于UNIX系统存在很多实现,而每个实现都有自己的标准I/O库,为了统一 ...
- UNIX环境高级编程 第16章 网络IPC:套接字
上一章(15章)中介绍了UNIX系统所提供的多种经典进程间通信机制(IPC):管道PIPE.命名管道FIFO.消息队列Message Queue.信号量Semaphore.共享内存Shared Mem ...
- unix环境高级编程第六章笔记
口令文件 阴影口令 组文件 附属组ID 登录账户记录 系统标识 口令文件<\h2> /etc/passwd文件是UNIX安全的关键文件之一.该文件用于用户登录时校验用户的口令,文件中每行的 ...
- UNIX环境高级编程 第8章 进程控制
本章是UNIX系统中进程控制原语,包括进程创建.执行新程序.进程终止,另外还会对进程的属性加以说明,包括进程ID.实际/有效用户ID. 进程标识 每个进程某一时刻在系统中都是独一无二的,它们之间是用一 ...
- UNIX环境高级编程 第7章 进程环境
本章涉及C/C++程序中main函数是如何被调用的.命令行参数如何传递给main函数.程序的内存空间布局.程序如何使用环境变量.程序如何终止退出. main函数 C程序或C++程序总是从main函数开 ...
- UNIX环境高级编程 第9章 进程关系
在第8章学习了进程的控制原语,通过各种进程原语可以对进程进行控制,包括新建进程.执行新程序.终止进程等.在使用fork( )产生新进程后,就出现了进程父子进程的概念,这是进程间的关系.本章更加详细地说 ...
随机推荐
- 图片上传预览,兼容IE
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HDU3452_Bonsai
题目的意思是给你一个棵树,每天边上有一个权值,现在要想根节点和每个叶子节点完全隔离开来,删除一些边,求最少需要删除的边权值综合是多少? 直接建模,以根节点为汇点,每个叶子节点连接虚拟源点流量无穷,树上 ...
- Nastya Studies Informatics CodeForces - 992B(增长姿势)
有增长姿势了 如果a * b == lcm * gcd 那么a和b为lcm因数 这个我之前真不知道emm... #include <bits/stdc++.h> #define mem( ...
- libuv 简单使用
libuv 简单使用 来源:https://zhuanlan.zhihu.com/p/50497450 前序:说说为啥要研究libuv,其实在很久之前(大概2年前吧)玩nodejs的时候就对这个核心库 ...
- 超计算(Hyper computation)模型
超计算(Hyper computation)模型 作者:Xyan Xcllet链接:https://www.zhihu.com/question/21579465/answer/106995708来源 ...
- 【BZOJ3534】重建(矩阵树定理)
[BZOJ3534]重建(矩阵树定理) 题面 BZOJ 洛谷 题解 这.... 矩阵树定理神仙用法???? #include<iostream> #include<cmath> ...
- 解题:POI 2008 Plot purchase
题面 原来看过然后没做,结果板板把这道题改了改考掉了,血亏=.= 首先看看有没有符合条件的点.如果没有开始寻找解,先把所有的大于$2*k$的点设为坏点,然后求最大子矩形,只要一个最大子矩形的权值和超过 ...
- C#线程篇---线程池如何管理线程(6完结篇)
C#线程基础在前几篇博文中都介绍了,现在最后来挖掘一下线程池的管理机制,也算为这个线程基础做个完结. 我们现在都知道了,线程池线程分为工作者线程和I/O线程,他们是怎么管理的? 对于Microsoft ...
- OpenCV入门指南----人脸检测
本篇介绍图像处理与模式识别中最热门的一个领域——人脸检测(人脸识别).人脸检测可以说是学术界的宠儿,在不少EI,SCI高级别论文都能看到它的身影.甚至很多高校学生的毕业设计都会涉及到人脸检测.当然人脸 ...
- 如何在servlet的监听器中使用spring容器的bean
另外补充下:在web Server容器中,无论是Servlet,Filter,还是Listener都不是Spring容器管理的,因此我们都无法在这些类中直接使用Spring注解的方式来注入我们需要的对 ...