2017-2018-1 20155326 《信息安全系统设计基础》第四周学习总结及myod改进版的补交

学习内容

  • 补充完成课上没有完成的内容

  • 学习教材附录A,第十章内容

  • 参考别出心裁的Linux系统调用学习法,学习视频,掌握两个重要命令:

    • man -k key1 | grep key2| grep 2 : 根据关键字检索系统调用

    • grep -nr XXX /usr/include :查找宏定义,类型定义

  • 完成head,tail的使用,相关API的分析,伪代码,产品代码,测试代码的编写(3分)

别出心裁的Linux系统调用学习法学习总结

输入 man -k system |grep call 得到调用的相关信息。

由图可知我们应该先查看intro 输入 man 2 intro。

得到system calls的介绍后,提示我们找syscalls(2)。输入man 2 syscalls 查看Linux kernel的系统调用。

在娄老师发的教程指导学习中,我们通过三个步骤来学习:

  • 首先分析程序

分析一个东西首先要了解它的功能,使用cheat命令可以通过一个个的例子展示命令的功能。由于我的虚拟机没有安装Python,cheat,所以我首先进行了安装。

安装完cheat后我们可以输入 cheat find 来得到find的功能例子。

我们想要分析who的用法,于是我们输入cheat who 来更好的理解who命令,遗憾的是并没有who的例子说明,于是我们转而输入man who 来查看who命令的功能。

根据帮助文档我们知道了who命令的功能是输出登录信息:登录者的姓名、机器名称和登录时间。

  • 之后我们学习它的系统调用

在下图中,我们得知存在一个/var/run/utmp文件,这个文件保存当前用户的登录信息。

我们进一步分析/var/run/utmp,输入 cat /var/run/utmp查看里面的信息,由此我们可以得知这是一个二进制文件,可以通过od -tx1 /var/run/utmp 来查看其中内容。

接着我们使用man -k查看我们需要找的utmp的属性信息,找到了要找的utmp(5)。

输入man 5 utmp 查看帮助手册,在帮助手册中我们看到了实现utmp功能的代码,其中有两个结构体功能,用于记录登陆信息。这下我们知道了who功能的实现是依靠读取utmp文件中的信息。

使用grep -nr "struct utmp" /usr/include来查找struct utmp在哪个头文件中被定义。查找结果如下:

  • 最后进行编程实现

利用学到的原理和系统调用,自己编程实现原来程序所实现的功能。

编写代码的三部曲是:

1) 伪代码:

	打开utmp文件
	循环读每一条记录
	打印用户名、终端和时间
	关闭utmp文件,结束

2)产品代码

3) 测试代码

教材学习中遇到的问题

运行书上代码时发现问题。见下图:

参考网址https://group.cnblogs.com/topic/73278.html后我得知,这是作者自己写的一个头文件,需要拷到电脑里。

然后发现放的地方不对,要放在usr/lib里。放的时候不能直接放,需要用命令行将csapp.c/.h复制到lib。参考网址http://www.linuxidc.com/Linux/2008-11/17179.htm

移进后仍然错误

将.c.h文件放到lib以后还是不行

移到include以后也不对。

head的实现

  • 首先分析head

由此大概可知head的功能是显示一个文件前十行的内容。

再接着查看帮助手册,进一步确定了它的功能。

  • 系统调用

由于这个命令较简单我们并不需要学习系统调用便可直接实现他的功能。

  • 最后进行编程实现

    1. 伪代码:

      打开自定义(lmc.txt)文件
      循环打印出每行内容直至第十行
      关闭文件,结束

    2)产品代码

      filename=argv[1];
      rfd=open(argv[1],O_RDONLY,0);
      if((rlen=read(rfd,rbuf,READSIZE))>0)
      {
              for(i=0;i<rlen;i++)
      	{
      		if(rbuf[i]=='\n')
      		{
      			pb=&rbuf[i];
      			if(++nent==10)
      			break;
      		}
      	}
      }
      close(rfd);	
      for(j=0;j<=i;j++)
      {
    
      	printf("%c",rbuf[j]);
      }
      }
      }	
    
  • head编译过程中遇到的问题

最开始我编译运行时,没有任何返回的值,为了查看错误出在哪里,我在for循环出设置了printf来查看循环是否起作用。

这样看来循环是正确的。

于是我在每个if、循环处都设置了printf,从变量的值来查看哪个模块出了问题。

