2006年09月22日

heap:是由malloc之类函数分配的空间所在地。地址是由低向高增长的。

stack:是自动分配变量,以及函数调用的时候所使用的一些空间。地址是由高向低减少的。

heap:  例如malloc 与new出来的东西用指针指向了它,所指的地方就是heap

stack: 就是我们平时所说的局部变量

一、预备知识—程序的内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值 等。其操作方式类似于数据结构中的栈。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能 由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初 始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 – 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

二、例子程序

这是一个前辈写的,非常详细

//main.cpp

int a = 0; 全局初始化区

char *p1; 全局未初始化区

main()

{

   int b; 栈

   char s[] = "abc"; 栈

   char *p2; 栈

   char *p3 = "123456"; 123456\0在常量区,p3在栈上。

   static int c =0; 全局(静态)初始化区

   p1 = (char *)malloc(10);

   p2 = (char *)malloc(20);

        分配得来得10和20字节的区域就在堆区。

  strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"1
          23456"优化成一个地方。

}

二、堆和栈的理论知识

2.1申请方式

stack:

由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间

heap:

需要程序员自己申请,并指明大小,在c中malloc函数

如p1 = (char *)malloc(10);

在C++中用new运算符

如p2 = (char *)malloc(10);

但是注意p1、p2本身是在栈中的。

2.2 申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提 示栈溢出。

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申 请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

2.3申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

2.4申请效率的比较:

栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活

2.5堆和栈中的存储内容

栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";

char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在运行时刻赋值的;

而bbbbbbbbbbb是在编译时就确定的;

但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

比如:

#include <stdio.h>

void main()

{

char a = 1;

char c[] = "1234567890";

char *p ="1234567890";

a = c[1];

a = p[1];

return;

}

对应的汇编代码

10: a = c[1];

00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

0040106A 88 4D FC mov byte ptr [ebp-4],cl

11: a = p[1];

0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]

00401070 8A 42 01 mov al,byte ptr [edx+1]

00401073 88 45 FC mov byte ptr [ebp-4],al

第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。

2.7小结:

堆和栈的区别可以用如下的比喻来看出:

使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
堆和栈的区别主要分:

操作系统方面的堆和栈,如上面说的那些,不多说了。

还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。

虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。

    由于donews的速度有时真让人无法忍受,所以昨晚就没有写今天的计划,但是心理自有数. 今天早上把数据结构中的栈和队列看了一下,又浏览了一遍串那一章的知识, 当中遇到堆和栈两个非常重要的概念,对于一个程序员来说,弄清楚堆和栈非常必要,我从喻信上找到了相关的两篇文章,已经转载到C语言专区.

    下午把数字信号处理中的数字滤波器设计一章看完了,这是这门课重点讨论的内容,对于IIR滤波器和FIR滤波器各自的特点,网络结构,实现方式都有了了解,由于FIR滤波器可以设计成线性相位,所以用途非常广泛,FIR滤波器的设计有窗函数法,这个应该算比较重要的内容。

     另外数字滤波器相对于模拟滤波器来说,有如下优点:

     1. 精度和稳定性高。

     2.  改变系统函数比较容易,因而比较灵活。

     3. 不存在阻抗匹配问题。

     4. 便于大规模集成。

     5. 可以实现多位滤波。

      比较一下IIR滤波器和FIR滤波器,FIR滤波器的独特优点就是可以得到线性相位,此外,FIR滤波器的极点都位于原点,所以FIR滤波器总是稳定的。FIR滤波器采用非递归结构,另外FIR滤波器的冲击相应由于是有限长的,所以可以用线性卷积来表示,而线性卷积的计算可以采用快速傅立叶变化FFT,大大提高了运算效率。

     IIR滤波器可以使用比FIR滤波器少的阶数来满足相同的技术指标(频率取样法)

     关于数字滤波器就先说这么多,晚上完成了暑期实习报告,明天下午要开暑期实习结束大会。

  

 

 

 

2006年09月20日

今天把通信原理和高频的剩下的东西看了一下,高频那里还有角度调制和解调没看,其实原理都懂了,不过不做题目总觉得不行,这些在以后慢慢串插吧。

明天要看数据结构了,数据结构很重要,因为对于C程序开发来说,数据结构非常重要,什么队列,堆栈之类的,都是常用的结构。所以说大学开的各门功课还是很有用的。明天还要继续把通信原理专题写完,其实的写的东西很多,慢慢来吧。 另外把暑期实习报告再写一下,争取后天写完报告。

通信系统可以分为模拟通信系统和数字通信系统,按是否采用调制可分为基带系统和调制系统。
现在采用最多的通信系统是数字通信系统。相对与模拟通信系统它主要有如下几个好处:
(1)         数字信号本身具有较强的抗噪能力,采用再生中继,纠错编码等差错控制措施可以使传输距离不受限制。
(2)         数字信号容易复用,也容易压缩。
(3)         容易加密
但数字通信系统也有如下缺点:
(1)            相同的容量下,传输带宽比模拟传输时宽。
(2)            数字通信系统需要复杂的同步系统。
    那么为什么要采用调制系统呢?
 模拟信号载波调制的目的:
1.   对于无线通信来说,是采用电磁波在空间传送信息的,但是发送天线的长度必须和电磁波的波长相比拟,才能将电磁波辐射出去。对于音频信号来说,其频率在20Hz 20KHz,波长范围在1.5Km1500Km。因此要想把音频信号辐射出去,发送天线的长度必须大于在1.51500Km范围内?想想有这么长的天线吗?因此为了缩短天线的长度,必须提高信号的频率。也就是把基带信号调制到高频载波上。
2.   各个电台的音频信号频率范围都是一样的,所以为了便于不同电台相同信号的同时接受,可以把基层带信号装载到不同的载频上。
3.   可实现的回路带宽。(注:这一点的理解应该是基带信号的频谱范围很大的缘故,但是调制为什么会减少带宽呢?我还没有弄明白)
还有一种说法是这样的:
1. 将基带信号变换成适合在信道中传输的已调信号
2. 改善系统的抗噪声性能
3. 实现信道的多路复用
数字信号载波调制的目的:
首先说一下信源编码和信道编码。
信源编码的目的是提高信源的效率,去除冗余度,也就是提高通信的有效性。
信道编码以提到通信的可靠性为目的,具体的表现在如下两个方面:
(1)要求码列的频谱特性适应通道频谱特性,从而使传输过程中能量损失最小,提高信号能量与噪声能量的比例,减小发生差错的可能性,提高传输效率。
(2)增加纠错能力,使得即便出现差错,也能得到纠正。
    数字信号的载波调制是信道编码的一部份
