热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Unix环境高级程序设计入门----Unix环境及系统数据信息使用

Unix环境高级程序

                                                                                          Unix环境高级程序设计入门
                                                                            ----Unix环境及系统数据信息使用
  

     Unix环境高级程序设计是一种难度较大的编程技术,它在软件开发领域的用途十分广泛。目前虽处于“曲高和寡”的状况,但却是程序员不断进阶的“利器”。笔者愿将个人对此的学习所得,分期总结整理后与有志深造者共享互助,携手前行。这一系列文章拟从整体上有着连贯性,而各篇又有相对独立性。文章中若涉及到前已发布的《Unix操作系统的入门与基础》、《Unix的轻巧“约取而实得”》中已解释的知识点,将不再赘述。读者阅读中如果遇到还不甚明了的概念,可以查阅本专栏中的上述文章。同时,本系列文章假设读者已熟悉C++语法,故不对涉及C++语法方面的知识进行解释。文中的代码已在SunOS 5.8中测试通过。

                                                                                一、在程序中使用环境变量

     我们已经知道,环境变量可以用于定制用户的工作环境,即用环境变量可以保存用户对系统进行设置的信息。要想查看系统已定义的所有环境变量,可以使用setenv命令。那么,如何在程序中操作环境变量呢?
     在系统为每个程序分配的空间中,都会自动保存一份系统环境变量的拷贝。ANSI C中定义的两个函数getenv()和petenv(),分别可以用于获取环境变量的值和设置环境变量的值。这两个函数的定义分别是:

     #include
     char* getenv(const char* name); //成功返回指向环境变量值的指针,失败返回NULL
     int putenv(const char* str);    //成功返回0,失败返回-1

我们通常使用getenv()从环境中获取一个指定的环境变量的值,使用putenv()来改变现有环境变量的值,或增加新的环境变量。putenv()参数形式为“name = value”的字符串,如果name指定的环境变量已经存在,则先删除其原来的定义再赋新值;如果不存在,则增加此环境变量。
     现来看下面的一个例程序:
     [程序1]

   #include
   #include
   using namespace std;

   int main()
   {
     char* env = new char[255];
     char* a = getenv("ABCDE");
     if(a!=NULL)
       strcpy(env,a);
     cout <<"env: " <     if(putenv("ABCDE=aaa")<0)
       exit(-1);
     strcpy(env,getenv("ABCDE"));
     cout <<"--------------------" <     cout <<"env: " <     delete env;
     return 0;
   }

     运行此程序,将会在程序空间的环境变量表中增加一个新的环境变量ABCDE,其值为aaa。请注意,程序是在系统为其所分配空间的环境变量表中增加环境变量,而不是在系统的环境变量表中增加环境变量。
   
                                                                               
