20145312 《信息安全系统设计基础》第13周学习总结

20145312 《信息安全系统设计基础》第13周学习总结

教材内容总结

第11章 网络编程

11.1客户端-服务端编程模型

  • 每一个网络应用都是基于客户端-服务端编程模型
  • 客户端和服务端是进程
    html

    11.2 网络

  • 对于一个主机而言,网络只是又一种I/O设备
  • 物理上而言,网络是一个按照地理远近组成的层次系统。
  • 最底层是LAN(局域网)
  • 适配器提供到网络的物理接口
  • 以太网段(电缆+集线器)
  • 一台主机能够发送一段位,称为帧
  • 每一个主机适配器都能看到这个帧,可是只有目的主机实际读取它
  • 多个以太网段能够链接成较大的局域网,称为桥接以太网
  • 网桥比集线器更充分的利用了电缆带宽
  • 在层次较高的级别中,多个不兼容的局域网能够经过路由器链接起来,组成互联网络
  • 每台路由器对于它所链接到的每一个网络都有一个适配器(端口)
  • 路由器能够用来由各类局域网和广域网构建互联网络
  • 网络协议提供两种基本能力:
命名机制
传送机制
  • 封装是关键

11.3 全球IP因特网

  • 因特网的客户端和服务端混合使用套接字接口函数和Unix I/O函数来进行通讯
  • 因特网上的主机经过IP地址和域名来标识
  • TCP/IP其实是一个协议族
  • IP机制从某种意义上而言是不可靠的
  • TCP是一个构建在IP之上的复杂协议,提供了进程间可靠地全双工链接
    linux

    11.3.1 IP地址
  • 一个IP地址就是一个32位无符号整数
  • IP/TCP为任意整数数据项定义了统一的网络字节顺序(大端字节顺序)
  • 对inet-aton的调用传递的是指向结构的指针,而对inet_ntoa的调用传递的是结构自己git

    11.3.2 因特网域名
  • 域名集合造成一个层次结构,子树称为子域
  • 一个IP对多个域名,可供多个域名解析,但域名解析到的地址是一个对一个
  • 某些合法域名没有映射到任何IP地址程序员

    11.3.3 因特网链接
  • 点对点、全双工、可靠
  • 客户端套接字地址中的端口是由内核自动分配的,称为临时端口
  • 服务端套接字地址中的端口一般是某个知名端口(HTTP:80)
  • 套接字地址:(地址:端口)
    web

    11.4套接字接口

  • 套接字接口是一组函数,用以建立网络应用
  • 套接字地址结构
sin_family成员是AF_INET
sin_port成员是一个16位端口
sin_addr成员是32位的IP地址
  • IP地址和端口号老是以网络字节顺序(大端法)存放的
socket函数

客户端和服务端使用socket函数来建立一个套接字描述符编程

connect函数

创建和服务器的链接。数组

open_clientfd函数

将socket和connect函数包装而成。客户端能够用它来和服务器创建链接。浏览器

bind函数

告诉内核将my_addr中的服务器套接字地址和套接字描述符sockfd联系起来安全

listen函数

将sockfd从一个主动套接字转化为一个监听套接字服务器

accept函数

均被服务器用于和客户端创建链接。

open_listenfd函数

socket、bind和listen函数结合。用于服务器建立一个监听描述符。

11.5Web服务器

协议
  • Web 客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫作 HTTP (超文本传输协议).
  • HTTP 是一个简单的协议。
  • 一个 Web 客户端(即浏览器) 打开一个到服务器的因特网链接,而且请求某些内容。服务器响应所请求的内容,而后关闭链接。浏览器读取这些内容,并把它显示在屏幕上。
  • Web内容能够用一种叫作 HTML(Hypertext Markup Language,超文本标记语言)的语言来编写。一个 HTML 程序(页)包含指令(标记),它们告诉浏览器如何显示这页中的各类文本和图形对象。

    内容
  • 对于Web客户端和服务端而言,内容是与一个MIME类型相关的字节序列
  • Web 服务器以两种不一样的方式向客户端提供内容:
  • 取一个磁盘文件,并将它的内容返回给客户端。磁盘文件称为静态内容 , 而返回文件给客户端的过程称为服务静态内容
  • 运行一个可执行文件,并将它的输出返回给客户端。运行时可执行文件产生的输出称为态内容 ,而运行程序并返回它的输出到客户端的过程称为服务动态内容
  • 每条由Web服务器返回的内容都是和他管理的某个文件相关联的。这些文件每个都有一个惟一的名字,叫作:URL

    第12章 并发程序

  • 使用应用级并发的应用程序称为并发程序。现代操做系统提供了三种基本的构造并发程序的方法:
进程
I/O多路复用
线程

12.1 基于进程的并发进程

  • 构造并发程序最简单的方法就是用进程
第一步:服务器接受客户端的链接请求
第二步:服务器派生一个子进程为这个客户端服务
第三步:服务器接受另外一个链接请求
第四步:服务器派生另外一个子进程为新的客户端服务
12.1.1 基于进程的并发服务器
  • 首先,包括一个SIGCHLD处理程序,回收僵死子进程资源
  • 其次,父子进程必须关闭它们各自的connfd拷贝,以避免存储器泄露
  • 最后,知道父子进程的connfd都关闭了,到客户端的链接才会终止

    12.1.2 关于进程的优劣
  • 父子进程间共享状态信息,共享文件表,可是不共享用户地址空间。
  • 进程有独立的地址空间(既是优势也是缺点)
优势:一个进程不可能不当心覆盖另外一个进程的虚拟存储器
缺点:独立的地址空间使得进程共享信息变得更加困难;IPC机制每每比较慢
12.2 基于I/O多路复用的并发进程
  • 基本思路就是使用select函数,要求内核挂起进程,在I/O事件发生后,才将控制返回给应用程序。
  • select函数处理类型为fd_set的集合,也叫做描述符集合
  • 只容许对描述符集合作三件事:
分配
将一个此种类型的变量赋值给另外一个变量
用FD_ZERO、FD_SET等宏指令来修改和检查它们
12.2.1 基于I/O多路复用的并发事件驱动服务器
  • 一个状态机就是:状态、输入事件和转移(状态机sk)
状态:等待描述符dk准备好可读
输入事件:描述符dk准备好能够读了
转移:从描述符dk读一个文本行
  • 自循环是同一输入和输出状态之间的转移
  • init pool:初始化活动客户端池
  • clientfd 数组表示已链接描述符的集合, 其中整数 -1 表示一个可用的槽位。初始时,已链接描述符集合是空的,并且监听描述符是 select 读集合中惟一的描述符。
  • add_client:向池中添加一个新的客户端链接
  • select 函数检测到输入事件,而 add_client 函数建立 一个新的逻辑流(状态机)。
  • check_clients:为准备好的客户端链接服务

    12.2.2 I/O多路复用技术的优劣
  • 优势:它比基于进程的设计给了程序员更多的对程序行为的控制,它是运行在单一进程上下文中的,所以每一个逻辑流都能访问该进程的所有地址空间
  • 缺点:编码复杂

12.3 基于线程的并发编程

  • 线程就是运行在进程上下文的逻辑流,由内核进行调度
  • 每一个线程都有它本身的线程上下文,包括一个惟一的整数线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码
  • 全部的运行在一个进程里的线程共享该进程的整个虚拟地址空间
12.3.1 线程执行模型
  • 主线程:每一个进程开始生命周期时第一个运行的线程
  • 对等线程:某时刻主线程建立的
  • 线程的上下文切换要比进程的上下文切换快得多。
  • 和一个进程相关的线程组成一个对等(线程)池 (pool),独立于其余线程建立的线程。
  • 线程不像进程同样,不是按照严格的父子进程组织的
  • 每一个对等线程都能读写相同共享数据

    12.3.2 Posix线程
  • Posix线程是在C程序中处理线程的一个标准接口
  • Pthread容许程序建立、杀死和回收线程,与对等线程安全的共享数据,还能够通知对等线程系统状态的变化
  • 现成的代码和本地数据被封装在一个线程例程中

    12.3.3 建立线程
  • 线程经过调用pthread_create函数来建立其余线程
  • 新线程能够经过调用pthread_self函数来得到它本身的线程ID

    12.3.4 终止线程
  • 当顶层的线程例程返回时,线程会隐式的终止
  • 经过调用pthread_exit函数,线程会显示的zhongzhi
  • 某个对等线程调用Unix的exit函数,该函数终止进程以及全部与该进程相关的线程
  • 另外一个对等线程经过调用pthread_cancle函数来终止当前线程

    12.3.5 回收已终止线程的资源
  • 线程经过调用pthread_join函数等待其余线程终止
  • 与wait函数不一样,pthread_join函数只能等待一个指定的线程终止

    12.3.6 分离线程
  • 一个分离的线程是不能被其余线程回收或杀死的。它的存储器资源在它终止时由系统自动释放
  • 每一个可结合线程都应该要么被其余线程显示的收回,要么经过调用pthread_detach函数被分离
  • pthreaddetach 函数分离可结合线程 tid. 线程可以经过以 pthreadself()为参数的 pthread_detach 调用来分离它们本身。

    12.3.7 初始化线程
  • pthread_once函数容许你初始化与线程例程相关的状态
  • once_control变量是一个全局或者静态变量

    12.3.8 一个基于线程的并发服务器
  • 主线程不断地等待链接请求,而后建立一个对等线程处理该要求
  • 为了不潜在的致命竞争,将每一个accept返回的已链接描述符分配到它本身的动态分配的存储器块
  • 另外一个问题是在线程例程中避免存储器泄露
  • 调用 pthread_ create 时,如何将已链接描述符传递给对等线程。最明显的方法就是传递一个指向这个描述符的指针。 对等线程间接引用这个指针,并将它赋值给一个局部变量。

12.4 多线程程序中的共享变量

12.4.1 线程存储器模型
  • 每一个线程都有它本身独立的线程上下文,每一个线程和其余线程一块儿共享进程上下文的剩余部分
  • 寄存器是从不共享的,而虚拟存储器老是共享的
  • 若是一个线程以某种方式获得一个指向其余线程栈的指针,那么它就能够读写这个栈的任何部分

    12.4.2 将变量映射到存储器(根据存储类型)
  • 全局变量:全局变量是定义在函数以外的变量
  • 本地自动变量:本地自动变量就是定义在函数内部可是没有static属性的变量
  • 本地静态变量:本地静态变量是定义在函数内部有static属性的变量

    12.4.3 共享变量
  • 一个变量是共享的,当且仅当它的一个实例被一个以上线程引用
  • myid不是共享的,由于它的两个实例中每个都只被一个线程引用
  • msgs这样的本地自动变量也能被共享

12.5 用信号量同步线程

12.5.1进度图
  • 进度图是将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,原点对应于没有任何线程完成一条指令的初始状态。
  • 当n=2时,状态比较简单,是比较熟悉的二维坐标图,横纵坐标各表明一个线程,而转换被表示为有向边
  • 转换规则:
合法的转换是向右或者向上,即某一个线程中的一条指令完成
两条指令不能在同一时刻完成,即不容许出现对角线
程序不能反向运行,即不能出现向下或向左
而一个程序的执行历史被模型化为状态空间中的一条轨迹线。
线程循环代码的分解: H:在循环头部的指令块 L:加载共享变量cnt到线程i中寄存器%eax的指令。 U:更新(增长)%eax的指令 S:将%eax的更新值存回到共享变量cnt的指令 T:循环尾部的指令块
  • 临界区:对于线程i,操做共享变量cnt内容的指令L,U,S构成了一个关于共享变量cnt的临界区。
  • 不安全区:两个临界区的交集造成的状态
  • 安全轨迹线:绕开不安全区的轨迹线

    12.5.2信号量
  • 信号量是具备非负整数值的全局变量,只能由两种特殊的操做来处理,这两种操做称为P和V
P(s):若是s是非零的,那么P将s-1,而且当即返回;若是s为零,那么就挂起这个线程,直到s变为非零
V(s):V操做将s+1
  • 当有多个线程在等待同一个信号量时,你不能预测V操做要重启哪个线程。
  • 信号量不变性:一个正在运行的程序毫不能进入这样一种状态,也就是一个正确初始化了的信号量有一个负值。

    12.5.3使用信号量来实现互斥
  • 二元信号量:将每一个共享变量与一个信号量联系起来,而后用而后用P(S)和V(s)操做将这种临界区包围起来,这种方式来保护共享变量的信号量。
  • 互斥锁:以提供互斥为目的的二元信号量
  • 加锁:一个互斥锁上执行P操做称为对互斥锁加锁,执行V操做称为对互斥锁解锁。对一个互斥锁加了锁但尚未解锁的线程称为占用了这个互斥锁。
  • 计数信号量:一个呗用做一组可用资源的计数器的信号量

    代码调试中的问题和解决过程

    countwithmutex.c

  • pthread库不是linux系统默认的库,所以pthread_creat建立线程时,在编译中要加上-lpthread参数。
  • 代码中涉及到的函数:
pthread_creat:建立线程,若成功则返回0,若失败则返回出错编号。第一个参数为指向线程标识符的指针,建立成功时指向的内存单元被设置为新建立线程的线程ID;第二个参数设置线程属性;第三个参数是线程运行函数的起始地址;最后一个参数是运行函数的参数
pthread_join:用来等待一个线程的结束。当函数返回时,被等待线程的资源被收回。
pthread_mutex_lock:线程调用该函数让互斥锁上锁。成功锁定时返回0,其余任何返回值都表示出现了错误。
pthread_mutex_unlock:与pthread_mutex_lock成对存在。释放互斥锁。
  • 因为定义的NLOOP值为5000,因此程序最后的输出值为10000。

count.c

  • 这个代码用于与countwithmutex.c进行对比,差异在于本代码doit函数的for循环中没有引入互斥锁,只进行了单纯的计数,建立两个线程共享同一变量都实现加一操做。

condvar.c

  • 这个代码演示的是生产者生产和消费者消费交替进行的过程。是线程间同步的一种状况。
  • 主函数中用srand(time(NULL))设置当前的时间值为种子,在后面的producer和consumer函数中调用rand()函数产生随机数。

    cp_t.c

  • 代码中涉及到的函数:
mmap函数
`void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);`
将一个文件或者其余对象映射进内存。文件被映射到多个页上,若是文件的大小不是全部页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中做用很大。
成功执行时,mmap()返回被映射区的指针,munmap()返回0.失败时,mmap()返回MAP_FAILED,munmap返回-1.
lseek函数
off_t lseek(int fd,off_t offset,int whence);
fd表示要操做的文件描述符,offset是相对于whence(基准)的偏移量,whence能够是SEEK_SET(文件指针开始),SEEK_CUR(文件指针当前位置),SEEK_END(文件指针尾)
lseek主要做用是移动文件读写指针,返回文件读写指针距文件开头的字节大小,若出错则返回-1.
  • 运行结果

    createthread.c

  • 程序主要演示了建立线程函数pthread_create()函数的使用,用来打印进程和线程的ID
  • 主函数中先利用pthread_create()函数建立一个线程,接着调用printids函数(打印标识符的函数)打印主线程号,最后线程函数thr_fn中打印出新建的线程号

    sieve.c

  • 编译时出现错误
  • 按照错误提示发现是由于没有链接数学库引起的错误,因此在编译时加上-lm,能够成功编译,运行结果提示了段错误。
  • 查找资料得知:
    段错误就是指访问的内存超出了系统所给这个程序的内存空间,一般这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指 向的gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的 表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了 越界访问,cpu就会产生相应的异常保护,因而segmentation fault就出现了. 在编程中如下几类作法容易致使段错误,基本是是错误地使用指针引发的
1)访问系统数据区,尤为是往 系统保护的内存地址写数据    最多见就是给一个指针以0地址 
  2)内存越界(数组越界,变量类型不一致等
  3)访问到不属于你的内存区域
  • 阅读代码后仍没找到错误,问题还没有解决。

    semphore.c

  • 代码中涉及到的函数:
sem_init函数
sem_init(sem_t *sem, int pshared, umsigned int value);
函数初始化一个定位在sem的匿名信号量;pshared参数为0指明信号量是由进程内线程共享,若为非0值则信号量在进程之间共享;value参数指定信号量的初始值。
sem_init()成功时返回0;错误时返回-1,并把errno设置为合适的值。
sem_destroy()函数用于销毁由sem指向的匿名信号量。只有经过sem_init()初始化的信号量才应该使用该函数销毁。函数成功时返回0,错误时返回-1,并把errno设置为合适的值。
这个函数和以前的condvar.c同样都是展现生产者和消费者交替工做的过程。区别是本程序实现生产或消费的过程是利用sem_wait()和sem_post(),它们的做用分别是从信号量的值减去一个“1”和从信号量的值加上一个“1”
  • 运行结果截图

    share.c

  • 代码运行结果以下

    hello_multi.c

  • 程序中的print_msg()函数中:在printf后的fflush(stdout);说明要马上将要输出的内容输出,每输出一次停1秒,并循环5次。
  • 若想要使程序输出像预期的打印出5个完整的helloworld,只须要将线程t1和t2的位置互换,修改代码以下
  • 修改后代码运行以下

    hello_multi1.c

  • 代码运行结果以下

    hello_single.c

  • 根据代码,先单独执行print_msg("hello");——输出5个hello,后输出5个带换行的world

    incprint.c

  • 因为定义中NUM=5,因此输出的count为1——5

    twordcount.c

  • twordcount1.c运行以下
  • twordcount2.c运行以下
  • twordcount3.c运行以下
  • twordcount4.c运行以下

本周代码托管截图





学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 200/200 2/2 20/20
第二周 200/400 2/4 18/38
第三周 100/500 1/5 10/48
第四周 250/750 1/6 10/58
第五周 100/850 1/7 10/68
第六周 100/950 1/8 12/80
第七周 200/1150 1/9 12/92
第八周 124/1274 2/11 10/102
第九周 205/1479 2/13 5/107
第十周 646/2125 2/15 9/116
第十一周 421/2546 2/17 12/128
第十二周 752/3298 3/10 11/139
第十三周 1001/4299 1/21 12/151

参考资料

相关文章
相关标签/搜索