一般传输通道的频率特性总是有限的,即有上、下限频率,超过此界限就不能进行有效的传输。如果数字信号流的频率特性与传输通道的频率特性很不相同,那么信号中的很多能量就会失去,信噪比就会降低,使误码增加,而且还会给邻近信道带来很强的干扰。因此,在传输前要对数字信号进行某种处理,减少数字信号中的低频分量和高频分量,使能量向中频集中,或者通过某种调制过程进行频谱的搬移。这两种处理都可以被看作是使信号的频谱特性与信道的频谱特性相匹配。
通信原理主要讲述的是数字基带系统和数字调制系统,模拟调制系统在高频里面讲述。
在往下说之前,先提一下通信系统的性能指标:有效性和可靠性,因为任何一个系统的设计都是为了满足一定的指标的。
1.   模拟通行系统的性能指标: 模拟通信系统占用的信道带宽必须不小于所传输的信号带宽,用它占用的信道带宽来说明其有效性,其可靠性用解调器输出信号的信噪比来衡量,信噪比越大,可靠性越好。
2.   在数字通信系统中,在保证无码间串绕的条件下,信号占用的带宽可以小于信号带宽,且信号带宽与进制数有关,故在数字通信系统中用信道的频带利用率来描述有效性。可靠性用误码率或者误比特率来表示,误码率定义为错误的码元数与传输总码元数的比值。
数字基带系统
数字基带系统原理框图如下:
数字基带系统原理框图
码型变换器:信号适合于信道传输
发送滤波器:低通滤波器,滤除高频成分,提高频带利用率
接受滤波器:低通滤波器,滤除带外噪声,与发送滤波器,信道一起构成无码间串绕条件。
待续
 

       今天的任务算比较重,要看完通信原理和高频。尽管复试的时候题目不难,但是自己看书总是会遇到各种各样的问题,也想着去弄懂它,有位老师对我说过:“艺多不压身!”,我一直以为看书不能以应付考试的方式去看,那样不但看的不情愿,效果也不好。“书到用时方恨少”,应该以一种把读书作为自己知识积累的姿态去学习,那样不但学的快乐,效果也有了

       简单说一下通信原理和高频这两门科主要涉及的知识。

      1. 高频,毫无疑问,高频主要研究的对象就是处于高频工作的电路,这是相对于低频电路而言的。因此对于三级管这样的器件我们在画它的等效电路时,必须考虑级间电容的影响。 为什么要使用高频? 调幅 、检波 、混频率,都是把低频频谱搬移到搞频载频上。为什么不直接用低频发送语音、图象等信号呢? 带着这些问题去看书,如果看完能够有所领悟,那么你对这门课已经掌握了。

      2. 通信原理,这是所有通信专业最重要的一门课,主要讲述的是数字基带系统和数字调制系统发送和接受的一些问题,因为计算机处理的是数字型号,所以光纤,电缆中传送的都不再是模拟信号,工程中应用的都是数字调制系统,模拟电路里面调制的目的非常明了,数字电路里面为什么要进行调制呢?

 

 

2006年09月18日

原文出处:http://www-128.ibm.com/developerworks/cn/linux/l-proc.html#downloads

 

 

 

/proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux® 内核空间和用户空间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的。本文对 /proc 虚拟文件系统进行了介绍,并展示了它的用法。

最初开发 /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置。

/proc 文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。实际上我们并不会同时需要实现这两点,但是本文将向您展示如何配置这个文件系统进行输入和输出。

尽管像本文这样短小的一篇文章无法详细介绍 /proc 的所有用法,但是它依然对这两种用法进行了展示,从而可以让我们体会一下 /proc 是多么强大。清单 1 是对 /proc 中部分元素进行一次交互查询的结果。它显示的是 /proc 文件系统的根目录中的内容。注意,在左边是一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。由于在 GNU/Linux 中创建的第一个进程是 init 进程,因此它的 process-id1。然后对这个目录执行一个 ls 命令,这会显示很多文件。每个文件都提供了有关这个特殊进程的详细信息。例如,要查看 init 的 command-line 项的内容,只需对 cmdline 文件执行 cat 命令。

/proc 中另外一些有趣的文件有:cpuinfo,它标识了处理器的类型和速度;pci,显示在 PCI 总线上找到的设备;modules,标识了当前加载到内核中的模块。

清单 1. 对 /proc 的交互过程


[root@plato]# ls /proc
1     2040  2347  2874  474          fb           mdstat      sys
104   2061  2356  2930  9            filesystems  meminfo     sysrq-trigger
113   2073  2375  2933  acpi         fs           misc        sysvipc
1375  21    2409  2934  buddyinfo    ide          modules     tty
1395  2189  2445  2935  bus          interrupts   mounts      uptime
1706  2201  2514  2938  cmdline      iomem        mtrr        version
179   2211  2515  2947  cpuinfo      ioports      net         vmstat
180   2223  2607  3     crypto       irq          partitions
181   2278  2608  3004  devices      kallsyms     pci
182   2291  2609  3008  diskstats    kcore        self
2     2301  263   3056  dma          kmsg         slabinfo
2015  2311  2805  394   driver       loadavg      stat
2019  2337  2821  4     execdomains  locks        swaps
[root@plato 1]# ls /proc/1
auxv     cwd      exe  loginuid  mem     oom_adj    root  statm   task
cmdline  environ  fd   maps      mounts  oom_score  stat  status  wchan
[root@plato]# cat /proc/1/cmdline
init [5]
[root@plato]#

清单 2 展示了对 /proc 中的一个虚拟文件进行读写的过程。这个例子首先检查内核的 TCP/IP 栈中的 IP 转发的目前设置,然后再启用这种功能。

清单 2. 对 /proc 进行读写(配置内核)


[root@plato]# cat /proc/sys/net/ipv4/ip_forward
0
[root@plato]# echo "1" > /proc/sys/net/ipv4/ip_forward
[root@plato]# cat /proc/sys/net/ipv4/ip_forward
1
[root@plato]#

另外,我们还可以使用 sysctl 来配置这些内核条目。有关这个问题的更多信息,请参阅 参考资料 一节的内容。