二、获取系统相关信息

     在Unix系统中定义的uname()函数,可以返回与主机和操作系统相关的信息。uname()的定义是:
 
     #include
     int uname(struct utsname* name);  //成功返回非负值,出错返回-1

     通过参数向其传递一个utsname结构的地址,uname函数将负责填写此结构。utsname结构定义如下:
   
   struct utsname {
       char sysname[9];  // name of the operating system
     char nodename[9];  // name of this node
     char release[9];  // current release of operating system
     char version[9];  // current version of this release
     char machine[9];  // name of hardware type
   };  

     utsname结构中的信息可用“uname -a”命令来显示,在程序中则可以如下方式来查看。
     [程序2]
   
     #include
   #include
   using namespace std;
 
   #define ERR_QUIT(arg) {cout <<#arg < 
   int main()
   {
     struct utsname buf;
     if(uname(&buf) <0) ERR_QUIT("uname error");
     cout <<"Operation System Name: " <     cout <<"Node Name          : " <     cout <<"O.S. release level   : " <     cout <<"O.S. Version level   : " <     cout <<"Hardware type        : " <     return 0; 
   }

     我们曾经介绍过使用hostname命令来获取主机名,而要在程序中得到主机名,则可以调用Unix系统提供的gethostname()函数,其实hostname命令就是调用了gethostname()函数。

   #include
   int gethostname(char* name, int namelen); //成功返回0,失败返回-1
 
     [程序3]
   
   #include
   #include
   #include
   using namespace std;
 
   #define ERR_QUIT(arg) {cout <<#arg <

   int main()
   {
     char buf[256];
     if(gethostname(buf,255)<0)
     {
       cout <<"ERR: " <       ERR_QUIT("gethostname error.");
     }
     cout <<"HostName is : " <     return 0;
   }


                                                                                     三、获取用户相关信息

     用户每次都需凭一个经系统确认的用户名登录系统,而每个用户名在系统中将对应一个惟一的用户ID。用户ID是一个数值,它用于向系统标识各个不同的用户。在程序中如果想要获得用户的登录名以及用户ID号,可以使用如下的函数:

     #include
     char* getlogin();   //得到用户登录名
     int getuid();       //得到当前登录用户的用户ID号
     int geteuid();      //得到当前运行该进程的有效用户ID号
     struct passwd* getpwuid(int userid); //得到一个指向passwd结构的指针,该结构中包含用户相关信息的记录
     passwd的结构如下表所示:
     

说明
struct passwd成员
用户名
char* pw_name
密码
char* pw_passwd
用户ID
uid_t pw_uid
组ID
gid_t pw_gid
注释
char* pw_gecos
工作目录
char* pw_dir
初始shell
char* pw_shell

    
     当用户登录后,系统会分配给每个用户一个组ID,它也是一个数值。一般来说,在Unix中的组可被用于将若干用户集合到某个课题或部门中去,这种机制允许同组的各个成员之间共享资源(例如文件)。当然,一个用户可以参加多个课题或项目,因此也就可以同时属于多个组。在程序中如果想要获得用户所属的组ID号,可以使用如下的函数:

     #include
     int getgid();   //得到当前登录用户的组ID号
     int getegid();  //得到当前运行该进程的有效用户的组ID号
     struct group* getpgrgid(int groupid);  //得到一个指向group结构的指针,该结构中包含用户组相关信息的记录
     group的结构如下表所示:
     

说明
struct group成员
组名
char* gr_name
密码
char* gr_passwd
组ID
int gr_gid
指向各用户名的指针数组(其中各指针指向该组的用户名,数组以null结尾)
char** gr_mem
    
     来看下面的例程序:
     [程序4]
    
  #include
  #include
  #include
  #include
  using namespace std;
  int main()
  {
      struct passwd* pwd;
      cout <<"Login name: " <      pwd = getpwuid(getuid());
      if(pwd)
      cout <<"Real user: " <pw_name <<")" <      pwd = getpwuid(geteuid());
      if(pwd)
          cout <<"Effective user: " <pw_name <         
      struct group* grp;
      grp = getgrgid(getgid());
      if(grp)
      cout <<"Real group: " <gr_name <<")" <    grp = getgrgid(getegid());
      if(grp)
      cout <<"Effective group: " <gr_name < }
 
  通常情况下,有效用户ID等于登录用户ID,有效组ID等于登录用户组ID。但是如果某一文件的拥有者通过命令“chmod u+s filename”设置了一个特殊标志位,则任何运行此文件的用户有效ID都将变为该程序拥有者的ID。这一特殊标志位被称为设置-用户-ID(set-user-ID)位,其定义是“当执行此文件时,将进程的有效用户ID设置为文件的所有者”。类似的,还可以通过设置使得执行此文件的进程有效组ID变为文件所有者的组ID。
 
                                                                                      