从图中看那些变量都没问题,每个模块都正常,唯独最后一行显示4196719。查看代码得出这是printf("%s",rbuf);的结果,然而我需要输出的是后十行内容,这样明显是错误的。

将输出换成一个for循环后运行终于正确了。

(head码云链接)[https://gitee.com/lmc1998/lmc20155326/tree/master/src/week4]

tail的实现

  • 首先分析tail

由此大概可知tail的功能和head相反,是显示一个文件后十行的内容。

再接着查看帮助手册,确定它的功能。

  • 系统调用

由于这个命令较简单我们并不需要学习系统调用便可直接实现他的功能。

  • 最后进行编程实现

    1. 伪代码:

      打开自定义(lmc.txt)文件
      找到文件后十行
      循环打印出每行内容直至结束
      关闭文件,结束

    2)产品代码

tail代码和head差不多,只是改了循环中的内容。

int main(int argc,char *argv[]){
    int rlen=0;  //wenjianneirongchangdu
    int nline=10;  //duquwenjianhangshu
    int nent=0;  //jishuqi
    int rfd; //wenjianmiaoshufu
    int i,j,m;
    char rbuf[READSIZE];  //duquwenjianhuancunqu
    char *filename;
    char *pb=0;  //zhixiangmeihanghuanhang
    memset(rbuf,0,READSIZE);
    filename=argv[1];
    rfd=open(argv[1],O_RDONLY,0);
    if((rlen=read(rfd,rbuf,READSIZE))>0)
	{
            for(i=0;i<rlen;i++)
                    {
                           if(rbuf[i]=='\n')
                           {
                                    pb=&rbuf[i];
									nent++;
                                    if(++nent==10)
                                      m=i;
                            }
                    }
            close(rfd);
    for(j=m;j<=rlen;j++)
	{

		printf("%c",rbuf[j]);
	}
	}
}
  • tail编译过程中遇到的问题

才开始的时候,我想当然的将第二个for循环的f=i换成了i-10。运行出来结果如下:

仔细思考过后我发现我太蠢了,j怎么能从i-10开始呢,rbuf[]是个字符串数组,储存打开的文件的所有内容。应该是从最后十行的开始的i开始输出。于是定义了一个新的变量m记录最后十行开始的i的值。

	for(i=0;i<rlen;i++)
    {
           if(rbuf[i]=='\n')
           {
                   pb=&rbuf[i];
				   nent++;
	//	    		if((read(rfd)
                    if(++nent==10)
                        m=i;
	//             break;

            }
     }
    close(rfd);
    for(j=m;j<=rlen;j++)
	{

		printf("%c",rbuf[j]);
	}

编译成功图片

(tail码云链接)[https://gitee.com/lmc1998/lmc20155326/tree/master/src/week4]

myod改进版

题目

1 参考教材第十章内容

2 用Linux IO相关系统调用编写myod.c 用myod XXX实现Linux下od -tx -tc XXX的功能,注意XXX是文件名,通过命令行传入,不要让用户输入文件名

3 不要把代码都写入main函数中

4 要分模块,不要把代码都写入一个.c中

实践过程:

这次主要运用了第十章的知识,根据题目,可以得知需要myod1.0版本改进的地方分别是:

  • 将主函数改为调用参数形式,即int main(int argc,char * argv[]), 其中argc是命令行总的参数个数 , * argv[]:是字符串数组,用来存放指向你的字符串参数的指针数组,每一个元素指向一个参数。

    argv[0]:指向程序的全路径名
    argv[1]:指向在DOS命令行中执行程序名后的第一个字符串。
    argv[2]:指向第二个字符串,以此类推。

  • 由于文件名从命令行获得,所以不用在程序中输入文件名啦,去掉*fp 文件指针。

  • 根据第十章内容,open函数是linux中非常方便的按照给定路径打开文件的函数。可建立一个整型变量用于取得文件打开后的返回值,即int fd。以只读方式打开已存在文件则有fd=open(argv[1],O_RDONLY),当open()返回值为-1时打开文件失败。close()函数关闭文件即可。

  • 接着对头文件进行修改

代码修改如下图:

对代码的修改基本都在主函数部分。

运行结果如下:

myod码云链接

总结

这次学习了第十章的内容,对I/O有了更深的理解。也知道了原来自己也可以编写linux里面的命令。但是觉得自己能力还是太弱,一步步加强吧。