顺便说一下,/proc 文件系统并不是 GNU/Linux 系统中的惟一一个虚拟文件系统。在这种系统上,sysfs 是一个与 /proc 类似的文件系统,但是它的组织更好(从 /proc 中学习了很多教训)。不过 /proc 已经确立了自己的地位,因此即使 sysfs 与 /proc 相比有一些优点,/proc 也依然会存在。还有一个 debugfs 文件系统,不过(顾名思义)它提供的更多是调试接口。debugfs 的一个优点是它将一个值导出给用户空间非常简单(实际上这不过是一个调用而已)。

内核模块简介

可加载内核模块(LKM)是用来展示 /proc 文件系统的一种简单方法,这是因为这是一种用来动态地向 Linux 内核添加或删除代码的新方法。LKM 也是 Linux 内核中为设备驱动程序和文件系统使用的一种流行机制。

如果您曾经重新编译过 Linux 内核,就可能会发现在内核的配置过程中,有很多设备驱动程序和其他内核元素都被编译成了模块。如果一个驱动程序被直接编译到了内核中,那么即使这个驱动程序没有运行,它的代码和静态数据也会占据一部分空间。但是如果这个驱动程序被编译成一个模块,就只有在需要内存并将其加载到内核时才会真正占用内存空间。有趣的是,对于 LKM 来说,我们不会注意到有什么性能方面的差异,因此这对于创建一个适应于自己环境的内核来说是一种功能强大的手段,这样可以根据可用硬件和连接的设备来加载对应的模块。

下面是一个简单的 LKM,可以帮助您理解它与在 Linux 内核中看到的标准(非动态可加载的)代码之间的区别。清单 3 给出了一个最简单的 LKM。(可以从本文的 下载 一节中下载这个代码)。

清单 3 包括了必须的模块头(它定义了模块的 API、类型和宏)。然后使用 MODULE_LICENSE 定义了这个模块使用的许可证。此处,我们定义的是 GPL,从而防止会污染到内核。

清单 3 然后又定义了这个模块的 initcleanup 函数。my_module_init 函数是在加载这个模块时被调用的,它用来进行一些初始化方面的工作。my_module_cleanup 函数是在卸载这个模块时被调用的,它用来释放内存并清除这个模块的踪迹。注意此处 printk 的用法:这是内核的 printf 函数。KERN_INFO 符号是一个字符串,可以用来对进入内核回环缓冲区的信息进行过滤(非常类似于 syslog)。

最后,清单 3 使用 module_initmodule_exit 宏声明了入口函数和出口函数。这样我们就可以按照自己的意愿来对这个模块的 initcleanup 函数进行命名了,不过我们最终要告诉内核维护函数就是这些函数。

清单 3. 一个简单的但可以正常工作的 LKM(simple-lkm.c)


#include <linux/module.h>

/* Defines the license for this LKM */
MODULE_LICENSE("GPL");

/* Init function called on module entry */
int my_module_init( void )
{
  printk(KERN_INFO "my_module_init called.  Module is now loaded.\n");

  return 0;
}

/* Cleanup function called on module exit */
void my_module_cleanup( void )
{
  printk(KERN_INFO "my_module_cleanup called.  Module is now unloaded.\n");

  return;
}

/* Declare entry and exit functions */
module_init( my_module_init );
module_exit( my_module_cleanup );

清单 3 尽管非常简单,但它却是一个真正的 LKM。现在让我们对其进行编译并在一个 2.6 版本的内核上进行测试。2.6 版本的内核为内核模块的编译引入了一种新方法,我发现这种方法比原来的方法简单了很多。对于文件 simple-lkm.c,我们可以创建一个 makefile,其惟一内容如下:


obj-m += simple-lkm.o

要编译 LKM,请使用 make 命令,如清单 4 所示。

清单 4. 编译 LKM


[root@plato]# make -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules
make: Entering directory `/usr/src/linux-2.6.11'
  CC [M]  /root/projects/misc/module2.6/simple/simple-lkm.o
  Building modules, stage 2.
  MODPOST
  CC      /root/projects/misc/module2.6/simple/simple-lkm.mod.o
  LD [M]  /root/projects/misc/module2.6/simple/simple-lkm.ko
make: Leaving directory `/usr/src/linux-2.6.11'
[root@plato]#

结果会生成一个 simple-lkm.ko 文件。这个新的命名约定可以帮助将这些内核对象(LKM)与标准对象区分开来。现在可以加载或卸载这个模块了,然后可以查看它的输出。要加载这个模块,请使用 insmod 命令;反之,要卸载这个模块,请使用 rmmod 命令。lsmod 可以显示当前加载的 LKM(参见清单 5)。

清单 5. 插入、检查和删除 LKM


[root@plato]# insmod simple-lkm.ko
[root@plato]# lsmod
Module                  Size  Used by
simple_lkm              1536  0
autofs4                26244  0
video                  13956  0
button                  5264  0
battery                 7684  0
ac                      3716  0
yenta_socket           18952  3
rsrc_nonstatic          9472  1 yenta_socket
uhci_hcd               32144  0
i2c_piix4               7824  0
dm_mod                 56468  3
[root@plato]# rmmod simple-lkm
[root@plato]#

注意,内核的输出进到了内核回环缓冲区中,而不是打印到 stdout 上,这是因为 stdout 是进程特有的环境。要查看内核回环缓冲区中的消息,可以使用 dmesg 工具(或者通过 /proc 本身使用 cat /proc/kmsg 命令)。清单 6 给出了 dmesg 显示的最后几条消息。

清单 6. 查看来自 LKM 的内核输出


[root@plato]# dmesg | tail -5
cs: IO port probe 0xa00-0xaff: clean.
eth0: Link is down
eth0: Link is up, running at 100Mbit half-duplex
my_module_init called.  Module is now loaded.
my_module_cleanup called.  Module is now unloaded.
[root@plato]#

可以在内核输出中看到这个模块的消息。现在让我们暂时离开这个简单的例子,来看几个可以用来开发有用 LKM 的内核 API。


回页首

集成到 /proc 文件系统中

内核程序员可以使用的标准 API,LKM 程序员也可以使用。LKM 甚至可以导出内核使用的新变量和函数。有关 API 的完整介绍已经超出了本文的范围,因此我们在这里只是简单地介绍后面在展示一个更有用的 LKM 时所使用的几个元素。

创建并删除 /proc 项

要在 /proc 文件系统中创建一个虚拟文件,请使用 create_proc_entry 函数。这个函数可以接收一个文件名、一组权限和这个文件在 /proc 文件系统中出现的位置。create_proc_entry 的返回值是一个 proc_dir_entry 指针(或者为 NULL,说明在 create 时发生了错误)。然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。create_proc_entry 的原型和 proc_dir_entry 结构中的一部分如清单 7 所示。

