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

教材学习内容总结

客户端-服务器编程模型

一个应用是由一个服务器进程和一个或多个客户端进程组成

服务器进程 -> 管理某种资源 -> 通过操作这种资源来为它的客户端提供某种服务

基本操作:事务

一个客户端-服务器事务由四步组成:

当一个客户端需要服务时,向服务器发送一个请求,发起一个事务。

服务器收到请求后,解释它,并以适当的方式操作它的资源。

服务器给客户端发送一个相应,并等待下一个请求。

客户端收到响应并处理它。

客户端和服务器都是进程

网络

对主机而言,网络是一种I/O设备:从网络上接收到的数据从适配器经过I/O和存储器总线拷贝到存储器,典型地是通过DMA(直接存储器存取方式)传送。

物理上,网络是一个按照地理远近组成的层次系统:最低层是LAN(局域网),最流行的局域网技术是以太网。

以太网段包括一些电缆和集线器。每根电缆都有相同的最大位带宽,集线器不加分辩地将一个端口上收到的每个位复制到其他所有的端口上,因此每台主机都能看到每个位。

每个以太网适配器都有一个全球唯一的48位地址,存储在适配器的非易失性存储器上。

一台主机可以发送一段位:帧,到这个网段内其它任何主机。每个帧包括一些固定数量的头部位(标识此帧的源和目的地址及帧长)和数据位(有效载荷)。每个主机都能看到这个帧,但是只有目的主机能读取。

使用电缆和网桥,多个以太网段可以连接成较大的局域网,称为桥接以太网。这些电缆的带宽可以是不同的。

全球IP因特网

TCP/IP协议族

因特网的客户端和服务器混合使用套接字接口函数和UnixI/O函数进行通信

把因特网看做一个世界范围的主机集合,满足以下特性:

主机集合被映射为一组32位的IP地址

这组IP地址被映射为一组称为因特网域名的标识符

因特网主机上的进程能够通过连接和任何其他主机上的进程

套接字接口

函数:

socket函数

connect函数

open_clientfd函数

bind函数

listen函数

open_listenfd函数

accept函数

Web服务器

  • Web客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫做 HTTP (Hypertext Transfer Protocol,超文本传输协议). HTTP 是一个简单的协议。一个 Web 客户端(即浏览器) 打开一个到服务器的因特网连接,并且请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取这些内容,并把它显示在屏幕上。

  • Web内容可以用一种叫做 HTML(Hypertext Markup Language,超文本标记语言)的语言来编写。一个 HTML 程序(页)包含指令(标记),它们告诉浏览器如何显示这页中的各种文本和图形对象。

  • Web 服务器以两种不同的方式向客户端提供内容:

取一个磁盘文件,并将它的内容返回给客户端。磁盘文件称为静态内容 (static content), 而返回文件给客户端的过程称为服务静态内容 (serving static content)。

运行一个可执行文件,并将它的输出返回给客户端。运行时可执行文件产生的输出称为态内容 (dynamic content),而运行程序并返回它的输出到客户端的过程称为服务动态内容 (serving dynamic content)。

并发编程

  • 并发:逻辑控制流在时间上重叠

  • 并发程序:使用应用级并发的应用程序称为并发程序。

  • 三种基本的构造并发程序的方法:

进程,用内核来调用和维护,有独立的虚拟地址空间,显式的进程间通信机制。

  • I/O多路复用,应用程序在一个进程的上下文中显式的调度控制流。逻辑流被模型化为状态机。

  • 线程,运行在一个单一进程上下文中的逻辑流。由内核进行调度,共享同一个虚拟地址空间。

基于进程的并发编程

构造并发服务器的自然方法就是,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

因为父子进程中的已连接描述符都指向同一个文件表表项,所以父进程关闭它的已连接描述符的拷贝是至关重要的,而且由此引起的存储器泄露将最终消耗尽可用的存储器,使系统崩溃。

基于线程的并发编程

线程:运行在进程上下文中的逻辑流。

线程有自己的线程上下文,包括一个唯一的整数线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有运行在一个进程里的线程共享该进程的整个虚拟地址空间。

使用线程提高并行性

写顺序程序只有一条逻辑流,写并发程序有多条并发流,并行程序是一个运行在多个处理器上的并发程序。并行程序的集合是并发程序集合的真子集。

代码学习

condvar:

输出生产者和消费者交替进行,消费者等待生产者产出产品后才打印,否则消费者阻塞等待生产者生产。

主函数代码如下:

运行结果如下:

