20145209 《信息安全系统设计基础》第13周问题汇总
20145209 《信息安全系统设计基础》第13周问题汇总
学习任务总结
网络编程
客户端-服务器编程模型
网络
全球IP因特网
套接字接口
Web服务器
并发编程
基于进程的并发编程
基于I/O多路复用的并发编程
基于线程的并发编程
使用线程提高并行性
其他并发问题
实践
教材学习中的问题和解决过程
1.练习题12.1
第33行代码,父进程关闭了连接描述符后,子进程仍然可以使用该描述符和客户端通信。为什么?
当父进程派生子进程时,它得到一个已经连接描述符的副本,并将相关文件表的引用计数从1加到2;父进程关闭它的描述符副本时,引用计数从2减少1(内核不会关闭文件知道它的引用计数变为0)。这样连接仍然保持打开
2.练习题12.3
如果在上述程序阻塞在对select的调用的时候,键入ctrl-d,会发生什么?
(参考答案)如果从一个描述符中读取一个字节的要求不会被阻塞,那么它就是准备好可以读的了。对于EOF来说也是如此。假如 EOF在一个描述符上为真,那么该描述符也准备好可以读了,因为读操作会立即返回一个零返回码,表示EOF。因此,键入ctrl-d会导致select函数返回。
3.练习题12.9
设p表示生产者数量,c表示消费者数量,n表示以项目单元为单位的缓冲区大小。对于下面的美国场景,指出subfinsert和subfremove中的互斥信号量是否是必需的。
A.p =1,c =1,n>1
B.p =1,c=1,n=1
C.p>1,c>1,n=1
A:是。因为生产者和消费者会并发地访问缓冲区
B:不是。因为n=1,一个非空的缓冲区就相当于一个满的缓冲区。当缓冲区包含一个项目的时候,生产者就已经被阻塞了;当缓冲区为空的时候,消费者就被阻塞了。所以在任意时刻,只有一个线程可以访问缓冲区,不必加互斥锁。
C:不是。同上。
4.为什么要用结构体来存放标量IP地址?
把一个标量地址存放在结构中,是套接字早期实现的不幸产物。位IP地址定义一个标量类型应该跟更有意义,但是现在更该已经太迟了,因为已经有大量的应用是基于此的了。
5.临界区使用原则(互斥条件)
使用原则:
有空让进:如果临界区空闲,则只要有进程申请就立即让其进入;
无空等待:每次只允许一个进程处于临界区;
多中择一:当没有进程在临界区,而同时有多个进程要求进入临界区,只能让其中之一进入临界区,其他进程必须等待;
让权等待:进入临界区的进程,不能在临界区内长时间阻塞等待某事件,使其它进程在临界区外无限期等待;
不能限制进程的并发数量和执行进度。
6.信号量实现互斥的基本原理
基本原理:
两个或多个进程通过传递信号进行合作,可以迫使进程在某个位置暂时停止执行(阻塞等待),直到它收到一个可以“向前推进”的信号(被唤醒);
将实现信号灯作用的变量称为信号量,常定义为记录型变量s,其一个域为整型,另一个域为队列,其元素为等待该信号量的阻塞进程(FIFO)。
信号量定义:
type semaphore=record
count: integer;
queue: list of process
end;
var s:semaphore;
定义对信号量的两个原子操作——P和V
P(wait)
wait(s)
s.count :=s.count-1;
if s.count<0 then
begin
进程阻塞;
进程进入s.queue队列;
end;
V(signal)
signal(s)
s.count :=s.count+1;
if s.count ≤0 then
begin
唤醒队首进程;
将进程从s.queue阻塞队列中移出;
end;
所遇到的问题及解决办法
一·博客链接:http://www.cnblogs.com/20145210ysy/p/6159432.html
问题:运行教材12-15的代码时出现“未定义的引用”
解决:由于pthread库不是Linux系统默认的库,链接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,要在编译语句末尾加-lpthread参数
附加:编译选项中指定 -pthread 会附加一个宏定义 -D_REENTRANT,该宏会导致 libc 头文件选择那些thread-safe的实现;链接选项中指定 -pthread 则同 -lpthread 一样,只表示链接 POSIX thread 库。由于 libc 用于适应 thread-safe 的宏定义可能变化,因此在编译和链接时都使用 -pthread 选项而不是传统的 -lpthread 能够保持向后兼容,并提高命令行的一致性。
(资料链接:http://blog.csdn.net/g446868263/article/details/6857840)
二·博客链接:http://www.cnblogs.com/zn20145214/p/6158406.html
问题:关于sieve.c出现编译错误和段错误
解决:首先编译时出现错误,是因为没有连接数学库而引发的错误,所以要在编译时加上-lm,就可以成功编译了。至于发生了段错误,则比较麻烦。
1.首先要知道什么是段错误:
段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址、栈溢出等等情况。
2.段错误信息的获取:
程序发生段错误时,提示信息很少,下面有几种查看段错误的发生信息的途径。
a.dmesg
dmesg可以在应用程序crash掉时,显示内核中保存的相关信息。如下所示,通过dmesg命令可以查看发生段错误的程序名称、引起段错误发生的内存地址、指令指针地址、堆栈指针地址、错误代码、错误原因等。
b.-g
使用gcc编译程序的源码时,加上-g参数,这样可以使得生成的二进制文件中加入可以用于gdb调试的有用信息。
c.nm
使用nm命令列出二进制文件中的符号表,包括符号地址、符号类型、符号名等,这样可以帮助定位在哪里发生了段错误。
d.ldd
使用ldd命令查看二进制程序的共享链接库依赖,包括库的名称、起始地址,这样可以确定段错误到底是发生在了自己的程序中还是依赖的共享库中。
3.段错误的调试方法
a.使用printf输出信息
就是在程序的重要代码附近加上像printf这类输出信息,这样可以跟踪并打印出段错误在代码中可能出现的位置。为了方便使用这种方法,可以使用条件编译指令#ifdef DEBUG和#endif把printf函数包起来。这样在程序编译时,如果加上-DDEBUG参数就能查看调试信息;否则不加该参数就不会显示调试信息。
b.使用gcc和gdb
步骤如下:
①为了能够使用gdb调试程序,在编译阶段加上-g参数
②使用gdb命令调试程序
③进入gdb后,运行程序
④完成调试后,输入quit命令退出gdb
(资料链接:http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html)