2017-2018-1 20155208 《信息安全系统设计基础》第四周学习总结

学习任务

  • 参考教材第十章内容
  • 用Linux IO相关系统调用编写myod.c 用myod XXX实现Linux下od -tx -tc XXX的功能,注意XXX是文件名,通过命令行传入,不要让用户输入文件名
  • 不要把代码都写入main函数中
  • 要分模块,不要把代码都写入一个.c中
  • 提交测试代码和运行结果截图, 提交调试过程截图,要全屏,包含自己的学号信息
  • 课上上传代码到码云

Linux下od命令的学习

1、 od命令解释:

用于输出文件的八进制、十六进制或其它格式码的字节,通常用于显示或查看文件中,不能直接显示在终端的字符。 常见的文件为文本文件和二进制文件。此命令主要用来查看保存在二进制文件中的值。比如,程序可能输出大量的数据记录,每个数据是一个单精度浮点数。这些数据记录存放在一个文件中,如果想查看下这个数据,这时候od命令就派上用场了。在我看来,od命令主要用来格式化输出文件数据,即对文件中的数据进行无二义性的解释。不管是IEEE754格式的浮点数还是ASCII码,od命令都能按照需求输出它们的值。

2、参数

命令作用
-a 此参数的效果和同时指定"-ta"参数相同。
-A<字码基数> 选择要以何种基数计算字码。
-b 此参数的效果和同时指定"-toC"参数相同。
-c 此参数的效果和同时指定"-tC"参数相同。
-d 此参数的效果和同时指定"-tu2"参数相同。
-f 此参数的效果和同时指定"-tfF"参数相同。
-h 此参数的效果和同时指定"-tx2"参数相同。
-i 此参数的效果和同时指定"-td2"参数相同。
-j<字符数目>或--skip-bytes=<字符数目> 略过设置的字符数目。
-l 此参数的效果和同时指定"-td4"参数相同。
-N<字符数目>或--read-bytes=<字符数目> 到设置的字符数目为止。
-o 此参数的效果和同时指定"-to2"参数相同。
-s<字符串字符数>或--strings=<字符串字符数> 只显示符合指定的字符数目的字符串。
-t<输出格式>或--format=<输出格式> 设置输出格式。
-v或--output-duplicates 输出时不省略重复的数据。
-w<每列字符数>或--width=<每列字符数> 设置每列的最大字符数。
-x 此参数的效果和同时指定"-h"参数相同。
--help 在线帮助。
--version 显示版本信息

实例效果:

执行下列命令:

echo abcdef g > tmp
 
 cat tmp
 
 od -b tmp

运行结果如图:

3、 用Linux IO相关系统调用编写myod.c 用myod 实现Linux下od -tx -tc :

运行代码为:

#include <stdio.h>

#include <stdlib.h>

#include"head.h"


int main(int argc,char *argv[])
{

    int i;
    int n1=0,n2=0,n3=0,n4=0;
    for(i=1;i<argc-1;i++)
    {
        switch(argv[i][2])
        {
            case 'c':n1=1;break;
            case 'x':n2=1;break;
            case 'd':n3=1;break;
            case 'o':n4=1;break;
        }
    }
    FILE *file=fopen(argv[argc-1],"r");
    if(file==NULL){printf("Error!\n");exit(0);}
    myod(file,n1,n2,n3,n4);
    return 0;
}
#include<stdio.h>
void myod(FILE *file,int n1,int n2,int n3,int n4)
{
    char ch,line[16];
    int i;
    int j=0;
    while((ch=fgetc(file))!=EOF){
        line[j%16]=ch;
	if((j+1)%16==0){
        if(n1){for(i=0;i<16;i++)
        {
            if(line[i]=='\n')
            {printf("%5s","\\n");continue;}
	    if(line[i]=='\t')
            {printf("%5s","\\t");continue;}
            putchar(line[i]);
            putchar(' ');
            putchar(' ');
            putchar(' ');
            putchar(' ');
        }
        putchar('\n');}
        if(n2){for(i=0;i<16;i++)
        {
            if(line[i]=='\n')
            {printf("0%-4x",'\n');continue;}
	    if(line[i]=='\t')
            {printf("0%-4x",'\t');continue;}
            printf("%-5x",line[i]);
        }
        putchar('\n');
        }

        if(n3){for(i=0;i<16;i++)
        {
            if(line[i]=='\n')
            {printf("%-5d",'\n');continue;}
	    if(line[i]=='\t')
            {printf("%-5d",'\t');continue;}
            printf("%-5d",line[i]);
        }
        putchar('\n');
        }
        if(n4){for(i=0;i<16;i++)
        {
            if(line[i]=='\n')
            {printf("%-5o",'\n');continue;}
	    if(line[i]=='\t')
            {printf("%-5o",'\t');continue;}
            printf("%-5o",line[i]);
        }
        putchar('\n');
        }
        printf("下一行!\n");
	}
	j++;

    }
}

代码编写中遇到的问题

问题: 关于char *argv的使用。

解决: 通过查找资料得知 argc是命令行总的参数个数 argv[]是argc个参数,其中第0个参数是程序的全名,以后的参数 命令行后面跟的用户输入的参数;

char *argv[]是一个字符数组,其大小是int argc,主要用于命令行参数 argv[] 参数,数组里每个元素代表一个参数;argc记录了用户在运行程序的命令行中输入的参数的个数 ;arg[]指向的数组中至少有一个字符指针,即arg[0].他通常指向程序中的可执行文件的文件名。在有些版本的编译器中还包括程序 文件所在的路径;