清单 7. 用来管理 /proc 文件系统项的元素


struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode,
                                             struct proc_dir_entry *parent );

struct proc_dir_entry {
	const char *name;			// virtual file name
	mode_t mode;				// mode permissions
	uid_t uid;				// File's user id
	gid_t gid;				// File's group id
	struct inode_operations *proc_iops;	// Inode operations functions
	struct file_operations *proc_fops;	// File operations functions
	struct proc_dir_entry *parent;		// Parent directory
	...
	read_proc_t *read_proc;			// /proc read function
	write_proc_t *write_proc;		// /proc write function
	void *data;				// Pointer to private data
	atomic_t count;				// use count
	...
};

void remove_proc_entry( const char *name, struct proc_dir_entry *parent );

稍后我们就可以看到如何使用 read_procwrite_proc 命令来插入对这个虚拟文件进行读写的函数。

要从 /proc 中删除一个文件,可以使用 remove_proc_entry 函数。要使用这个函数,我们需要提供文件名字符串,以及这个文件在 /proc 文件系统中的位置(parent)。这个函数原型如清单 7 所示。

parent 参数可以为 NULL(表示 /proc 根目录),也可以是很多其他值,这取决于我们希望将这个文件放到什么地方。表 1 列出了可以使用的其他一些父 proc_dir_entry,以及它们在这个文件系统中的位置。

表 1. proc_dir_entry 快捷变量

proc_dir_entry 在文件系统中的位置
proc_root_fs /proc
proc_net /proc/net
proc_bus /proc/bus
proc_root_driver /proc/driver

回调函数

我们可以使用 write_proc 函数向 /proc 中写入一项。这个函数的原型如下:


int mod_write( struct file *filp, const char __user *buff,
               unsigned long len, void *data );

filp 参数实际上是一个打开文件结构(我们可以忽略这个参数)。buff 参数是传递给您的字符串数据。缓冲区地址实际上是一个用户空间的缓冲区,因此我们不能直接读取它。len 参数定义了在 buff 中有多少数据要被写入。data 参数是一个指向私有数据的指针(参见 清单 7)。在这个模块中,我们声明了一个这种类型的函数来处理到达的数据。

Linux 提供了一组 API 来在用户空间和内核空间之间移动数据。对于 write_proc 的情况来说,我们使用了 copy_from_user 函数来维护用户空间的数据。

读回调函数

我们可以使用 read_proc 函数从一个 /proc 项中读取数据(从内核空间到用户空间)。这个函数的原型如下:


int mod_read( char *page, char **start, off_t off,
              int count, int *eof, void *data );

page 参数是这些数据写入到的位置,其中 count 定义了可以写入的最大字符数。在返回多页数据(通常一页是 4KB)时,我们需要使用 startoff 参数。当所有数据全部写入之后,就需要设置 eof(文件结束参数)。与 write 类似,data 表示的也是私有数据。此处提供的 page 缓冲区在内核空间中。因此,我们可以直接写入,而不用调用 copy_to_user

其他有用的函数

我们还可以使用 proc_mkdirsymlinks 以及 proc_symlink 在 /proc 文件系统中创建目录。对于只需要一个 read 函数的简单 /proc 项来说,可以使用 create_proc_read_entry,这会创建一个 /proc 项,并在一个调用中对 read_proc 函数进行初始化。这些函数的原型如清单 8 所示。

清单 8. 其他有用的 /proc 函数


/* Create a directory in the proc filesystem */
struct proc_dir_entry *proc_mkdir( const char *name,
                                     struct proc_dir_entry *parent );

/* Create a symlink in the proc filesystem */
struct proc_dir_entry *proc_symlink( const char *name,
                                       struct proc_dir_entry *parent,
                                       const char *dest );

/* Create a proc_dir_entry with a read_proc_t in one call */
struct proc_dir_entry *create_proc_read_entry( const char *name,
                                                  mode_t mode,
                                                  struct proc_dir_entry *base,
                                                  read_proc_t *read_proc,
                                                  void *data );

/* Copy buffer to user-space from kernel-space */
unsigned long copy_to_user( void __user *to,
                              const void *from,
                              unsigned long n );

/* Copy buffer to kernel-space from user-space */
unsigned long copy_from_user( void *to,
                                const void __user *from,
                                unsigned long n );

/* Allocate a 'virtually' contiguous block of memory */
void *vmalloc( unsigned long size );

/* Free a vmalloc'd block of memory */
void vfree( void *addr );

/* Export a symbol to the kernel (make it visible to the kernel) */
EXPORT_SYMBOL( symbol );

/* Export all symbols in a file to the kernel (declare before module.h) */
EXPORT_SYMTAB



回页首

通过 /proc 文件系统实现财富分发

下面是一个可以支持读写的 LKM。这个简单的程序提供了一个财富甜点分发。在加载这个模块之后,用户就可以使用 echo 命令向其中导入文本财富,然后再使用 cat 命令逐一读出。

清单 9 给出了基本的模块函数和变量。init 函数(init_fortune_module)负责使用 vmalloc 来为这个点心罐分配空间,然后使用 memset 将其全部清零。使用所分配并已经清空的 cookie_pot 内存,我们在 /proc 中创建了一个 proc_dir_entry 项,并将其称为 fortune。当 proc_entry 成功创建之后,对自己的本地变量和 proc_entry 结构进行了初始化。我们加载了 /proc readwrite 函数(如清单 9 和清单 10 所示),并确定这个模块的所有者。cleanup 函数简单地从 /proc 文件系统中删除这一项,然后释放 cookie_pot 所占据的内存。

cookie_pot 是一个固定大小(4KB)的页,它使用两个索引进行管理。第一个是 cookie_index,标识了要将下一个 cookie 写到哪里去。变量 next_fortune 标识了下一个 cookie 应该从哪里读取以便进行输出。在所有的 fortune 项都读取之后,我们简单地回到了 next_fortune

清单 9. 模块的 init/cleanup 和变量


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");

#define MAX_COOKIE_LENGTH       PAGE_SIZE
static struct proc_dir_entry *proc_entry;

static char *cookie_pot;  // Space for fortune strings
static int cookie_index;  // Index to write next fortune
static int next_fortune;  // Index to read next fortune