四、时间与日期的使用

     由Unix内核提供的基本时间服务是自1970年1月1日00:00:00以来国际标准时间(UTC)所经过的秒数累计值,通常被称为日历时间。日历时间包括时间和日期,这种秒数是以数据类型time_t来表示的(在Solaris中time_t等同于long)。我们可以使用time()函数来获得表示当前时间和日期的秒值。time()函数的定义是:

     #include
     time_t time(time_t* mem);  //成功则返回时间值,出错返回-1;如果参数非NULL,则时间值也存放在由mem指向的单元内

     因此,time()函数常有如下两种使用方法:
     (1) time_t now1;
            time(&now1);
            cout <     (2) time_t now2;
             now2=time(NULL);
             cout <     [程序5]
   
   #include //#include
   #include
   using namespace std;
 
   int main()
   {
     time_t now;
     if(time(&now)<0)
     {
       cout <<"error" <       exit(-1);
     }
     cout <     cout <     exit(0);
   }
   
     由于这种秒值的表示方式,不能直观的让人们明白当前的时间,因此通常需要再调用其它的时间函数来将其转换为人们可读的时间与日期。图1说明了各种时间函数之间的关系。


                   (图1)
     函数localtime()和gmtime()可以将秒值转换成以年、月、日、时、分、秒、周日表示的时间,并将这些存放在一个tm结构中。tm结构定义如下:
   
   struct tm {
     int tm_sec;     /* seconds after the minute: [0, 61] */
     int tm_min;     /* minutes after the hour: [0, 59] */
     int tm_hour;    /* hours after midnight: [0, 23] */
     int tm_mday;    /* day of the month: [1, 31] */
     int tm_mon;     /* month of the year: [0, 11] */
     int tm_year;    /* years since 1900 */
     int tm_wday;    /* days since Sunday: [0, 6] */
     int tm_yday;    /* days since January 1: [0, 365] */
     int tm_isdst;   /* daylight saving time flag: <0, 0, >0 */
   };

     localtime()和gmtime()之间的区别是:localtime将日历时间转换成本地时间(考虑到本地时区和夏时制标志),而gmtime则将日历时间转换成国际标准时间的年、月、日、时、分、秒、周日。它们的定义如下:

     struct tm* gmtime(const time_t* mem);
     struct tm* localtime(const time_t* mem);

     函数mktime()则正好相反,它是以存放有本地时间年、月、日等的tm结构作为参数,将其转换成time_t类型的秒值。mktime()函数的定义是:

     time_t mktime(struct tm* tmptr);  //成功返回日历时间,失败则返回-1 

     函数asctime()和ctime()可以获得人们可读的时间字符串,表示形式如同使用date命令所获得的系统默认的时间输出形式。它们的定义如下:

     char* asctime(const struct tm* tmptr); //参数是指向存放有本地时间年、月、日等的tm结构的指针
     char* ctime(const time_t* mem);   //参数是指向日历时间的指针

     函数strftime()是最为复杂的时间函数,可用于用户自定义时间的表示形式。函数strftime()的定义如下:

     size_t strftime(char* buf, size_t maxsize, const char* format,
                       const struct tm* tmptr);  //有空间则返回所存入数组的字符数,否则为0

自定义格式的结果存放在一个长度为maxsize的buf数组中,如果buf数组长度足以存放格式化结果及一个null终止符,则该函数返回在buf数组中存放的字符数(不包括null终止符),否则该函数返回0。format参数用于控制自定义时间的表示格式,格式的定义是在百分号之后跟一个特定字符,format中的其他字符则按原样输出。其中特别应注意的是,两个连续的百分号则是表示输出一个百分号。常用的定义格式如下表所示。