教材学习内容总结

第十章学习

  • 输入/输出(I/O)是在主存和外部设备(如磁盘驱动器、终端和网络)之间拷贝数据的过程。输入操作是从I/O设备拷贝数据到主存,而输出操作是从主存拷贝数据到I/O设备。

10.1 Unix I/O:

  • 打开文件 1、应用程序向内核发出请求

    2、要求内核打开相应的文件

    3、内核返回文件描述符

  • 一个小的非负整数,用来在后续对此文件的所有操作中标识这个文件叫做文件描述符,有三个已经指定了的文件描述符为:

    标准输入——0(STDIN_FILENO)

    标准输出——1(STDOUT_FILENO)

    标准错误——2(STDERR_FILENO)

  • 也就是说,在Unix生命周期一开始,0、1、2就被占用,以后的open只能从3开始,并且在UNIX下还有stdin,stdout,stderr表示同样的含义。

10.2打开和关闭文件

  • 打开文件:int open(char *filename, int flags, mode_t mode)

  • flags参数指明了进程打算如何访问这个文件:

    O_RDONLY:只读

    O_WRONLY:只写

    O_RDWR:可读可写

  • flags参数也可以是一个或者更多位掩码的或:

    O_CREAT:如果文件不存在,就创建它的一个截断的文件

    O_TRUNC:如果文件已存在,就截断它

    O_APPEND:在每次写操作前,设置文件的位置到文件的结尾处

  • 关闭文件

    int close(int fd)

10.3读和写文件

1、读 read

函数原型:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t n);

2、写 write

函数原型:

#include <unistd.h>
ssize_t write(int fd, void *buf, size_t n);

10.4用RIO包健壮的读写

  • 通过调用 rio_readn 和 rio_write函数,应用程序可以在内存和文件之间直接传送数据。

  • rio_readn函数在遇到EOF时只能返回一个不足值;而rio_write函数绝不会返回不同值;

  • 参数解析:

    fd:文件描述符

    usrbuf:存储器位置

    n:传送的字节数

    返回值:

    rio_readn成功则返回传送的字节数,EOF为0(一个不足值),出错为-1。

    rio_writen成功则返回传送的字节数,出错为-1,没有不足值。

  • RIO的带缓冲的输入函数

    缺点:效率不是很高,每读取文件中的一个字节都要求陷入内核

10.5 读取文件元数据

  • 应用程序能够通过调用stat和fstat函数,检索到关于文件的信息:元数据

  • 两种输入方式:

    stat函数以一个文件名作为输入

    fstat函数以文件描述符作为输入

  • stat数据结构中的st_size成员包含了文件的字节数大小,st_mode成员编码了文件访问许可位和文件类型。

10.6 共享文件

内核用三个相关的数据结构来表示打开的文件:
  • 描述符表:每个进程都它独立的描述符表,每个打来的描述符表指向文件表中的一个表项。
  • 文件表:打开文件的集合是由一张文件表来表示的。
  • v-node表:所有进程共享,每个表项包含stat结构中的大多数信息

10.7 I/O重定向

  • 使用dup2函数:

    int dup2(int oldfd,int newfd);

  • dup2函数拷贝描述符表表项oldfd到描述符表表项newfd

10.8 标准I/O

  • 标准I/O库将一个打开的文件模型化为一个流,一个流就是一个指向FILE类型的结构的指针

  • ANSI C程序开始时有三个打开的流:标准输入stdin、标准输出stdout和标准错误stderr

  • 类型为FILE的流是对文件描述符和流缓冲区的抽象

附录A 错误处理

错误处理风格

  • Unix风格

遇到错误后返回-1,并且将全局变量errno设置为指明错误原因的错误代码;

如果成功完成,就返回有用的结果。

  • Posix风格

返回0表示成功,返回非0表示失败;

有用的结果在传进来的函数参数中。

  • DNS风格

有两个函数,gethostbyname和gethostbyaddr,失败时返回NULL指针,并设置全局变量h_errno。

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

  • head命令用来查看文件的前几行内容,默认是查看前10行的内容,也可以设置一个数字改变查看行数

head hello.c/*当前目录下的文件名称*/

head -20 hello.c

  • tail命令用来查看文件的最后几行内容,默认是查看最后10行的内容,同样,也可以设置一个数字改变查看行数

tail -20 hello.c 运行代码效果如图:

代码:

#ifndef HEAD_H
#define HEAD_H
void main(int argc,char *argv[]);
void ihead(char *ch[],int n);
#endif
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"head.h"
#define N 1000
void main(int argc,char *argv[])
{
	char ch[N];
	int i,find,n;
	if((find=open(argv[1],O_RDONLY))==-1)
		{
			perror(argv[1]);
			exit(1);
		}
	n=read(find,ch,N);
	ihead(ch,n);
	close(find);
}
#include<stdio.h>
#include"head.h"
void ihead(char *ch[],int n)
{
	int k,count=0;
	for(k=0;k<n,count<10;i++)
	{
		if(ch[k]!='\n') 
		printf("%c",ch[k]);
		else {
			count++;
			printf("\n");
		}
	}
}

代码托管

码云链接

学习进度条

 代码行数(新增/积累)博客量(新增/积累学习时间(新增/累积)
目标 5000行 30篇 400小时
第一周 5/5 1/1 8/8
第二周 120/120 1/1 12/12
第三周 100/100 1/1 15/15
第四周 80/80 1/1 9/9

参考资料

《深入理解计算机系统V3》学习指导

详细介绍Linux指令od