int init_fortune_module( void )
{
  int ret = 0;

  cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );

  if (!cookie_pot) {
    ret = -ENOMEM;
  } else {

    memset( cookie_pot, 0, MAX_COOKIE_LENGTH );

    proc_entry = create_proc_entry( "fortune", 0644, NULL );

    if (proc_entry == NULL) {

      ret = -ENOMEM;
      vfree(cookie_pot);
      printk(KERN_INFO "fortune: Couldn't create proc entry\n");

    } else {

      cookie_index = 0;
      next_fortune = 0;

      proc_entry->read_proc = fortune_read;
      proc_entry->write_proc = fortune_write;
      proc_entry->owner = THIS_MODULE;
      printk(KERN_INFO "fortune: Module loaded.\n");

    }

  }

  return ret;
}

void cleanup_fortune_module( void )
{
  remove_proc_entry("fortune", &proc_root);
  vfree(cookie_pot);
  printk(KERN_INFO "fortune: Module unloaded.\n");
}

module_init( init_fortune_module );
module_exit( cleanup_fortune_module );

向这个罐中新写入一个 cookie 非常简单(如清单 10 所示)。使用这个写入 cookie 的长度,我们可以检查是否有这么多空间可用。如果没有,就返回 -ENOSPC,它会返回给用户空间。否则,就说明空间存在,我们使用 copy_from_user 将用户缓冲区中的数据直接拷贝到 cookie_pot 中。然后增大 cookie_index(基于用户缓冲区的长度)并使用 NULL 来结束这个字符串。最后,返回实际写入 cookie_pot 的字符的个数,它会返回到用户进程。

清单 10. 对 fortune 进行写入操作所使用的函数


ssize_t fortune_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data )
{
  int space_available = (MAX_COOKIE_LENGTH-cookie_index)+1;

  if (len > space_available) {

    printk(KERN_INFO "fortune: cookie pot is full!\n");
    return -ENOSPC;

  }

  if (copy_from_user( &cookie_pot[cookie_index], buff, len )) {
    return -EFAULT;
  }

  cookie_index += len;
  cookie_pot[cookie_index-1] = 0;

  return len;
}

对 fortune 进行读取也非常简单,如清单 11 所示。由于我们刚才写入数据的缓冲区(page)已经在内核空间中了,因此可以直接对其进行操作,并使用 sprintf 来写入下一个 fortune。如果 next_fortune 索引大于 cookie_index(要写入的下一个位置),那么我们就将 next_fortune 返回为 0,这是第一个 fortune 的索引。在将这个 fortune 写入用户缓冲区之后,在 next_fortune 索引上增加刚才写入的 fortune 的长度。这样就变成了下一个可用 fortune 的索引。这个 fortune 的长度会被返回并传递给用户。

清单 11. 对 fortune 进行读取操作所使用的函数


int fortune_read( char *page, char **start, off_t off,
                   int count, int *eof, void *data )
{
  int len;

  if (off > 0) {
    *eof = 1;
    return 0;
  }

  /* Wrap-around */
  if (next_fortune >= cookie_index) next_fortune = 0;

  len = sprintf(page, "%s\n", &cookie_pot[next_fortune]);

  next_fortune += len;

  return len;
}

从这个简单的例子中,我们可以看出通过 /proc 文件系统与内核进行通信实际上是件非常简单的事情。现在让我们来看一下这个 fortune 模块的用法(参见清单 12)。

清单 12. 展示 fortune cookie LKM 的用法


[root@plato]# insmod fortune.ko
[root@plato]# echo "Success is an individual proposition.  Thomas Watson" > /proc/fortune
[root@plato]# echo "If a man does his best, what else is there?  Gen. Patton" > /proc/fortune
[root@plato]# echo "Cats: All your base are belong to us.  Zero Wing" > /proc/fortune
[root@plato]# cat /proc/fortune
Success is an individual proposition.  Thomas Watson
[root@plato]# cat /proc/fortune
If a man does his best, what else is there?  General Patton
[root@plato]#

/proc 虚拟文件系统可以广泛地用来报告内核的信息,也可以用来进行动态配置。我们会发现它对于驱动程序和模块编程来说都是非常完整的。在下面的 参考资料 中,我们可以学习到更多相关知识。

2006年09月17日

发信人: mutecat ((I love Linux)), 信区: Linux
标  题: 我和Linux
发信站: 武汉白云黄鹤站 (2006年09月16日13:41:45 星期六), 站内信件

  by mutecat@byhh

  题记:

      好久没有在Linux版发表原创了,在自己申请版三之际,谨以此文献给所有热爱

Linux的忠实朋友们,也趁机为自己加点人气^_^

 

一. 我和Linux的缘分

    像很多人一样,我接触Linux也完全是偶然,是听朋友介绍的,然后就糊里糊涂的爱上她

了,呵呵…

    大一快暑假的时候第一次安装Linux,选择的发行版是RedHat 9,和很多新手一样,安装

的过程并不顺利,那时候对电脑软硬件都是一窍不通,别人说ntfs,vfat之类的都是一头

雾水,也不怪自己,上大学之前基本上没碰过电脑。在朋友的帮助下最后还是一切都搞好

了,接下来就开始自己的Linux之旅了。

二. 最初的追求

    当时觉得Linux界面真丑,但是也没介意,毕竟我用它是做服务器的,不是浏览网页的.

进Linux的第一件事情当然是想着如何上网,这个也是别人教我的,因为一开始只会cd,ls

两个命令$@#$@@.下面就开始着手如何让他变得好看一点,试了网上说的各种方法,把系统

搞得崩溃了N次,当然也重装了N次,在这N次重装的过程中,也是一个学习进步的过程,毕竟

什么分区啊,命名啊,grub都要了解一点的. 可是很不幸,最后还是那么丑. 在放弃了美化

界面之后,就开始根据自己的需要装各种软件了,输入法选择了fctix,上bbs用qterm,上

qq用Lumaqq,字符终端输入用zhcon,听歌,看电影用xmms,mplayer,等等. 我想让她在我手

里变得完美,可是那是不现实的,不能用bt,不能看office文档,尽管有openoffice,永中

office,可他们和ms兼容的并不是很好. 可这并不影响我对Linux的好感,为什么? 因为她

是开源的,这意味着你可以学到windows下面学不到的东西,比如对操作系统的认识,有很多

源码可以阅读,也有很好的试验环境.

三. 走上正轨

    可能你看到这个标题有点惊讶,不错,美化界面,拼命寻找各种软件都不是一个Linux