格式
说明
例子
% a
缩写的周日名
Tue
% A
全周日名
Tuesday
% b
缩写的月名
Jan
% B
月全名
January
% c
日期和时间
Wed Aug 17 19:40:30 2005
% d
月日:[01, 31]
14
% H
小时(每天2 4小时):[00, 23]
19
% I
小时(上、下午各1 2小时[01, 12]
07
% j
年日:[001, 366]
014
% m
月:[01, 12]
01
% M
分:[00, 59]
40
% p
A M / P M
PM
% S
秒:[00, 61]
30
% U
星期日周数:[00, 53]
02
% w
周日:[ 0 =星期日,6 ]
2
% W
星期一周数:[00, 53]
02
% x
日期
08/17/05
% X
时间
19:40:30
% y
不带公元的年:[00, 991]
05
% Y
带公元的年
2005
% Z
时区名
MST

     下面再来看一个具体程序。在此程序中,如提供一个秒值作参数,则会依照自定义的格式输出日期与时间信息;如提供一个以年、月、日、时、分、秒为格式的参数,则会输出从1970年1月1日00:00:00以来国际标准时间(UTC)所经过的秒数累计值。
     [程序6]
   
   #include
   #include
   #include
   #include
   #include
   using namespace std;
 
   void usage(char * proc)
   {
     cout <<" Usage: 1." <     cout <<"        2." <   }  
 
   int main(int argc, char* argv[])
   {
     if(argc == 1)
     {
       usage(argv[0]);
       return EXIT_FAILURE;
     }
 
     if(argc == 2) //Case in 2
     {
       long transfer;
       transfer = atol(argv[1]);
       struct tm* now = NULL;
       now = localtime(&transfer);
   
       char timestamp[150];
       strftime(timestamp,sizeof(timestamp),"%Y-%m-%d %H:%M:%S",now);
       strcat(timestamp," (format in: YYYY-MM-DD HH:MM:SS)");
       cout <       return EXIT_SUCCESS;
     }
     else //Case in 1
     {
       if(argc != 7)
       {
         usage(argv[0]);
         return EXIT_FAILURE;
       }
       try{
          struct tm* now = new struct tm;
          now->tm_year = atoi(argv[1]);
          now->tm_mon = atoi(argv[2]);
          now->tm_mday = atoi(argv[3]);
          now->tm_hour = atoi(argv[4]);
          now->tm_min = atoi(argv[5]);
          now->tm_sec = atoi(argv[6]);
    
          //Adjust
          now->tm_mon--;
          now->tm_year -= 1900;
    
          cout <          delete now; 
       }catch(logic_error& e) {
          cout <       }
       return EXIT_SUCCESS;
     }
     return EXIT_SUCCESS;
   }

                                                                                 五、登录会计文件的使用

     Unix系统中提供了下列两个数据文件:
     (1)utmp文件,用于记录当前登录系统的各个用户,它被放于/var/adm/utmpx;
     (2)wtmp文件,用于跟踪所有登录与登出系统的事件,它被放于/var/adm/wtmpx。
     此外,还定义了一个结构体utmpx,其包含了用户登录与登出系统的相关信息。用户每次登录系统,系统会自动填写这一结构,然后将其写入到utmp文件中,同时也将其添写到wtmp文件中。登出时,系统会将utmp文件中相应的记录擦除(每个字节都填以0),并将一条新记录添写到wtmp文件中。另外,在系统再次启动以及更改系统时间和日期时,也都会在wtmp文件中添写特殊的记录项。
     上述两个数据文件为二进制文件,人们要是直接打开会看到一堆的乱码,但可以通过who命令读utmp文件并以可读格式输出其内容,使用last命令可以查看wtmp文件中所有的记录。而在自己编写的程序中,则可以使用相应的函数来查看数据文件的实用信息,其程序如:
   
     [程序7]
   
     #include
   #include
   #include
   #include
   using namespace std;

   #define trace(arg) cout <<#arg <<" = " <<(arg) <

   int main(int argc, char** argv)
   {
     if(argc == 2) {
       utmpxname(argv[1]);  //打开新的记录文件,请注意由argv[1]参数指定的文件名必须以x结尾
     }
     struct utmpx * tmp;
     tmp = getutxent();    //getutxent()可以从wtmpx中获得一行数据记录,即一条登录或登出事件
     while(tmp != NULL)
     {
           trace(tmp->ut_user);     //登录账号
       trace(tmp->ut_id);       //登录ID
       trace(tmp->ut_line);     //登录设备名,省略了“/dev/”
       trace(tmp->ut_pid);      //进程ID
       trace(tmp->ut_type);     //登录类型,7(或USER_PROCESS)代表登录,8(或DEAD_PROCESS)代表登出
      trace(tmp->ut_tv.tv_sec); //执行当前操作时的时间记录
      cout <<"---------------" <      tmp = getutxent(); 
      sleep(1);
     }
     return 0;
   }
    
       运行此程序,会自动读取wtmp文件中的信息,并依照我们所定义的格式输出。再来看一个复杂点的例程序。
     [程序8]

   #include
   #include
   #include
   #include
   using namespace std;

   void show(struct utmpx*& tmp)
   {
     cout <ut_user <     cout <ut_id <     cout <ut_line <     cout <ut_pid <     cout <ut_type <     cout <ut_tv.tv_sec <   }

   int main()
   {
     static int i=0;
     struct utmpx * tmp;
     struct utmpx * t = new utmpx;
     tmp = getutxent();
     while(tmp != NULL)
     {
       show(tmp);
       cout <<"______________" <       tmp = getutxent(); 
       sleep(1);
       if(++i == 7) break;
       if(i == 5)
       {
         cout <<"************** " <         memcpy(t,tmp,sizeof(struct utmpx));
         show(t); 
         cout <<"************** " <       }
     }
     struct utmpx* ll;
     setutxent();                        //将当前文件位移量置0,即从文件头部重新开始读第一条记录
     cout <<"************** " <     ll = getutxline(t);                 //找到与t的ut_user相同、ut_type也相同的第一条记录
     show(ll);
     cout <<"************** " <     while(1)
     {
       ll = getutxent();
       show(ll);
       if(ll->ut_type == USER_PROCESS || ll->ut_type == DEAD_PROCESS)
       {
         memcpy(t,ll,sizeof(struct utmpx));
         break;
       }
     }
     utmpxname("utmp.bakx");             //打开一个新的记录文件,请注意文件名必须以x结尾
     cout <<" the following will be write into .. \n";
     show(t);
     pututxline(t);                      //将t指向的记录写入新的记录文件
     endutxent();                        //关闭文件流
     delete t;
     return 0;
   }

    每个人都有打电话的经历,在我们拨打电话时会进入电信计费系统,一旦拨通电话则系统会记录登录时间,挂断电话则系统会记录退出时间,两时间差即为用户的通话时长。那么在每个月底将某一用户的通话记录搜索出来放入一个新的记录文件,并将其打印出来便是用户的每月通话账单了。因此,Unix的登录会计文件完全可以用于电信服务中的数据采集工作,电信的数据采集系统可能也正是采用了类似的方式。



推荐阅读
  • java解析json转Map前段时间在做json报文处理的时候,写了一个针对不同格式json转map的处理工具方法,总结记录如下:1、单节点单层级、单节点多层级json转mapim ... [详细]
  • 深入解析Java中的空指针异常及其预防策略
    空指针异常(NullPointerException,简称NPE)是Java编程中最常见的异常之一。尽管其成因显而易见,但开发人员往往容易忽视或未能及时采取措施。本文将详细介绍如何有效避免空指针异常,帮助开发者提升代码质量。 ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom&amp;quot;echarts&amp;quot;;4、如果用到map(地图),还 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • 本文介绍了如何在 Spring Boot 项目中使用 spring-boot-starter-quartz 组件实现定时任务,并将 cron 表达式存储在数据库中,以便动态调整任务执行频率。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 一个建表一个执行crud操作建表代码importandroid.content.Context;importandroid.database.sqlite.SQLiteDat ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
author-avatar
fdsafjlkjgklg_431
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有