此代码中,使用srand来根据当前时间重置随机数,用rand产生随机数。
用pthread_mutex_lock来将线程上锁,pthread_mutex_unlock解锁。当consumer等待条件变量的条件成立时,用pthread_cond_wait使consumer挂起。pthread_cond_signal的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态。

count:

代码如下:

运行结果如下:

在运行count的时候,每次运行的结果都不一样,有的超过5000,是因为代码中没有加入互斥锁,两个线程共享同一变量,输出的结果相互覆盖。

countwithmudex:

countwithmudex在count代码的基础上加了互斥锁,所以避免了访问冲突的问题。

因此,两个线程一共会输出10000

creatthread

这个代码用来打印进程和线程ID:

int pthread_create(pthread_t restrict tidp,const pthread_attr_t restrict_attr,void(start_rtn)(void),void restrict arg);

若成功则返回0,否则返回出错编号

restrict tidp为指向线程标识符的指针。
restrict_attr用来设置线程属性。
(start_rtn)(void)是线程运行函数的起始地址。
restrict arg是运行函数的参数。

semphore:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
#define NUM 5

    int queue[NUM];
    sem_t blank_number, product_number;
    void *producer ( void * arg )
    {
	    static int p = 0;
	    for ( ;; ) {
		    sem_wait( &blank_number );
		    queue[p] = rand() % 1000;
		    printf("Product %d \n", queue[p]);
		    p = (p+1) % NUM;
		    sleep ( rand() % 5);
		    sem_post( &product_number );
	    }
    }
    void *consumer ( void * arg )
    {
	    static int c = 0;
	    for( ;; ) {
	         	sem_wait( &product_number );
		        printf("Consume %d\n", queue[c]);
        		    c =    (c+1) % NUM;
    			sleep( rand() % 5 );
	        	sem_post( &blank_number );
	    }
    }

    int main(int argc, char *argv[] )
    {
        	pthread_t pid, cid;
    	        sem_init( &blank_number, 0, NUM );
            sem_init( &product_number, 0, 0);
	    pthread_create( &pid, NULL, producer, NULL);
	    pthread_create( &cid, NULL, consumer, NULL);
	    pthread_join( pid, NULL );
        	pthread_join( cid, NULL );
	    sem_destroy( &blank_number );
	    sem_destroy( &product_number );
        	return 0;
    	}

这个代码与condvar相似,只是semphore中使用的是sem_wait和sem_post,condvar中使用互斥锁。

函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。  

函数sem_wait( sem_t sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。 

share:

代码中定义了3个函数,thr_fn 1,thr_fn 2和thr_fn 3。这三个函数也对应着线程终止的三个方法:从线程函数return、调用pthread_exit终止自己、调用pthread_cancel终止同一进程中的另一个线程。此代码目的是获取线程的终止状态。

代码托管

上周考试错题总结

1、有关exec系列函数,下面说法正确的是()

A.可以用char[][] 来传递argv
B.进程调用了exec系列函数后,pid会变
C.进程调用了exec系列函数后,代码会改变。
D.system()和exec系列等价。
E.exec系列函数中带e的要传入环境变量参数
F.exec系列函数中带v的要传入环境变量参数

正确答案: C E 。不能用char[][] 来传递argv,结尾的0(null)无法处理;system=fork+exec+wait;

2、关于代码  int main(){} 说法正确的是()
A.返回值是0
B.返回值不确定
C.会调用exit(0)
D.返回值大于0
E.上面代码运行完,在命令行中运行echo $?  的值是0  

正确答案: A C E 。
main中不调用exit,会补上exit(0)

3、x86-64中指令长度是()字节
A.1
B.2
C.4
D.1-4
E.1-8
F.1-15
G.1-16
H.以上都不对

正确答案:F

结对及互评

本周结对学习情况

- [20155320](http://www.cnblogs.com/ljq1997/)
- 结对照片
- 结对学习内容
   
    - 教材第十一、二章的内容
    - 对代码的练习

其他(感悟、思考等,可选)

学习了两章的内容,因为之前刘念老师课上学到的知识距离老师要求的还很有距离,所以又重新看了一遍。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 10/20
第二周 57/100 1/1 20/30
第三周 100/100 1/1 30/30
第四周 233/200 1/1 20/20
第五周 267/200 1/1 20/20
第六周 220/2000 3/1 40/20
第七周 362/300 1/1 30/30
第八周 3000/300 3/1 45/30

尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。

参考:软件工程软件的估计为什么这么难软件工程 估计方法

  • 计划学习时间:30小时

  • 实际学习时间:45小时

  • 改进情况:

(有空多看看现代软件工程 课件
软件工程师能力自我评价表
)

参考资料