学科前沿技术专题第四章
1、 了解开源文化
开放源码软件运动是计算机科学领域的一种文化现象,源自黑客对智慧成果共享、自由的追求。开源运动发展到现在,这种能够积极促进人类文明发展的文化已经渗透到信息、教育、健康等领域,融入了哲学范畴。
开放源码运动的史前史包括了整个Unix,自由软件和黑客文化的历史。“开放源码”一词来源于1997年春天在加州的Palo Alto召开的一个所谓“纯粹程序员”参与的战略研讨会。参加会议的有Todd Anderson, 来自Foresight研究所Chris Peterson, 来自Linux国际协会的John Hall 和Larry Augustin,有硅谷Linux用户协会的Sam Ockman,以及Eric Raymond。 它们关系的是寻找一种方式,来像以前躲避自由软件的人们来推广这种思想,自由软件和自由软件基金会的反商业信条让很多人对自由软件敬而远之。在Eric Raymond的坚持下,他们一致通过了用新的术语:OpenSource(开源软件)来描述他们所推进的软件。
1998年2月23日 网景宣布它将发布Navigator浏览器的源代码成为开源软件发展历史的转折点,经过一番激烈争辩,“开放源码”取代“自由软件”成为黑客们对开放原始码软件的代名词。
编辑本段开源软件与自由软件
"开源软件"就是源代码开放的软件。
由于开源思想源于计算机软件界,所以发展至今,计算机类开源产品的种类、数量都是很多的,使用也比较广泛。如在操作系统领域,Linux在服务器市场就占据了相当大的份额,并持续扩张,IBM公司销售的薄片式服务器中大约有75%就运行着Linux操作系统;Web方面,全球有百分之五十以上的Web服务器在使用开源的Apache系统;数据库,有针对互联网应用的轻量级数据库MySQL,也有针对大型应用的PostgreSQL,中国最大的门户网站Sina的后端数据库就采用了开源的MySQL数据库软件。其他还有各种非常流行的软件,如GNU的编辑软件Emacs、Linux的图形处理界面Xfree86、排版软件TeX等等,都获得了广泛的应用。
开源被很多黑客认为是计算机科学的一种文化复兴,是计算机科学真正成为科学并能够与其他科学一起同步发展的手段。开源发展到今天,不仅仅有数以万计的黑客在积极地参与,像IBM、HP、CA、SUN等一些软硬件厂商也在加大在开源方面的投入并积极向开源社区贡献优秀开源软件,因为开源催化了软件业快速向服务也蜕变的速度并为IBM这样的硬件和集成服务提供商提供了新的商机。
编辑本段开源软件与许可证授权
获得开放软件源码是免费的,但对所获取源码的使用却需要遵循该开源软件所作的许可声明。开源软件常用的许可证方式包括BSD、Apache Licence、GPL等,其中GNU的GPL被最多的开源软件组织所采用。开源软件许可模式主要分为两类:一类是copyleft,一类是non-copyleft。copyleft许可,比如GNU GPL类型,坚持认为在开源软件基础上作修改后的软件,仍旧是自由软件。
自1998年2月美国OSI成立之后,经其正式认可的开源许可类型已达30种(2002年1月止)。同时,在GNU工程/自由软件基金会的"自由软件许可"页面上包含了15种与GPL兼容的自由软件许可、2种文档许可以及总共42个软件许可再加上4种非软件许可。
编辑本段一些著名开源许可类型
(1)BSD许可--它要求版权和著者身份申明;
(2)GPL--通用公共许可("CopyLeft")禁止派生或发行产品的限制;
(3)LGPL--库/次级GPL,它不同于GPL许可证,在这个许可证下,库(函
数库)可以自由地联接到专有软件;
(4)Artistic许可--使作者保持对进一步开发的控制。
(5)Netscape公共许可(NPL)--基于GPL精神的新的许可证,但是保持
了对源码更多的控制和所有权;
(6)公共域(Public Domain)--通常用于决定性的算法以鼓励广泛使用,通常由美国政府使用。
编辑本段BSD
BSD是"Berkely Software Distribution"的缩写,意思是"伯克利软件发行版"。显然,BSD这个名称并不是我们现在所理解的操作系统,而且其原意也并非简单的操作系统,而是一整套软件发行版的统称。从软件发行版到操作系统的演变是有历史过程的,这一点对FreeBSD很重要。
BSD于1983年8月正式发布,在18个月内就签发了1000多份站点许可证,是非常具有知名度的版本。到了1986年6月,4.3BSD发布,而到了1988年,CSRG发布了4.3BSD-Tahoe,这是第一个把BSD内核分解为依赖于机器和独立于机器的两部分的版本,这是非常有价值的,它使BSD得以移植到众多不同的体系结构中。
由于BSD使用了AT&T Unix的部分源代码,当AT&T源代码许可证费用不断增加的时候,一些希望能够使用BSD代码为PC生产基于TCP/IP联网产品的厂商要求Berkely将AT&T代码从BSD发行版中分离出来,并给他们签发单独的许可证条款,而不需要AT&T的源代码许可证。因此,到了1989年6月,一个完全没有AT&T Unix代码的BSD版本诞生了,称之为"Networking Release 1"。这是第一套由Berkely发布的自由可再发行(freely-redistributable)的代码,,它允许被授权的用户以源代码或者二进制的形式发布修改过的或为修改过的代码,并且可以不向Berkely申报版税,唯一要求是在源代码文件中原封不动的保留Berkely的版权声明,并且在含有以上代码的其他产品文档中声明其产品包括来自于加州大学和其他贡献者的代码。这就是著名的BSD许可证的起源。
GPL许可证是自由软件的应用最广泛的软件许可证,人们可以修改程式的一个或几个副本或程式的任何部分,以此形成基於这些程式的衍生作品。必须在修改过的档案中附有明显的说明:您修改了此一档案及任何修改的日期。您必须让您发布或出版的作品,包括本程式的全部或一部分,或内含本程式的全部或部分所衍生的作品,允许第三方在此许可证条款下使用,并且不得因为此项授权行为而收费。
最普遍的是开放源代码后,满足GPL许可,然后通过发行、咨询、增加用户定制功能来收费。另一种是将开放源代码和有版权的软件捆绑发行,这样,赚取版权费用。 一种是通过开放全部或部分源代码,收集补丁程序,并满足用户知情权的要求。作为商业软件的补充。通过发行多许可证的方式,从其他许可证赚钱。通过开放源代码和免费使用赚取垄断标准的地位。
LGPL是GPL的派生,它是微软件库尔设计的。与GPL不同,LGPL化的程序可以合并到专有版权程序中。与LINUX以其提供的C语言库就是LGPL许可的实例。
NPL是1998年网景公司把Netscape开源之后,由Netscape公司以公众可以对他进行测试版本测试的形势提出的,它试图在商业企业推广自由软件开发和保护自由软件开发之间达成一种妥协,受NPL约束的代码在授权给第三方时的条款与NPL的不同使NPL无法让开源社区接受。
对于将来提供开源软件的人来说,一般情况下从上述一些许可协议中选择一种既可,而没有必要花费精力再去编写新的许可协议。
2、写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率。程序越精简越好,计算机语言不限。例如,可以实现下面三种情况:
(1) CPU的占用率固定在50%,为一条直线:
(2) CPU的占用率为一条直线,但是具体占用率由命令行参数决定(参数范围1~100):
(3) CPU的占用率状态是一个正选曲线。--摘之《编程之美》
控制CPU占用率,因为要调用Windows的API,要考虑到多核、超线程的情况,要考虑到不同版本的Windows的计时相关的API的精度不同,使问题变得相当复杂,若再考虑其它程序的CPU占用率,则该问题则变得很复杂。(taskmgr调用了一个未公开的API)。
如果不考虑其它程序的CPU占用情况,可以在每个核上开一个线程,运行指定的函数,实现每个核的CPU占用率相同。
要让CPU的占用率,呈函数 y = calc(t) (0 <= y <= 1, t为时间,单位为ms )分布,只要取间隔很短的一系列点,认为在某个间隔内,y值近似不变。
设间隔值为GAP,显然在指定t值附近的GAP这段时间内,
CPU占用时间为:busy = GAP * calc(t),
CPU空闲时间为:idle = GAP – busy
如果CPU占用率曲线不是周期性变化,就要对每个t值都要计算一次,否则,可以只计算第一个周期内的各个t值,其它周期的直接取缓存计算结果。
以CPU占用率为正弦曲线为例,显然:y = 0.5 * (1 + sin(a * t + b))
其周期T = 2 * PI / a (PI = 3.1415927),可以指定T值为60s即60000ms,则可以确定a值为 2 * PI / T, 若在这60000ms内我们计算200次(c = 200),则GAP值为 T / c = 300ms.也就是说,只要确定了周期和计算次数,其它几个参数也都确定下来。
对CPU核数的判断,书上是调用GetProcessorInfo,其实可以直接调用GetSystemInfo,SYSTEM_INFO结构的dwNumberOfProcessors成员就是核数。
完整代码:
#include<cstdio>
#include<cmath>
#include<windows.h>
const int PERIOD = 60 * 1000; //60,000 ms
const int COUNT = 300;
const double GAP_LINEAR = 100;
const double PI = 3.1415926535898;
const double GAP = (double)PERIOD / COUNT;
const double FACTOR = 2 * PI / PERIOD;
typedef double Func(double);
inline DWORD get_time() { return GetTickCount(); }
double calc_sin(double x) { return (1 + sin(FACTOR * x)) / 2;}
static double Ratio = 0.7;
void set_ratio()
{
double ret = 0.0;
printf("Ratio:([0,1]) ");
scanf("%lf", &ret);
if (ret < 0.0 || ret > 1.0) ret = 0.5;
Ratio = ret;
}
void solve_nonperiod(Func *calc)
{
double tb = 0;
while(1) {
unsigned ta = get_time();
double r = calc(tb);
if (r < 0 || r > 1) r = 1;
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
//tb += GAP;
tb += get_time() - ta;
}
}
void solve_period(Func *calc)
{
double x = 0.0;
double cache[COUNT];
for (int i = 0; i < COUNT; ++i, x += GAP)
cache[i] = calc(x);
int count = 0;
while(1) {
unsigned ta = get_time();
if (count >= COUNT) count = 0;
double r = cache[count++];
DWORD busy = r * GAP;
while(get_time() - ta < busy) {}
Sleep(GAP - busy);
}
}
void solve_linear(Func*)
{
const unsigned BUSY = Ratio * GAP_LINEAR;
const unsigned IDLE = (1 - Ratio) * GAP_LINEAR;
while(1) {
unsigned ta = get_time();
while(get_time() - ta < BUSY) {}
Sleep(IDLE);
}
}
void run(unsigned index = 0, double ratio = -1.0)
{
typedef void Solve(Func *calc);
Func *func[] = { calc_sin};
Func *calc = func[0];
Solve *solve_func[] = { solve_linear, solve_period, solve_nonperiod };
if (index >= sizeof(solve_func) / sizeof(solve_func[0])) index = 0;
Solve *solve = solve_func[index];
if (solve == solve_linear) {
if (ratio >= 0 && ratio <= 1) Ratio = ratio;
else set_ratio();
}
const int MAX_CPUS = 32;
HANDLE handle[MAX_CPUS];
DWORD thread_id[MAX_CPUS];
SYSTEM_INFO info;
GetSystemInfo(&info);
const int num = info.dwNumberOfProcessors;
for (int i = 0; i < num; ++i) {
if ((handle[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)solve,
(VOID*)calc, 0, &thread_id[i])) != NULL)
SetThreadAffinityMask(handle[i], i + 1);
}
WaitForSingleObject(handle[0],INFINITE);
}
int main()
{
run(0, 0.5);
//run(0);
//run(1);
//run(1);
}