2018-2019-1 20165325 《信息安全系统设计基础》第六周学习总结
一、学习笔记
1、输入输出是在主存和外部设备之间复制数据的过程
输入是I/O设备复制数据到主存,输出是主存复制数据到I/O设备;
2、Unix I/O
Linux内核引出的一个简单低级的接口,用于文件的读写。
描述符
内核返回的一个非负整数,好比文件指针。打开文件时被创建,当内核释放文件,描述符恢复到描述符池当中。
标准输入、标准输出、标准错误
头文件<unistd.h>引入的常量,分别对应STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO,描述符分别是0、1、2,可用来代替显式的描述符值;
例:一次一个字节地从标准输入复制到标准输出;
当前文件位置
对于当前文件位置,内核始终显式地保存着一个值k,这个k就是从文件开头起始字节的偏移量,发生读写操作以后,k值会更新。
#include "csapp.h"
int main (void){
char c;
while (Read(STDIN_FILENO,&c,1)!=0)
Write(STDOUO_FILENO,&c,1);
exit(0);
}
3、open()
对应头文件<sys/type.h>、<sys/stat.h>、<fcntl.h>。
int open(char *filename,int flags,mode_t mode);
返回值是打开文件以后的标识符。
flags参数
- O_RDONLY:只读;
- O_WRONLY:只写;
- O_RDWD:可读可写;
4、读、写文件
头文件:<unistd.h>
ssize_t read(int fd,void *buf,size_t n);
ssize_t write(int fd,const void *buf,size_t n);
ssize_t
是有符号long型;size_t
是无符号;
5、RIO包
提供更方便健壮高效的I/O,分为两种函数:无缓冲的输入输出和带缓冲的输入函数。
二、课上笔记
1、两个重要命令:
man -k key1 | grep key2| grep 2
: 根据关键字检索系统调用grep -nr XXX /usr/include
:查找宏定义,类型定义
2、操作系统的功能我总结为两点:管家婆和服务生:
- 管家婆:通过进程、虚拟内存和文件三个重要抽象管理计算机的CPU、内存、I/O设备。
- 服务生:为用户提供shell, 为程序员提供系统调用(API)。
3、od
命令复习
$ od -tc -tx1 abc.txt
4、echo
命令创建二进制文件
echo -ne "/x7b" > abc.bin
5、who命令的编写
who命令用作在Linux命令行中查看用户的身份,使用man who
查询结果:
DESCRIPTION
Print information about users who are currently logged in.
.....
If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is
common. If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are
usual.
所以可以想到,who命令的编写如下:
1、fopen()函数从指定位置打开文件/var/run/utmp;
2、用while循环读取文件直到文件末尾EOF;
3、输出先关信息,实现和who相同的功能。
问题的关键在于/var/run/utmp是怎么存储的,打开utmp.h如下:
#ifndef _UTMP_H
# error "Never include <bits/utmp.h> directly; use <utmp.h> instead."
#endif
#include <paths.h>
#include <sys/time.h>
#include <sys/types.h>
#include <bits/wordsize.h>
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
/* The structure describing an entry in the database of
previous logins. */
struct lastlog
{
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ll_time;
#else
__time_t ll_time;
#endif
char ll_line[UT_LINESIZE];
char ll_host[UT_HOSTSIZE];
};
/* The structure describing the status of a terminated process. This
type is used in `struct utmp' below. */
struct exit_status
{
short int e_termination; /* Process termination status. */
short int e_exit; /* Process exit status. */
};
/* The structure describing an entry in the user accounting database. */
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]; /* Devicename. */
char ut_id[4]; /* Inittab ID. */
char ut_user[UT_NAMESIZE]; /* Username. */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked
as DEAD_PROCESS. */
/* The ut_session and ut_tv fields must be the same size when compiled
32- and 64-bit. This allows data files and shared memory to be
shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ut_session; /* Session ID, used for windowing. */
struct
{
int32_t tv_sec; /* Seconds. */
int32_t tv_usec; /* Microseconds. */
} ut_tv; /* Time entry was made. */
#else
long int ut_session; /* Session ID, used for windowing. */
struct timeval ut_tv; /* Time entry was made. */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote host. */
char __unused[20]; /* Reserved for future use. */
};
/* Backwards compatibility hacks. */
#define ut_name ut_user
#ifndef _NO_UT_TIME
/* We have a problem here: `ut_time' is also used otherwise. Define
_NO_UT_TIME if the compiler complains. */
# define ut_time ut_tv.tv_sec
#endif
#define ut_xtime ut_tv.tv_sec
#define ut_addr ut_addr_v6[0]
/* Values for the `ut_type' field of a `struct utmp'. */
#define EMPTY 0 /* No valid user accounting information. */
#define RUN_LVL 1 /* The system's runlevel. */
#define BOOT_TIME 2 /* Time of system boot. */
#define NEW_TIME 3 /* Time after system clock changed. */
#define OLD_TIME 4 /* Time when system clock changed. */
#define INIT_PROCESS 5 /* Process spawned by the init process. */
#define LOGIN_PROCESS 6 /* Session leader of a logged in user. */
#define USER_PROCESS 7 /* Normal process. */
#define DEAD_PROCESS 8 /* Terminated process. */
#define ACCOUNTING 9
/* Old Linux name for the EMPTY type. */
#define UT_UNKNOWN EMPTY
/* Tell the user that we have a modern system with UT_HOST, UT_PID,
UT_TYPE, UT_ID and UT_TV fields. */
#define _HAVE_UT_TYPE 1
#define _HAVE_UT_PID 1
#define _HAVE_UT_ID 1
#define _HAVE_UT_TV 1
#define _HAVE_UT_HOST 1
看起来很长但是实际上关键的地方就是这里:
struct utmp
{
short int ut_type; /* Type of login. */
pid_t ut_pid; /* Process ID of login process. */
char ut_line[UT_LINESIZE]; /* Devicename. */
char ut_id[4]; /* Inittab ID. */
char ut_user[UT_NAMESIZE]; /* Username. */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login. */
struct exit_status ut_exit; /* Exit status of a process marked as DEAD_PROCESS. */
·······//此处省略一部分
现在我们明白了读取utmp文件的意义就是查找上面这样一个结构体,who命令所需要的信息都在里面。度娘表示:其中ut_type
有很多种不同的登录方式,而我们需要的是用户的登陆,所以我们要对/var/log/utmp文件的文件内容进行筛选,只显示登录方式为USER_PROCESS
的信息。
我们运行who命令来试一下
$ who
paul tty7 2018-11-03 15:51 (:0)
这里解释一下,我的用户名是paul,设备名tty7,后面是时间,最后那个(:0)百度查到了是host;
然后我们就可以开始自己写mywho.c了,这里的时间用的是用ctime函数。
代码如下:
/*
* mywho.c
* author lidongjun
*/
#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
void show_time(long time){
char * c;
c = ctime(&time);
printf("%12.20s", c+4);
}
void show_info(struct utmp *a){
if(a->ut_type != USER_PROCESS)
return;
printf("%-8s ", a->ut_user);
printf("%-8s ", a->ut_line);
show_time(a->ut_time);
printf(" (%s)", a->ut_host);
printf("\n");
}
int main() {
struct utmp a;
int fd;
int len = sizeof(a);
if((fd = open("/var/run/utmp", O_RDONLY,0)) == -1){
return -1;
}
while(read(fd, &a, len) == len)
show_info(&a);
close(fd);
return 0;
}
运行结果: