网络应用随处可见。
网络应用依赖于很多在系统研究中已经学习过的概念,例如,进程、信号、字节器映射以及动态存储分配,都扮演着重要的角色。还有一些新概念要掌握。我们需要理解基本的客户端-服务器编程模型,以及如何编写使用因特网提供的服务的客户端―服务器程序。最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web的服务器,能够为真实的Web,浏览器提供静态和动态的文本和图形内容。
事务由四步组成
1)当一个客户端需要服务时,它向服务器发送一个请求,发起一个事务。例如,当Web览器需要一个文件时,它就发送一个请求给Web服务器
2)服务器收到请求后,解释它,并以适当的方式操作它的资源。例如,当Web服务器收到浏览器发出的请求后,它就读一个磁盘文件
3)服务器给客户端发送一响应,并等待下一个请求。例如,Web服务器将文件发送回客户端;
4)客户端收到响应并处理它。例如,当Web浏览器收到来自服务器的一页后,它就在屏幕上显示此页。
协议软件的基本能力:
局域网由集线器和网桥及连接的电缆组成。
全球IP因特网是最著名和最成功的互联网络实现。虽然因特网的内部体系结构复杂而且不断变化,但是自从20世纪80年代早期以来,客户端-服务器应用的组织就一直保持相当的稳定。下图展示了一个因特网客户端-服务器应用程序的基本硬件和软件组织。每台因特网主机都运行实现TCP/TP协议的软件,几乎每个现代计算机系统都支持这个协议。因特网的客户端和服务器混合使用套接字接口函数和Unix I/O函数来进行通信。套接字函数典型地是作为会陷入内核的系统调用来实现的,并调用各种内核模式的TCP/IP函数。
因特网客户端和服务器互相通信时使用的是IP地址。然而,对于人们而言,大整数是很难记住的,所以因特网也定义了一组更加人性化的域名,以及一种将域名映射到IP地址的机制。域名是一串用句点分隔的单词(字母、数字和破折号)。
域名集合形成了一个层次结构,每个域名编码了它在这个层次中的位置。通过一个示例你将很容易理解这点。下展示了域名层次结构的一部分。层次结构可以表示为一棵树。树的节点表示城名,反向到根的路径形成了域名。子树称为子域。层次结构中的第一层是个未命名的根节点。下一层是一组一级域名由非赢利组织(因特网分酒名字数字协会)定义。常见的第一层域名包括com、edu、gov、org、net,这些域名是由ICANN的各个授权代理按照先到先服务的基础分配的的。一旦一个组织得到了一个二级域名,那么它就可以在这个子域中创建任何新的域名了。
从Unix内核的角度来看,一个套接字就是通信的一个端点。
2、socket函数
Web服务器以两种不同的方式向客户端提供内容:
如果逻辑控制流在时间上重叠,那么他们就是并发的。应用级并发在以下情况中发挥作用:
使用应用级并发的应用程序称为并发程序。现代操作系统提供了三种基本的构造并发程序的方法:
一个基于进程的并发的echo服务器的代码,重要说明:
关于进程的优劣,对于在父、子进程间共享状态信息,进程有一个非常清晰的模型:共享文件表,但是不共享用户地址空间。进程有独立的地址控件爱你既是优点又是缺点。由于独立的地址空间,所以进程不会覆盖另一个进程的虚拟存储器。但是另一方面进程间通信就比较麻烦,至少开销很高。
事件驱动程序:将逻辑流模型化为状态机。
状态机:
对于状态机的理解,参考EDA课程中学习的状态转换图的画法和状态机。
整体的流程是:
几个需要注意的函数:
事件驱动设计的优点:
1.它比基于进程的设计给了程序员更多的对程序行为的控制。例如我们可以设想编写一个事件驱动的并发服务器,为某些客户提供他们需要的服务,而这对于新进程的并发服务器来说,是很困难的
2.一个基于I/O多路复用的事件驱动器是运行在单一进程上下文中的,因此每个逻辑流都能访问该进程的全部地址空间。这使得在流之间共享数据变得很容易,一个与作为单个进程运行相关的优点是,你可以利用熟悉的调试工具,例如GDB,来调试你的并发服务器,就像对顺序程序那样。最后,事件驱动设计常常比基于进利的设计要高效得多,因为它们不需要进程上下文切换来调度新的流。
事件驱动设计的缺点:
就是编码复杂,我们的事件驱动的并发服务器需要的代度是指每个逻辑流每个时间片执行的指令数量。基于事件的设计的另一个重大缺点是它们不能充分利利用多核处理器。
每个线程都有自己的线程上下文,包括一个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的线程共享该进程的整个虚拟地址空间。由于线程运行在单一进程中,因此共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。
线程执行的模型。线程和进程的执行模型有些相似。每个进程的声明周期都是一个线程,我们称之为主线程。但是大家要有意识:线程是对等的,主线程跟其他线程的区别就是它先执行。
POSIX线程是在C程序中处理线程的一个标准接口。它最早出现在1995年,而且在大多数Unix系统上都可用。Pthreads定义了大约60个函数,允许程序创建、杀死和回收线程,与对等线程安全地共享数据,还可以通知对等线程系统状态的变化。
线程通过调用pthread_create来创建其他线程。
int pthread_create(pthread_t *tid,pthread_attr_t *attr,func *f,void *arg); 成功则返回0,出错则为非零
当函数返回时,参数tid包含新创建的线程的ID,新线程可以通过调用pthread_self函数来获得自己的线程ID。
pthread_t pthread_self(void);返回调用者的线程ID。
当顶层的线程例程返回时,线程会隐式地终止
某个对等线程调用Unix的e×it函数,该函数终止进程以及所有与该进程相关的线程
线程通过调用pthread_join函数等待其他线程终止。
int pthread_join(pthread_t tid,void **thread_return); 成功则返回0,出错则为非零
在任何一个时间点上,线程是可结合或可分离的。一个可结合的线程能够被其他线程收回其资源和杀死,在被回收之前,它的存储器资源是没有被释放的。分离的线程则相反,资源在其终止时自动释放。
int pthread_deacth(pthread_t tid); 成功则返回0,出错则为非零
pthread_once允许初始化与线程例程相关的状态。
pthread_once_t once_control=PTHREAD_ONCE_INIT; int pthread_once(pthread_once_t *once_control,void (*init_routine)(void)); 总是返回0
一个变量是共享的,当且仅当多个线程引用这个变量的某个实例。
线程化的C程序中变量根据它们的存储类型被映射到虚拟存储器:
当且仅当变量的一个实例被一个以上的线程引用时,就说变量是共享的。
共享变量的同时引入了同步错误,即没有办法预测操作系统是否为线程选择一个正确的顺序。
1、进度图
将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,将指令模型化为从一种状态到另一种状态的转换。
2、信号量
信号量不变性:一个正确初始化了的信号量有一个负值。
信号量操作函数:
int sem_init(sem_t *sem,0,unsigned int value);//将信号量初始化为value
int sem_wait(sem_t *s);//P(s)
int sem_post(sem_t *s);//V(s)
3、使用信号量来实现互斥
信号量提供了一种很方便的方法来确保对共享变量的互斥访问。基本思想是将每个共享变量(或者一组相关的共享变量)与一个信号量联系起来 。以这种方式来保护共享变量的信号量叫做二元信号量,因为它的值总是0或者1。以提供互斥为目的的二元信号量常常也称为互斥锁。在一个互斥锁上执行P操作称为对互斥锁加锁。类似地,执行V操作称为对互斥锁解锁。对一个互斥锁加了锁但是还没有解锁的线程称为占用这个互斥锁。一个被用作一组可用资源的计数器的信号量称为计数信号量。关键思想是这种P和V操作的结合创建了一组状态,叫做禁止区。因为信号量的不变性,没有实际可行的轨迹线能够包含禁止区中的状态。而且,因为禁止区完全包括了不安全区,所以没有实际可行的轨迹线能够接触不安全区的任何部分。因此,每条实际可行的轨迹线都是安全的,而且不管运行时指令顺序是怎样的,程序都会正确地增加计数器的值。
到目前为止,在对并发的研究中,我们都假设并发线程是在单处许多现代机器具有多核处理器。并发程序通常在这样的机器上运理器系统上执行的。然而,在多个核上并行地调度这些并发线程,而不是在单个核顺序地调度,在像繁忙的Web服务器、数据库服务器和大型科学计算代码这样的应用中利用这种并行性是至关重要的。
定义四个(不相交的)线程不安全函数类:
就是使用线程不安全函数的可重入版本,名字以_r为后缀结尾。
当一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达他的控制流x点时,就会发生竞争。
为消除竞争,可以动态地为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针。
【解决死锁的方法】
a.不让死锁发生:
静态策略:设计合适的资源分配算法,不让死锁发生---死锁预防;
动态策略:进程在申请资源时,系统审查是否会产生死锁,若会产生死锁则不分配---死锁避免。
b.让死锁发生:
进程申请资源时不进行限制,系统定期或者不定期检测是否有死锁发生,当检测到时解决死锁----死锁检测与解除。
总结:
参考资料:
1.教材《深入理解计算机系统》
2.《计算机操作系统》
3.20135202闫佳歆博客http://www.cnblogs.com/20135202yjx/p/4926597.html解决死锁部分