爱好者真正需要的东西,windows漂亮而且安装软件方便,如果我们寻求这些,那么你只会

觉得Linux一无是处,除了开源. Linux真正值得我们去寻求的是什么? 是她下面的各种

应用: 服务器配置,网络开发,内核移植. 学习一样东西最重要的就是去实践,给自己机会

去做,去闯.当时一个师兄做了一个用php和perl制作的网站, 在Linux方面他是我的前辈,

所以我经常和他探讨,他也给我一些任务去完成,比如在网站上添加留言板等等.. 所以

我最初涉及到的Linux下的开发就是php和perl网页编程了.

四. 迷失方向

     php和perl学了一段时间,毫无成就感,因为一点成果都没有. 尽管网上有大量的

资料可以学习,有好的书籍可以参考,照着别人的代码敲了一遍,效果也有了,可是总觉得

哪里出了问题. 网页做出来很丑,可能是这个影响到了积极性,自己没有美工基础,所以

最后还是放弃了.

五. 进入服务器领域

    由于在一个团队里面学习,正好团队里面的服务器是Linux系统,我就承担了’网络管理

员’的职责,刚开始并不能胜任,毕竟是刚接触.但是自己一直努力学习,并向团队里面的各位

前辈讨教.在此期间,有bbs的移植任务,ftp的搭建任务等等,这些都给了我锻炼的机会,也是

这个时候开始有了自信,不管怎么说我也有一两手了啊^_^. Linux在服务器领域的确占有优

势,bbs服务器,ftp服务器,www服务器,socks,squid代理服务器都可以在Linux下搭建.

六. Linux的精髓-内核

    和很多人一样,刚开始接触它,觉得它很高深,可望而不可及. 但是当你慢慢了解它之后

,你就会觉得它真的很神奇. 我曾经想着去读Linux内核源码,热情空前的高涨,选择的书籍

是同济大学赵炯博士的<<Linux内核完全注释>>.当时汇编,操作系统都没学,真是看得一点

信心也没有了,后来向老师讨教,他觉得没有必要去读Linux的源码,所以最后也就不再去读

源码了. 那我们用Linux的内核可以干什么事情呢? 内核裁减,嵌入式开发! 从老师那里得

到一个任务:对Linux最小化裁减,使其胜任网关需求.  很有挑战性吧,呵呵….确实,很有

意思的东西. 这个一做就是一学期,当然整体上完成它我只花了不到一个月的时间,但是测

试,功能的完善花了大量的时间,这段时间是我的Linux水平飞速提高的阶段,一个学期下来

,对Linux系统可谓了如指掌,因为内核裁减,文件系统的裁减这些基本上涵盖了Linux下的所

有东西. 最后完成了裁减的Linux网关,UsbLinux,这是我最引以自豪的事情.

ps: 建议热爱Linux的朋友们不妨试着去裁减一下Linux.

七. Linux下的网络编程

    不管在什么平台下,都会涉及到编程的问题,Linux下的网络开发相对windows下比较

方便. 一开始学编程也是根据别人的文章,写最基本的客户服务器程序,实现通信. 后来

团队里面有了任务,要求写一个网络小软件,这个小软件很有意思,自己也很感兴趣去写,

尽管最后还是没有很完美的去完成它,但是这个过程让我对网络编程产生了兴趣.

后纪:

   有很多话想说,总觉得有点罗嗦,就到此为止吧. 最后想告诉大家,Linux需要你去深入的

研究,需要你去理解它.当你有一天觉得自己很牛的时候,别忘了与大家分享你的经验.


 ┏━━━━┓┏┓    ┏┓┏━━━━┓┏━━━┓┏━━━┓┏━━━┓┏━━━━┓
 ┃┏┓┏┓┃┃┃    ┃┃┗━┓┏━┛┃┏━━┛┃┏━━┛┃ ┏┓ ┃┗━┓┏━┛
 ┃┃┃┃┃┃┃┃    ┃┃    ┃┃    ┃ ____) ┃┃      ┃ ┗┛ ┃    ┃┃
 ┃┃┃┃┃┃┃┗━━┛┃    ┃┃    ┃┗━━┓┃┗━━┓┃┏━┓┃    ┃┃
 ┗┛┗┛┗┛┗━━━━┛    ┗┛    ┗━━━┛┗━━━┛┗┛  ┗┛    ┗┛
                    http://blog.donews.com/mutecat
※ 来源:·武汉白云黄鹤站 bbs.whnet.edu.cn·

2006年09月12日

大学的目的到底是为了什么?李开复教授回答

Q:  大学的目的到底是什么?

