$ \bigcup $
: 并$ - $
: 差$ * $
: 笛卡尔积$ \sigma $
: 投影$ \pi $
: 选择$ \bigcap $
: 交 $ \theta $
: 等值链接$ \bowtie $
: 自然链接$ \div $
: 除法注意 |
---|
1.等值连接表示先做笛卡尔积之后,对相应的列进行选择,或者等值关联;仅筛选行,不筛选列。 |
2.自然连接表示两个关系中若有相同的名称的属性,则自动作为关联条件,且仅列出一列。 |
例如:
给定学生S(学号,姓名,年龄,入学时间,联系方式)和选课SC(学号,课程号,成绩)关系,若要查询选修了1号课程的学生的学号、姓名和成绩,则该查询与关系代数表达式为什么?
\pi_{1,2,7}(\sigma_{6='1'}(S \bowtie SC))
首先,我们需要将两个关系S和SC做自然连接$(S \bowtie SC)$
。这样一来,我们得到关系R(学号,姓名,年龄,入学时间,联系方式,课程号,成绩)。注意,我们采用自然链接那么“学号”只保留1列,就剩下7个属性。接下来,我们要选择课程号为1的记录,所以$ \sigma_{6='1'}(S \bowtie SC)$
。注意,这里第6列为课程号,所以$ \sigma_{6='1'} $
。最后,我们选取第1、2、7列属性,得到最终表达式为$\pi_{1,2,7}(\sigma_{6='1'}(S \bowtie SC))$
要了解操作系统的存储管理,我们首先需要明确这里的存储是指的什么?那么,这里的“存储”是指,计算机的内存和外存(比如:硬盘)。目前,大多数的操作系统都采用虚拟存储器的方式来管理存储。所谓虚拟存储技术,即在内存中保留一部分程序和数据,在外存中放置整个地址空间的副本。就是说程序的大部分数据和可执行程序都放在外存上。程序运行时可以访问内存中的数据和程序,如果要访问的程序和数据不在内存中时,就将之前内存中的程序和数据回写到外存,然后从外存中调入所需的程序和数据。
单一连续分区、固定分区、可变分区和可重定位分区,这些存储的组织方式不能实现虚拟存储管理。页式、段式和页段式才是虚拟存储的管理方式。我们这里主要讨论虚拟存储的管理中的地址变换问题。
我们来假设我们的一个程序以及它运行起来需要的数据一共是1K bit大小。而我们的内存只有512bit。那么我们可以看做虚拟存储大小为1Kbit。即,我们将程序和程序执行的数据整体看做是虚拟存储。那么,虚拟存储的容量是允许大于内存的容量的,也就是所说的虚拟存储的地址范围是可以大于内存的地址范围的。(在非虚拟存储的一些操作系统中,是不允许作业的大小超过内存容量的,因为作业是一次性装载入内存的。)因为,实际运行过程只有一部分程序和数据存放在内存上,大部分存放在外存上。程序运行过程中,程序和数据在操作系统的管理下,在内存和外存之间进行不断的交换。
我们把内存中的实际地址叫做物理地址,而虚拟存储中的地址叫做逻辑地址。也就是说逻辑地址的范围是允许大于物理地址的范围的。那么,如果按照逻辑地址在内存中寻址,就会产生错误。这怎么办?幸运的是计算机硬件会在存储管理的过程中,帮我们把逻辑地址转换成物理地址。
页式存储是将虚拟存储的数据按页分割。然后在内存中按照虚拟存储中页的大小,开辟与页数相同的区。再在内存中创建一个页表。页表中记录虚拟存储的页号和一页开始的物理地址。最后,在控制寄存器中记录页表的长度和页表的物理地址。那么,页式管理的逻辑地址就由页号和页内偏移(即页内存储块的序号)组成。
页号 | 页内偏 |
---|
在操作系统原理中,最基础的一个理论点就是信号量和PV操作。那么什么是信号量和PV操作呢?简单来说就是操作系统处理并发进程资源竞争的一种方法。
详情请查看
信号量我们可以简单理解为一个整型变量。P和V操作我们可以理解为两个函数。我们用伪代码实现如下:
//信号量s
int s;
function P(s){
s=s-1;
if(s<0){
//阻塞当前进程的执行
}
}
function V(s){
s=s+1;
if(s<=0){
//唤醒在s上阻塞的进程
}
}
以上就是信号量和PV操作声明。你可能以为这是在开玩笑,这就是信号量和PV操作?真的就是这样。下面我们来深入了解信号量和PV操作。
我们知道进程是由程序和数据组成的。简单说进程就是操作系统执行中的程序代码。一段程序代码可以同时由多个进程在多用户的操作系统中执行,这就是并发执行程序,或者可以理解为并发进程。
我们来看一个例子。我们有如下代码的一个程序A:
function A(){
getSomeSystemResource();
}
A();
这个程序主要实现了一个函数A
这个函数中调用了一些系统资源。然而这些系统资源仅允许同时只有1个进程来访问。我们同时启动10个A程序,就会产生$A_0$
、$A_1$
、$A_2$
、$A_3$
、$A_4$
、$A_5$
、$A_6$
、$A_7$
、$A_8$
、$A_9$$
这样10个进程。这10个进程同执行到A函数时,由于资源是独占的所以程序执行会出错。
我们在程序A
之外声明一个全局变量S
:
int S=1;
然后修改我们的程序:
P(S);
A();
V(S);
这样,我们就可以同时运行10个A程序,并且他们可以合理的调用系统资源。我们把程序从函数中拿出来,再来看一下PV操作和信号量S是如何工作的。
//P操作
S=S-1;
if(S<0){
//阻塞当前进程的执行
}
/**临界区**/
A();
/**临界区**/
//V操作
S=S+1;
if(S<=0){
//唤醒一个阻塞在S上的进程
}
我们可