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》学习指导