A:  我的第四封信就是针对这个问题所写的。
下面是写第四封信之前的一些回答。
读大学的目的不是为了获得一纸文凭。在大学里,最重要的事情是打好基础、学习如何学
习、培养独立思考学习、离开家庭独立的机会、练习与人相处的技巧,这也是人生一次专
注学习的机会。”让大学对自己有用”是自己的责任。如果一个人感觉大学学的”没用”
,很可能是因为自己没有把握自己需要的、并且主动去追求。
1、打好基础:很多学生急迫地想学习”管理”,或其他社会地位高的职位只是。但是,大
学不是”职业培训班”,而是一个能让自己能适应社会,做各种不同的工作的平台。这个
平台的基础就是基础课程。大学时,一定要把一些基础知识–数学、英语、还有你的专业
基础课程,如经济、财务、会计、编程、写作、化学、物理等等–学好。在科技发达的今
天,学这些不变的基础才是最重要的。有些高深的技术,几年后就会改变。而且没有好的
基础,也学不会高深的技术。最后,在中国的大学里,基础课程教授水平还不错,但是高
深的课程,却令很多教授无法胜任。
2、学习的能力:专业知识固然重要,但是大学毕业生更重要的是思想的能力、学习新东西
的能力。因为未来的世界会有很大的改变,所以熟悉旧知识不如有学习新知识的能力。
3、找到你的兴趣:大学阶段最重要的考研准备是通过你对自己专业的体验和对其他专业的
了解而来。你需要明白和验证自己的兴趣和特长所在,以便逐渐确定今后研究生阶段的专
业方向。
大学生活中,很多学生很容易就恢复过去被动的习惯。因此大学生必须尽快地明确自己的
目标。”毕业”不是一个目标。远期的目标是人生目标,中期的目标是职业、出国、读研
。近期的目标是要把英语学好、要去旁听自己有兴趣的课、要通过某些考试、要参加社团
、要提升沟通或演讲的能力。这些目标因人而异,但是每个人都必须考虑自己的兴趣以及
客观条件,并逐步地进行和调整。
4、练习与人相处的能力:第一次离开家门,会开始遇到很多人际间的矛盾,但是未来在社
会里与人相处的能力会越来越重要。你必须培养与人相处的能力。培养的方法很多,打工
、社团、交友都是方法。在学业上了正常轨道的前提下,你可以慢慢去培养。我们不能与
人和睦相处,问题很可能出在自己身上。在他人身上所见到的,往往是自己所反射出去的
态度与情感的回应,就像照镜子一样,我们的表情、态度,可以由他人对我们的表情和态
度上一览无遗。因此,在我们和他人的关系中,如果我们本身不友善,他人对我们也不会
友善。如果我们不信任别人,他们也不会信任我们。如果我们敌视别人,那别人也会敌视
我们。战争的爆发就是这么来的。
5、找合适的老师:尽量找年轻、动手做事的老师。如果要从事研究工作,那么就找从国外
归来的老师;如果将来要参加工作,那么尽量找有过开公司经验的。
6、打好扎实的基础,欲速则不达:很多同学问我该选择什么领域,或该学什么计算机语言
。很多同学才读大二,就决定将来”要做管理”。这类问题一方面没有考虑自己的才华、
兴趣,另一方面太急迫了。大学最重要的是要把基础打好,学东西要按部就班,不可能一
步登天。打基础是苦功夫,不愿吃苦却想成才是不可能的。
7、不要以为在大学功课好就够了:在二十一世纪的今天,人才已是国际的概念。你不能只
因为在你的大学过关或成绩不错就自满。上MIT OCW,去做做MIT的课程、功课、考题。如
果能达到好的成绩,这时候你就可以自信地面向国际了。掌握自学能力很重要,因为中学
和大学的一个分界线是大学里的很多东西需要自学,而且以后走上工作岗位以后很多东西
也需要自学。如果在大学阶段没有学会自学,那么以后很容易就会落后。毕业之后,所有
的学习都要靠自己,所以学习自习是大学期间重要的一环。
8、事分清轻重缓急:人生的每个阶段都有那个阶段最重要最急需完成的事情。比如,在大
学阶段,学习文化课,掌握自学的能力,和开阔视野是最重要的。文化专业课重要,因为
成绩不好会影响毕业,深造或就业,也因为对于很多人来说专业知识和技能影响了他一生
从事的职业,同时因为离开了学校以后再也没有机会这样系统地,专一地,不受任何干扰
的学习机会。
9、开阔视野:因为大学是开阔视野的最好的地方(有图书馆,各科的老师,五湖四海的同
学);更因为很多大学生还没有吃准自己的职业兴趣所在,开阔视野有助于自己找到自己
最感兴趣和最擅长的方面。
10、融会贯通:任何一种能力和修养的培养都不是彼此孤立的。比如,有些专业课有实践
项目,几个同学合作,既是上好专业课的一部分,也是培养合作精神的途径。
11、主要应以学业为主:不要浪费了在大学宝贵的四年。这是你一生有最多独立学习时间
的地方。

12、以目标为中心:你应该想想自己的目标,还有需要培养什么样的能力。

不要认为学什么专业你就一定要干那一行。比尔盖茨是学法律的,Sun的Scott McNealy是
学经济的,HP的Carly Fiorina是学哲学和中古文学专业的。无论你想走那一行,只要毕业
后努力,都还是有机会的

2006年09月11日

发信人: wangchun (luckydog<->mutecat), 信区: DianLinux
标  题: 生活启示(zz)
发信站: 喻信星空 (2006年04月19日12:43:56 星期三), 站内信件

   01.每天告诉自己一次,“我真的很不错”。

   02.生气是拿别人做错的事来惩罚自己。

   03.生活中若没有朋友,就像生活中没有阳光一样。

   04.明天的希望,让我们忘了今天的痛苦。

   05.生活若剥去理想、梦想、幻想,那生命便只是一堆空架子。

   06.发光并非太阳的专利,你也可以发光。

   07.愚者用肉体监视心灵,智者用心灵监视肉体。

   08.获致幸福的不二法门是珍视你所拥有的、遗忘你所没有的。

   09.贪婪是最真实的贫穷,满足是最真实的财富。

   10.你可以用爱得到全世界,你也可以用恨失去全世界

   11.人的价值,在遭受诱惑的一瞬间被决定。

   12.年轻是我们唯一拥有权利去编织梦想的时光。

  13.青春一经典当即永不再赎。

  14.没有了爱的语言,所有的文字都是乏味的。

  15.真正的爱,应该超越生命的长度、心灵的宽度、灵魂的深度。

  16.爱的力量大到可以使人忘记一切,却又小到连一粒嫉妒的沙石也不能容纳。

  17.当一个人真正觉悟的一刻,他放弃追寻外在世界的财富,而开始追寻他内心世界的  真正财富。

  18.只要有信心,人永远不会挫败。

  19.不论你在什么时候开始,重要的是开始之后就不要停止。

  20.不论你在什么时候结束,重要的是结束之后就不要悔恨。

  21.人若软弱就是自己最大的敌人。

  22.人若勇敢就是自己最好的朋友。

  23.如果你不喜欢现在的工作,要么辞职不干,要么就闭嘴不言。

  24.抱最大的希望,为最大的努力,做最坏的打算。

  25.家!甜蜜的家!天下最美好的莫过于家。

  26.游手好闲会使人心智生锈。

  27.每一件事都要用多方面的角度来看它。

  28.有理想在的地方,地狱就是天堂。

  29.有希望在的地方,痛苦也成欢乐。

  30.所有的胜利,与征服自己的胜利比起来,都是微不足道。

  31.所有的失败,与失去自己的失败比起来,更是微不足道。

  32.上帝从不埋怨人们的愚昧,人们却埋怨上帝的不公平。

  33.美好的生命应该充满期待、惊喜和感激。

  34.世上最累人的事,莫过于虚伪的过日子。

  35.觉得自己做的到和不做的到,其实只在一念之间。

  36.第一个青春是上帝给的;第二个的青春是靠自己努力的。

  37.少一点预设的期待,那份对人的关怀会更自在。

  38.思想如钻子,必须集中在一点钻下去才有力量。

  39.人只要不失去方向,就不会失去自己。!

  40.如果你曾歌颂黎明,那么也请你拥抱黑夜。

  41.问候不一定要慎重其事,但一定要真诚感人。

  42.人生重要的不是所站的位置,而是所朝的方向。

  43.当你能飞的时候就不要放弃飞。

  44.当你能梦的时候就不要放弃梦。

  45.当你能爱的时候就不要放弃爱。

  46.生命太过短暂,今天放弃了明天不一定能得到。

  47.天才是百分之一的灵感加上百分之九十九的努力。

  48.人总是珍惜未得到的,而遗忘了所拥有的。

  49.快乐要懂得分享,才能加倍的快乐。

  50.自己要先看得起自己,别人才会看得起你。

  51.一个今天胜过两个明天。

  52.要铭记在心;每天都是一年中最美好的日子。

  53.乐观者在灾祸中看到机会;悲观者在机会中看到灾祸。

  54.有勇气并不表示恐惧不存在,而是敢面对恐惧、克服恐惧。

  55.肯承认错误则错已改了一半。

  56.明天是世上增值最快的一块土地,因它充满了希望。

  57.理想的路总是为有信心的人预备着。

  58.所有欺骗中,自欺是最为严重的。

  59.人生最大的错误是不断担心会犯错。

  60.把你的脸迎向阳光,那就不会有阴影。

  61.经验是由痛苦中粹取出来的。

  62.机会从不会“失掉”,你失掉了,自有别人会得到。

  63.用最少的浪费面对现在。

  64.用最多的梦面对未来。

  65.快乐不是因为拥有的多而是计较的少。

  66.你的选择是做或不做,但不做就永远不会有机会。

  67.如你想要拥有完美无暇的友谊,可能一辈子找不到朋友。

  68.不如意的时候不要尽往悲伤里钻,想想有笑声的日子吧。

  69.把自己当傻瓜,不懂就问,你会学的更多。

  70.要纠正别人之前,先反省自己有没有犯错。

  71.因害怕失败而不敢放手一搏,永远不会成功。

  72.要克服生活的焦虑和沮丧,得先学会做自己的主人。

  73.你不能左右天气,但你能转变你的心情。

  74.孤单寂寞与被遗弃感是最可怕的贫穷。
  

  75.想象力比知识更重要。

  76.漫无目的的生活就像出海航行而没有指南针。

  77.好好扮演自己的角色,做自己该做的事。

  78.一切伟大的行动和思想,都有一个微不足道的开始。

  79.得意时应善待他人,因为你失意时会需要他们。

   80.学做任何事得按部就班,急不得。

     mutecat 加油!!
     喜欢是浅浅的爱,爱是深深的喜欢
 健康乐观、积极向上的生活!
