2017-2018-1 20155324 《信息安全系统设计基础》第八周学习总结
第八周课下作业2(课上没完成的必做)
基于socket 使用教材的csapp.h csapp.c,实现daytime(13)服务器(端口我们使用13+后三位学号)和客户端
服务器响应消息格式是
“
客户端IP:XXXX
服务器实现者学号:XXXXXXXX
当前时间: XX:XX:XX
”
上方提交代码
提交一个客户端至少查询三次时间的截图测试截图
提交至少两个客户端查询时间的截图测试截图
书上相关代码
多线程
/*
* echoserveri.c - An iterative echo server
*/
/* $begin echoserverimain */
#include "csapp.h"
void echo(int connfd);
int main(int argc, char **argv)
{
int listenfd, connfd, port, clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>
", argv[0]);
exit(0);
}
port = atoi(argv[1]);
listenfd = Open_listenfd(port);
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
/* determine the domain name and IP address of the client */
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)
", hp->h_name, haddrp);
echo(connfd);
Close(connfd);
}
exit(0);
}
/* $end echoserverimain */
echoclient.c:
/*
* echoclient.c - An echo client
*/
/* $begin echoclientmain */
#include "csapp.h"
int main(int argc, char **argv)
{
int clientfd, port;
char *host, buf[MAXLINE];
rio_t rio;
if (argc != 3) {
fprintf(stderr, "usage: %s <host> <port>
", argv[0]);
exit(0);
}
host = argv[1];
port = atoi(argv[2]);
clientfd = Open_clientfd(host, port);
Rio_readinitb(&rio, clientfd);
while (Fgets(buf, MAXLINE, stdin) != NULL) {
Rio_writen(clientfd, buf, strlen(buf));
Rio_readlineb(&rio, buf, MAXLINE);
Fputs(buf, stdout);
}
Close(clientfd);
exit(0);
}
/* $end echoclientmain */
遇到的问题:怎么导入csapp.h
下载并解压后(以 root
身份登录)应该是一个code
的文件夹,在其子文件夹include
和src
中分别可以找到csapp.h
和csapp.c
两个文件,把这两个文件拷贝到文件夹/usr/include
里面,并在csapp.h
文件中 #endif
之前加上一句 #include<csapp.h>
,然后编译时在最后加上 lpthread
例如:gcc main.c -lpthread
就可以编译了。
家庭作业4.47
取指阶段 icode:ifun = D:0
valP<-PC+1
译码阶段:valB<-R[%ebp]
执行阶段:valE<-valB+4
访存阶段:valM<-M4[valB]
写回阶段:R[%esp]<-valE R[%ebp]<-valM
家庭作业4.48
取指阶段:icode:ifun = M1[PC] = C:0
rA:rB<-M1[PC+1]
valC<-M4[PC+2]
valP<-PC+6
译码阶段:valB<-R[rB]
执行阶段:valE<-valB+valC
SetCC
访存阶段:-
写回阶段 R[rB]<-valE
教材学习内容总结
教材第十二章学习
什么是进程(Process)?
进程就是一段程序的执行过程。
进程的概念主要有两点:
-
第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程中调用的指令和本地变量。
-
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。
进程的状态: 进程有三个状态,就绪,运行和阻塞。
-
就绪状态其实就是获取了除cpu外的所有资源,只要处理器分配资源马上就可以运行。
-
运行态就是获取了处理器分配的资源,程序开始执行。
-
阻塞态,当程序条件不够时,需要等待条件满足时候才能执行,如等待I/O操作的时候,此刻的状态就叫阻塞态。
进程的特征:
-
动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;
-
并发性:任何进程都可以同其他进程一起并发执行;
-
独立性:进程是系统进行资源分配和调度的一个独立单位;
-
结构性:进程由程序、数据和进程控制块三部分组成。
什么是线程(thread)?
定义:线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
线程起源:
-
在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
-
后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了,于是就发明了线程。
线程特征:
-
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程;
-
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。
进程 和 线程的区别
1、一个程序至少有一个进程,一个进程至少有一个线程;
进程是资源分配的最小单位,线程是CPU调度的最小单位。
2、资源(内存、寄存器等)分配给进程,进程在执行过程拥有独立的内存空间,而同一进程下的所有线程共享所有资源,从而提高程序的运行效率;
3、处理机分配给线程,即处理机真正运行的是线程;
4、线程在执行过程中,需要协作同步。不同线程间的要利用通信协议来实现同步。
I/O多路复用
I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流. 发明它的原因,是尽量多的提高服务器的吞吐能力。
基于 I/O 多路复用的并发编程
-
I/O多路复用技术的基本思路:使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序
-
状态机就是一组状态、输入事件和转移,转移就是将状态和输入时间映射到状态,自循环是同一输入和输出状态之间的转移。
-
I/O 多路复用技术的优劣
优点:
-
它比基于进程的设计给了程序员更多的对程序行为的控制。
-
一个基于 I/O 多路复用的事件驱动服务器是运行在单一进程上下文中的,因 此每个逻辑流都能访问该进程的全部地址空间。
缺点:
- 编码复杂且不能充分利用多核处理器。
线程控制及相关系统调用
-
内核级线程:切换由内核控制,当线程进行切换的时候,由用户态转化为内核态。切换完毕要从内核态返回用户态;可以很好的利用smp,即利用多核cpu。windows线程就是这样的。
-
用户级线程内核的切换由用户态程序自己控制内核切换,不需要内核干涉,少了进出内核态的消耗,但不能很好的利用多核Cpu,目前Linux pthread大体是这么做的。
线程的实现可以分为两类:用户级线程(User-Level Thread)和内核线线程(Kernel-Level Thread),后者又称为内核支持的线程或轻量级进程。在多线程操作系统中,各个系统的实现方式并不相同,在有的系统中实现了用户级线程,有的系统中实现了内核级线程。
用户进程的优点:
(1) 线程的调度不需要内核直接参与,控制简单。
(2) 可以在不支持线程的操作系统中实现。
(3) 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多。
(4) 允许每个进程定制自己的调度算法,线程管理比较灵活。这就是必须自己写管理程序,与内核线程的区别
(5) 线程能够利用的表空间和堆栈空间比内核级线程多。
(6) 同一进程中只能同时有一个线程在运行,如果有一个线程使用了系统调用而阻塞,那么整个进程都会被挂起。另外,页面失效也会产生同样的问题。
多线程程序中的变量共享
每个线程和其他线程一起共享进程上下文的剩余部分。包括整个用户虚拟地址空间,是由只读文本、读/写数据、堆以及所有的共享库代码和数据区域组成的。线程也共享同样的打开文件的集合。
-
全局变量:虚拟存储器的读/写区域只会包含每个全局变量的一个实例。
-
本地自动变量:定义在函数内部但没有static属性的变量。
-
本地静态变量:定义在函数内部并有static属性的变量。
用信号量同步线程
进度图是将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,原点对应于没有任何线程完成一条指令的初始状态。
转换规则:
合法的转换是向右或者向上,即某一个线程中的一条指令完成
两条指令不能在同一时刻完成,即不允许出现对角线
程序不能反向运行,即不能出现向下或向左
信号量定义:
type semaphore=record
count: integer;
queue: list of process
end;
var s:semaphore;
读者—写者问题:
(1)读者优先,要求不让读者等待,除非已经把使用对象的权限赋予了一个写者。
(2)写者优先,要求一旦一个写者准备好可以写,它就会尽可能地完成它的写操作。
(3)饥饿就是一个线程无限期地阻塞,无法进展。
HTML静态化
效率最高、消耗最小的就是纯静态化的html页面,所以尽可能使网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,无法全部手动去挨个实现,于是出现了常见的信息发布系统CMS,像常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
阻塞 VS 非阻塞
阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态。
阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
socket接收数据函数recv是一个阻塞调用的例子。
当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
教材学习中的问题和解决过程
- 问题1:一个头文件
csapp.h
,编译代码时遇到csapp
不存在的问题 - 问题1解决方案:下载并解压后(以
root
身份登录)应该是一个code
的文件夹,在其子文件夹include
和src
中分别可以找到csapp.h
和csapp.c
两个文件,把这两个文件拷贝到文件夹/usr/include
里面,并在csapp.h
文件中#endif
之前加上一句#include<csapp.h>
,然后编译时在最后加上lpthread
例如:gcc main.c -lpthread
就可以编译了。
代码托管
其他(感悟、思考等,可选)
学习了系统的输入、输出,了解了一些I/O函数,只看书的页数的话,本章的页数很少,相较于之前几周感觉轻松不少。但是,页数少并不意味着不重要或是用时少,认真去理解系统的输入、输出对于我们理解程序的运行至关重要,而每一个函数的参数、不同情况的返回值也需要我们仔细研读。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 85/85 | 1/1 | 5/5 | |
第二周 | 150/230 | 1/2 | 10/15 | |
第三周 | 50/280 | 1/3 | 7/22 | |
第四周 | 70/350 | 1/4 | 5/27 | |
第五周 | 100/450 | 2/6 | 5/32 | |
第六周 | 50/500 | 1/7 | 10/42 | |
第七周 | 70/570 | 2/9 | 10/55 | |
第八周 | 80/650 | 1/10 | 8/63 |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:XX小时
-
实际学习时间:XX小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)