欢迎访问我的blog  http://blog.donews.com/mutecat
~

发信人: wangchun (mutecat), 信区: DianLinux
标  题: 利用expect实现自动交互
发信站: 喻信星空 (2006年07月28日17:51:13 星期五), 站内信件

 

by  wangchun@yxxk( mutecat@byhh )

题纪:

    作为喻信服务器的管理员,前几天要实现一个定时更新mysql数据库的脚本,本来

用crond这个守护进程很好实现,只要编辑/var/spool/cron/root,在里面加入一行:

00 2 *  *  *(cd /usr/local/mysql/data; ./quotaget.sh; mysql -p < update.sql)

也即是在每天凌晨2:00自动更新数据库。关于crontab的语法可以参考:

 http://www.wanglimin.com/Tutorial/HP-UNIX/docs/hp-ux07.htm

但是遇到了一个问题,即执行mysql -p < update.sql这条语句时,需要用户输入密码。

尽管shell功能很强大,但对于需要用户输入密码的程序,shell并不能非交互的运行,

于是上网搜索到了expect这个工具,用了才知道这个工具对于一个系统管理员来说是多

么的有价值。

 

    Expect的作者Don Libes在1990年开始编写Expect时对Expect做有如下定义:

   (Expect is a software suite for automating interactive tools。

    Expect的官方主页对它作了如下介绍:
    Expect is a tool for automating interactive applications such as telnet,
    ftp, passwd, fsck, rlogin, tip, etc. Expect really makes this stuff
    trivial. Expect is also useful for testing these same applications. And
    by adding Tk, you can also wrap interactive applications in X11 GUIs.

    Expect是基于TCL的,作为一个脚本语言,expect能在无需管理员参与的情况下实现

自动交互(比如passwd,fsck,telnet等),expect也能用于自动测试一些应用程序。

    expect的语法和shell的语法非常相似,它支持函数调用,有while语句,switch

语句。

1)    expect使用spawn调用其他的执行程序,比如

    spawn  telnet  218.199.20.98  2600

    但是在使用的过程中发现spawn不支持管道和重定向,也就是说对于

        ls |more ; mysql -p < update.sql 这样的命令spawn不能正确解析。

    解决的办法是把这些命令放到一个shell脚本里面,在用spawn执行这个shell

    脚本。

2)    expect 创建子函数使用proc标志,也即:

    proc  functionname { parameter1,parameter2 } {
       ……

    }

    调用子函数非常简单

    functionname  $param1 $param2

3)  expect  使用expect ,send 组合实现自动交互 ,语法如下:

    expect {
            "login;"  {  send  "$user\n"   }
            "passwd:" {  send  "$passwd\n" }

    }
    使用send的使用后面的内容不显示给用户,如要显示给用户,应使用send_user

4) 注意点:

   1. expect里面基本是都是使用{} 而不是使用(),比如函数参数输入外面应用{},

应该是while { } 而不是 while ( ).

   2. { 应和其他符合有空格, expect { 正确,expect{ 就会报错.

   3.  spawn 不支持管道和重定向.

5) 下面是实现的mysql数据库自动更新的expect脚本:

   proc do_console_login {pass} {

        set timeout 5

    set done 1

    while { $done } {
                expect {
                          "password:" {
                               send "$pass\n"
                          }
                          eof {
                                set done 0
                          }
                }
    }
   }

   if {$argc<1} {

        puts stderr "Usage: $argv0  password.\n "
        exit 1
   }

   set PASS    [lindex $argv 0]

   spawn   /usr/local/mysql/data/updatemysql

   do_console_login  $PASS

 

         mutecat 加油!!
      健康乐观、积极向上的生活!
    喜欢是浅浅的爱,爱是深深的喜欢
欢迎访问我的blog  http://blog.donews.com/mutecat
~
※ 来源:·喻信星空 bbs.eistar.net·[FROM: 222.20.73.221]