Monday, December 14, 2009

linux 线程 进程经典文章

有关linux下进程与线程看过很多文章,我觉的这篇可以说最经典
---------------------------------

一.基础知识:线程和进程

按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销。

无论按照怎样的分法,一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。一个进程当然可以拥有多个线程,此时,如果进程运行在SMP机器上,它就可以同时使用多个cpu来执行各个线程,达到最大程度的并行,以提高效率;同时,即使是在单cpu的机器上,采用多线程模型来设计程序,正如当年采用多进程模型代替单进程模型一样,使设计更简洁、功能更完备,程序的执行效率也更高,例如采用多个线程响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进程模型来实现,而与后者相比,线程的上下文切换开销就比进程要小多了,从语义上来说,同时响应多个输入这样的功能,实际上就是共享了除cpu以外的所有资源的。

针对线程模型的两大意义,分别开发出了核心级线程和用户级线程两种线程模型,分类的标准主要是线程的调度者在核内还是在核外。前者更利于并发使用多处理器的资源,而后者则更多考虑的是上下文切换开销。在目前的商用系统中,通常都将两者结合起来使用,既提供核心线程以满足smp系统的需要,也支持用线程库的方式在用户态实现另一套线程机制,此时一个核心线程同时成为多个用户态线程的调度者。正如很多技术一样,"混合"通常都能带来更高的效率,但同时也带来更大的实现难度,出于"简单"的设计思路,Linux从一开始就没有实现混合模型的计划,但它在实现上采用了另一种思路的"混合"。

在线程机制的具体实现上,可以在操作系统内核上实现线程,也可以在核外实现,后者显然要求核内至少实现了进程,而前者则一般要求在核内同时也支持进程。核心级线程模型显然要求前者的支持,而用户级线程模型则不一定基于后者实现。这种差异,正如前所述,是两种分类方式的标准不同带来的。

当核内既支持进程也支持线程时,就可以实现线程-进程的"多对多"模型,即一个进程的某个线程由核内调度,而同时它也可以作为用户级线程池的调度者,选择合适的用户级线程在其空间中运行。这就是前面提到的"混合"线程模型,既可满足多处理机系统的需要,也可以最大限度的减小调度开销。绝大多数商业操作系统(如Digital Unix、Solaris、Irix)都采用的这种能够完全实现POSIX1003.1c标准的线程模型。在核外实现的线程又可以分为"一对一"、"多对一"两种模型,前者用一个核心进程(也许是轻量进程)对应一个线程,将线程调度等同于进程调度,交给核心完成,而后者则完全在核外实现多线程,调度也在用户态完成。后者就是前面提到的单纯的用户级线程模型的实现方式,显然,这种核外的线程调度器实际上只需要完成线程运行栈的切换,调度开销非常小,但同时因为核心信号(无论是同步的还是异步的)都是以进程为单位的,因而无法定位到线程,所以这种实现方式不能用于多处理器系统,而这个需求正变得越来越大,因此,在现实中,纯用户级线程的实现,除算法研究目的以外,几乎已经消失了。

Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,一定程度上也弥补了这一缺陷。目前最流行的线程机制LinuxThreads所采用的就是线程-进程"一对一"模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。Linux-LinuxThreads的运行机制正是本文的描述重点。

二.Linux 2.4内核中的轻量进程实现

最初的进程定义都包含程序、资源及其执行三部分,其中程序通常指代码,资源在操作系统层面上通常包括内存资源、IO资源、信号处理等部分,而程序的执行通常理解为执行上下文,包括对cpu的占用,后来发展为线程。在线程概念出现以前,为了减小进程切换的开销,操作系统设计者逐渐修正进程的概念,逐渐允许将进程所占有的资源从其主体剥离出来,允许某些进程共享一部分资源,例如文件、信号,数据内存,甚至代码,这就发展出轻量进程的概念。Linux内核在2.0.x版本就已经实现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程。在内核中,clone()调用经过参数传递和解释后会调用do_fork(),这个核内函数同时也是fork()、vfork()系统调用的最终实现:


int do_fork(unsigned long clone_flags, unsigned long stack_start,
struct pt_regs *regs, unsigned long stack_size)

其中的clone_flags取自以下宏的"或"值:

#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */ #define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ #define CLONE_PID 0x00001000 /* set if pid shared */ #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
#define CLONE_THREAD 0x00010000 /* Same thread group? */
#define CLONE_NEWNS 0x00020000 /* New namespace group? */
#define CLONE_SIGNAL (CLONE_SIGHAND CLONE_THREAD)

在do_fork()中,不同的clone_flags将导致不同的行为,对于LinuxThreads,它使用(CLONE_VM CLONE_FS CLONE_FILES CLONE_SIGHAND)参数来调用clone()创建"线程",表示共享内存、共享文件系统访问计数、共享文件描述符表,以及共享信号处理方式。本节就针对这几个参数,看看Linux内核是如何实现这些资源的共享的。

1.CLONE_VM

do_fork()需要调用copy_mm()来设置task_struct中的mm和active_mm项,这两个mm_struct数据与进程所关联的内存空间相对应。如果do_fork()时指定了CLONE_VM开关,copy_mm()将把新的task_struct中的mm和active_mm设置成与current的相同,同时提高该mm_struct的使用者数目(mm_struct::mm_users)。也就是说,轻量级进程与父进程共享内存地址空间,由下图示意可以看出mm_struct在进程中的地位:

2.CLONE_FS

task_struct中利用fs(struct fs_struct *)记录了进程所在文件系统的根目录和当前目录信息,do_fork()时调用copy_fs()复制了这个结构;而对于轻量级进程则仅增加fs->count计数,与父进程共享相同的fs_struct。也就是说,轻量级进程没有独立的文件系统相关的信息,进程中任何一个线程改变当前目录、根目录等信息都将直接影响到其他线程。

3.CLONE_FILES

一个进程可能打开了一些文件,在进程结构task_struct中利用files(struct files_struct *)来保存进程打开的文件结构(struct file)信息,do_fork()中调用了copy_files()来处理这个进程属性;轻量级进程与父进程是共享该结构的,copy_files()时仅增加files->count计数。这一共享使得任何线程都能访问进程所维护的打开文件,对它们的操作会直接反映到进程中的其他线程。

4.CLONE_SIGHAND

每一个Linux进程都可以自行定义对信号的处理方式,在task_struct中的sig(struct signal_struct)中使用一个struct k_sigaction结构的数组来保存这个配置信息,do_fork()中的copy_sighand()负责复制该信息;轻量级进程不进行复制,而仅仅增加signal_struct::count计数,与父进程共享该结构。也就是说,子进程与父进程的信号处理方式完全相同,而且可以相互更改。

do_fork()中所做的工作很多,在此不详细描述。对于SMP系统,所有的进程fork出来后,都被分配到与父进程相同的cpu上,一直到该进程被调度时才会进行cpu选择。

尽管Linux支持轻量级进程,但并不能说它就支持核心级线程,因为Linux的"线程"和"进程"实际上处于一个调度层次,共享一个进程标识符空间,这种限制使得不可能在Linux上实现完全意义上的POSIX线程机制,因此众多的Linux线程库实现尝试都只能尽可能实现POSIX的绝大部分语义,并在功能上尽可能逼近。

三.LinuxThread的线程机制

LinuxThreads是目前Linux平台上使用最为广泛的线程库,由Xavier Leroy (Xavier.Leroy@inria.fr)负责开发完成,并已绑定在GLIBC中发行。它所实现的就是基于核心轻量级进程的"一对一"线程模型,一个线程实体对应一个核心轻量级进程,而线程之间的管理在核外函数库中实现。

1.线程描述数据结构及实现限制

LinuxThreads定义了一个struct _pthread_descr_struct数据结构来描述线程,并使用全局数组变量__pthread_handles来描述和引用进程所辖线程。在__pthread_handles中的前两项,LinuxThreads定义了两个全局的系统线程:__pthread_initial_thread和__pthread_manager_thread,并用__pthread_main_thread表征__pthread_manager_thread的父线程(初始为__pthread_initial_thread)。

struct _pthread_descr_struct是一个双环链表结构,__pthread_manager_thread所在的链表仅包括它一个元素,实际上,__pthread_manager_thread是一个特殊线程,LinuxThreads仅使用了其中的errno、p_pid、p_priority等三个域。而__pthread_main_thread所在的链则将进程中所有用户线程串在了一起。经过一系列pthread_create()之后形成的__pthread_handles数组将如下图所示:

图2 __pthread_handles数组结构

新创建的线程将首先在__pthread_handles数组中占据一项,然后通过数据结构中的链指针连入以__pthread_main_thread为首指针的链表中。这个链表的使用在介绍线程的创建和释放的时候将提到。

LinuxThreads遵循POSIX1003.1c标准,其中对线程库的实现进行了一些范围限制,比如进程最大线程数,线程私有数据区大小等等。在LinuxThreads的实现中,基本遵循这些限制,但也进行了一定的改动,改动的趋势是放松或者说扩大这些限制,使编程更加方便。这些限定宏主要集中在sysdeps/unix/sysv/linux/bits/local_lim.h(不同平台使用的文件位置不同)中,包括如下几个:

每进程的私有数据key数,POSIX定义_POSIX_THREAD_KEYS_MAX为128,LinuxThreads使用PTHREAD_KEYS_MAX,1024;私有数据释放时允许执行的操作数,LinuxThreads与POSIX一致,定义PTHREAD_DESTRUCTOR_ITERATIONS为4;每进程的线程数,POSIX定义为64,LinuxThreads增大到1024(PTHREAD_THREADS_MAX);线程运行栈最小空间大小,POSIX未指定,LinuxThreads使用PTHREAD_STACK_MIN,16384(字节)。

2.管理线程

"一对一"模型的好处之一是线程的调度由核心完成了,而其他诸如线程取消、线程间的同步等工作,都是在核外线程库中完成的。在LinuxThreads中,专门为每一个进程构造了一个管理线程,负责处理线程相关的管理工作。当进程第一次调用pthread_create()创建一个线程的时候就会创建(__clone())并启动管理线程。

在一个进程空间内,管理线程与其他线程之间通过一对"管理管道(manager_pipe[2])"来通讯,该管道在创建管理线程之前创建,在成功启动了管理线程之后,管理管道的读端和写端分别赋给两个全局变量__pthread_manager_reader和__pthread_manager_request,之后,每个用户线程都通过__pthread_manager_request向管理线程发请求,但管理线程本身并没有直接使用__pthread_manager_reader,管道的读端(manager_pipe[0])是作为__clone()的参数之一传给管理线程的,管理线程的工作主要就是监听管道读端,并对从中取出的请求作出反应。

创建管理线程的流程如下所示:
(全局变量pthread_manager_request初值为-1)

图3 创建管理线程的流程

初始化结束后,在__pthread_manager_thread中记录了轻量级进程号以及核外分配和管理的线程id,2*PTHREAD_THREADS_MAX+1这个数值不会与任何常规用户线程id冲突。管理线程作为pthread_create()的调用者线程的子线程运行,而pthread_create()所创建的那个用户线程则是由管理线程来调用clone()创建,因此实际上是管理线程的子线程。(此处子线程的概念应该当作子进程来理解。)

__pthread_manager()就是管理线程的主循环所在,在进行一系列初始化工作后,进入while(1)循环。在循环中,线程以2秒为timeout查询(__poll())管理管道的读端。在处理请求前,检查其父线程(也就是创建manager的主线程)是否已退出,如果已退出就退出整个进程。如果有退出的子线程需要清理,则调用pthread_reap_children()清理。

然后才是读取管道中的请求,根据请求类型执行相应操作(switch-case)。具体的请求处理,源码中比较清楚,这里就不赘述了。

3.线程栈

在LinuxThreads中,管理线程的栈和用户线程的栈是分离的,管理线程在进程堆中通过malloc()分配一个THREAD_MANAGER_STACK_SIZE字节的区域作为自己的运行栈。

用户线程的栈分配办法随着体系结构的不同而不同,主要根据两个宏定义来区分,一个是NEED_SEPARATE_REGISTER_STACK,这个属性仅在IA64平台上使用;另一个是FLOATING_STACK宏,在i386等少数平台上使用,此时用户线程栈由系统决定具体位置并提供保护。与此同时,用户还可以通过线程属性结构来指定使用用户自定义的栈。因篇幅所限,这里只能分析i386平台所使用的两种栈组织方式:FLOATING_STACK方式和用户自定义方式。

在FLOATING_STACK方式下,LinuxThreads利用mmap()从内核空间中分配8MB空间(i386系统缺省的最大栈空间大小,如果有运行限制(rlimit),则按照运行限制设置),使用mprotect()设置其中第一页为非访问区。该8M空间的功能分配如下图:

图4 栈结构示意

低地址被保护的页面用来监测栈溢出。
对于用户指定的栈,在按照指针对界后,设置线程栈顶,并计算出栈底,不做保护,正确性由用户自己保证。
不论哪种组织方式,线程描述结构总是位于栈顶紧邻堆栈的位置。

4.线程id和进程id

每个LinuxThreads线程都同时具有线程id和进程id,其中进程id就是内核所维护的进程号,而线程id则由LinuxThreads分配和维护。

__pthread_initial_thread的线程id为PTHREAD_THREADS_MAX,__pthread_manager_thread的是2*PTHREAD_THREADS_MAX+1,第一个用户线程的线程id为PTHREAD_THREADS_MAX+2,此后第n个用户线程的线程id遵循以下公式:

tid=n*PTHREAD_THREADS_MAX+n+1

这种分配方式保证了进程中所有的线程(包括已经退出)都不会有相同的线程id,而线程id的类型pthread_t定义为无符号长整型(unsigned long int),也保证了有理由的运行时间内线程id不会重复。

从线程id查找线程数据结构是在pthread_handle()函数中完成的,实际上只是将线程号按PTHREAD_THREADS_MAX取模,得到的就是该线程在__pthread_handles中的索引。

5.线程的创建

在pthread_create()向管理线程发送REQ_CREATE请求之后,管理线程即调用pthread_handle_create()创建新线程。分配栈、设置thread属性后,以pthread_start_thread()为函数入口调用__clone()创建并启动新线程。pthread_start_thread()读取自身的进程id号存入线程描述结构中,并根据其中记录的调度方法配置调度。一切准备就绪后,再调用真正的线程执行函数,并在此函数返回后调用pthread_exit()清理现场。

6.LinuxThreads的不足

由于Linux内核的限制以及实现难度等等原因,LinuxThreads并不是完全POSIX兼容的,在它的发行README中有说明。 1)进程id问题 这个不足是最关键的不足,引起的原因牵涉到LinuxThreads的"一对一"模型。 Linux内核并不支持真正意义上的线程,LinuxThreads是用与普通进程具有同样内核调度视图的轻量级进程来实现线程支持的。这些轻量级进程拥有独立的进程id,在进程调度、信号处理、IO等方面享有与普通进程一样的能力。在源码阅读者看来,就是Linux内核的clone()没有实现对CLONE_PID参数的支持。

在内核do_fork()中对CLONE_PID的处理是这样的:
if (clone_flags & CLONE_PID) {
if (current->pid)
goto fork_out; }

这段代码表明,目前的Linux内核仅在pid为0的时候认可CLONE_PID参数,实际上,仅在SMP初始化,手工创建进程的时候才会使用CLONE_PID参数。

按照POSIX定义,同一进程的所有线程应该共享一个进程id和父进程id,这在目前的"一对一"模型下是无法实现的。

2)信号处理问题

由于异步信号是内核以进程为单位分发的,而LinuxThreads的每个线程对内核来说都是一个进程,且没有实现"线程组",因此,某些语义不符合POSIX标准,比如没有实现向进程中所有线程发送信号,README对此作了说明。

如果核心不提供实时信号,LinuxThreads将使用SIGUSR1和SIGUSR2作为内部使用的restart和cancel信号,这样应用程序就不能使用这两个原本为用户保留的信号了。在Linux kernel 2.1.60以后的版本都支持扩展的实时信号(从_SIGRTMIN到_SIGRTMAX),因此不存在这个问题。

某些信号的缺省动作难以在现行体系上实现,比如SIGSTOP和SIGCONT,LinuxThreads只能将一个线程挂起,而无法挂起整个进程。

3)线程总数问题

LinuxThreads将每个进程的线程最大数目定义为1024,但实际上这个数值还受到整个系统的总进程数限制,这又是由于线程其实是核心进程。

在kernel 2.4.x中,采用一套全新的总进程数计算方法,使得总进程数基本上仅受限于物理内存的大小,计算公式在kernel/fork.c的fork_init()函数中:

max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8

在i386上,THREAD_SIZE=2*PAGE_SIZE,PAGE_SIZE=2^12(4KB),mempages=物理内存大小/PAGE_SIZE,对于256M的内存的机器,mempages=256*2^20/2^12=256*2^8,此时最大线程数为4096。

但为了保证每个用户(除了root)的进程总数不至于占用一半以上物理内存,fork_init()中继续指定:
init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;

这些进程数目的检查都在do_fork()中进行,因此,对于LinuxThreads来说,线程总数同时受这三个因素的限制。

4)管理线程问题

管理线程容易成为瓶颈,这是这种结构的通病;同时,管理线程又负责用户线程的清理工作,因此,尽管管理线程已经屏蔽了大部分的信号,但一旦管理线程死亡,用户线程就不得不手工清理了,而且用户线程并不知道管理线程的状态,之后的线程创建等请求将无人处理。

5)同步问题

LinuxThreads中的线程同步很大程度上是建立在信号基础上的,这种通过内核复杂的信号处理机制的同步方式,效率一直是个问题。

6)其他POSIX兼容性问题

Linux中很多系统调用,按照语义都是与进程相关的,比如nice、setuid、setrlimit等,在目前的LinuxThreads中,这些调用都仅仅影响调用者线程。

7)实时性问题

线程的引入有一定的实时性考虑,但LinuxThreads暂时不支持,比如调度选项,目前还没有实现。不仅LinuxThreads如此,标准的Linux在实时性上考虑都很少。

Saturday, December 5, 2009

malloc函数,动态存储分配

动态存储分配
在数组一章中,曾介绍过数组的长度是预先定义好的,在整个程序中固定不变。C语言中不允许动态数组类型。

例如:
int n;
scanf("%d",&n);
int a[n];

用变量表示长度,想对数组的大小作动态说明,这是错误的。但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

常用的内存管理函数有以下三个:
1. 分配内存空间函数malloc
调用形式: (类型说明符*)malloc(size)
功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。
“类型说明符”表示把该区域用于何种数据类型。
(类型说明符*)表示把返回值强制转换为该类型指针。
“size”是一个无符号数。
例如:
pc=(char *)
malloc(100);
表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。

2. 分配内存空间函数 calloc
calloc 也用于分配内存空间。
调用形式:
(类型说明符*)calloc(n,size)
功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。
(类型说明符*)用于强制类型转换。
calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。
例如:
ps=(struet stu*)
calloc(2,sizeof(struct stu));
其中的sizeof(struct stu)是求stu的结构长度。因此该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。

2. 释放内存空间函数free
调用形式: free(void*ptr);
功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。
【例】分配一块区域,输入一个学生数据。
main()
{
struct stu {
int num;
char *name;
char sex;
float score;
} *ps;

ps=(struct stu*)malloc(sizeof(struct stu));
ps->num=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
printf("Number=%d\nName=%s\n",ps->num,ps->name);
printf("Sex=%c\nScore=%f\n",ps->sex,ps->score); f
ree(ps);
}

本例中,定义了结构stu,定义了stu类型指针变量ps。然后分配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps为指向结构的指针变量对各成员赋值,并用printf输出各成员值。最后用free函数释放ps指向的内存空间。整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤,实现存储空间的动态分配。

main函数参数argc和argv的简单介绍

在C\C++语言中,main()函数有参数形式 int main(int argc,char *argv[]) 此形式与 int main(int argc,char **argv)完全等同 那么其参数argc和argv有什么作用呢?



argc是一个整型变量,指的是命令行输入参数的个数,argv是字符串数组,它包含argc个字符串,每个字符串存储着一个命令行参数,如argv[0]存储着第一个命令行参数字符串,argv[1]存储着第二个命令行参数字符串,argv[argc-1]存储着最后一个命令行参数字符串.一般来说,argv[0]存储的是当前程序的路径与全称. 程序演示如下:


#include

int main (int argc, char **argv)

{

int i; printf ("%d\n", argc);



for (i = 0; i <>
{

printf ("%s\n", argv[i]);

}

return 0;

}


上述程序编译连接之后,生成的可执行文件test.exe位于D:盘目录下。在windows命令提示符下(cmd)转到D盘,输入test a b c d,如下图所示




在上述程序中
argc=5argv[0]="test" (第一个命令行参数字符串,代表程序的路径和全称)
argv[1]="a" (第二个命令行参数字符串)
argv[2]="b" (第三个命令行参数字符串)
argv[3]="c" (第四个命令行参数字符串)
argv[4]="d" (第五个命令行参数字符串)


Wednesday, December 2, 2009

Ubuntu root密码设置 or 切换用户

Ubuntu 8.10
在安装时,没有设置root密码,所以这就必须在安装完成后设置 命令如下:
$ sudo passwd root
输入你希望的root用户的密码------------------------------------------------------------------------------------------------------------------------------------------------------
ubuntu用户管理

root 用户为根用户,也就是 系统管理员 拥有全部权限 一个用户只能拥有一个 GID ,但是还可以归属于其它附加群组

用户管理的重要配置文件
/etc/passwd 用户名 密码位 UID 归属GID 姓名 $HOME目录 登录Shell/etc/shadow 用户名 已加密密码 密码改动信息 密码策略/etc/group 群组名 密码位 GID 组内用户/etc/gshadow 群组密码相关文件,不重要/etc/sudoers 用户名 权限定义 权限[/pre]可以使用 pwconv 命令创建影子密码,将 /etc/passwd 文件中的密码转换到 /etc/shadow 文件

su [用户名]
切换到其它用户,默认切换到root用户。提示密码为将切换用户密码 -f 快速切换,忽略配置文件-l 重新登录-m ,-p 不更改环境变量-c <命令> 切换后执行命令,并退出切换

sudo [命令]
以其它用户的身份执行命令,默认以root的身份执行。提示密码为当前用户密码 -s 切换为root shell-i 切换为root shell,并初始化-u <用户名UID> 执行命令的身份-l 显示自己的权限

passwd [用户名]
设定用户密码

-d 清除密码-l 锁定用户-e 使密码过期,在下次登录时更改密码-S 显示密码认证信息-x <天数> 密码过期,最大使用时间-n <天数> 冻结密码,最小使用时间-s 更改登录Shell-f 更改用户信息

示例:
$passwdChanging password for user(current) UNIX password: 原密码Enter new UNIX password: 新密码Retype new UNIX password: 确认新密码[/pre

chsh [-s ] [用户名]
更改登录Shell

挂载 USB
若/proc/bus/usb下没有相应USB设备信息,应输入以下命令将 USB 设备文件系统手动挂装到 /proc/bus/usb:
# mount -t usbfs none /proc/bus/usb
为了在系统引导时自动挂装 USB 设备文件系统,请将下面一行添加到 /etc/fstab 中的 /proc 挂装行之后: none /proc/bus/usb usbdevfs defaults 0 0

插入usb后:
sudo mount /dev/sdb1 /mnt/usb/ -t vfat -o iocharset=gb2312 #注意gb2312

配置上网***********************

Ubuntu的网络参数保存在文件 /etc/network/interfaces中,默认设置使用dhcp,内容如下:
# The primary network interface
auto eth0
iface eth0 inet dhcp

按你的修改:
1)编辑 /etc/network/interfaces =》vim /etc/network/interfaces
auto eth0
iface eth0 inet static
address 192.168.1.183
netmask 255.255.255.0
gateway 192.168.1.1

2)编辑 /etc/resolv.conf,设置dns
nameserver 110.120.119.114
nameserver 110.120.119.114

------------------------------------------------------------------------------------------------------------------------------------------------------
其它命令:
一、安装Grub
$ grub
$ find /boot/grub/stage1
(hd0,*)
$ root (hd0,*)
$ setup (hd0,*)
$ quit
$ sudo dd if=/dev/sda(*+1) of=\ubuntu.lnx bs=512 count=1
将ubuntu.lnx文件复制到Windows的根目录下,比如C:\,修改boot.ini,添加C:\ubuntu.lnx="Ubuntu 7.10"。

二、语言支持与软件更新
系统–>管理工具–>软件源–>下载自:–>其他…–>台湾–>tw.archive.ubuntu.com–>选择服务器–>关闭–>重新载入
系统–>管理工具–>语言支持(Language Support)–>支持的语言–>Chinese–>应用–>确定 $ sudo apt-get update
$ sudo apt-get upgrade

三、安装nVidia显卡驱动
下载NVIDIA-Linux-x86-100.14.19-pkg1.run。登录控制台(按下ctrl+alt+F1~F6任一)。
$ sudo /etc/init.d/gdm stop
$ sudo apt-get install build-essential #或只安装libc6-dev
$ sudo sh NVIDIA-Linux-x86-100.14.19-pkg1.run
$ sudo /etc/init.d/gdm start

四、安装字体
如果字体文件都保存在/home/(user_name)/fonts里
$ cd /usr/share/fonts/
$ ln -s /home/(user_name)/fonts myfonts/
#或者 $ mkdir myfonts
# $ cp /home/(user_name)/fonts/* myfonts/
$ cd myfonts/
$ sudo mkfontscale
$ sudo mkfontdir
$ fc-cache
系统–>首选项–>外观–>字体,在这里修改。

五、输入法SCIM
$ sudo im-switch -s scim -z default
$ sudo apt-get install scim-qtimm
$ sudo apt-get install scim scim-pinyin scim-tables-zh im-switch scim-qtimm scim-bridge scim-bridge-client-gtk scim-bridge-client-qt scim-bridge-agent
$ gksu gedit /etc/X11/xinit/xinput.d/scim
将默认的 GTK_IM_MODULE=scim 修改为 GTK_IM_MODULE="scim-bridge"。
另外,在scim输入法中进行了如下设定:scim设置->全局设置->将预编辑字符串嵌入到客户端中 前的勾去掉;scim设置->gtk->嵌入式候选词标的勾去掉。

六、NTFS分区读写支持
UBUNTU7.10默认就支持NTFS分区的读写,可是不完美,对NTFS分区下的中文文件支持不好,你会发现找不到中文的文件。
$ sudo apt-get -y –force-yes install ntfs-3g
$ sudo apt-get -y –force-yes install ntfs-config
$ [ -x /usr/bin/ntfs-3g ] && sudo ln -sf /usr/bin/ntfs-3g /sbin/mount.ntfs
$ gksudo ntfs-config &
$ set +v

七、媒体播放器的安装
$ sudo apt-get install mplayer mozilla-mplayer totem-xine libxine1-ffmpeg
下载w32codecs_20061022-0.1_i386.deb包并安装
mplayer调试(视频、字幕):启动mplayer,右键->Preferences->Video;Available drivers选择xv;然后进入Font标签 Font里选择一个中文字体,Encoding里设置为Simplified Chinese charset (CP936)。 $

八、安装解码器、flashplayer、java虚拟机、微软字体
多媒体解决方案是:一般普通的播放任务使用 Totem 电影播放机,如果遇到不能播放的情况或者看大碟时就选用 Mplayer ,播放音乐使用 audacious。
装多媒体软件和相应解码器:安装audacious Ubuntu中类似千千静听的的播放器,支持播放 ogg*, flac*, mp3, wma, wav, 3gp 这些格式。安装 mplayer 和 totem (播放 xvid/divx 编码的 avi 格式视频, rm/rmvb/asf/wmv 等流媒体视频,外加 vcd/dvd和其他 mpeg2/mpeg4 视频。)
为了方便所以来个一键全媒体方案:
$ sudo apt-get install mplayer mozilla-mplayer totem-xine libxine1-ffmpeg audacious ffmpeg gstreamer0.10-plugins-ugly gstreamer0.10-pitfdll gstreamer0.10-ffmpeg gstreamer0.10-* lame faad sox mjpegtools gstreamer0.10-* totem-xine ffmpeg lame faad sox mjpegtools sidplay-base xsidplay libggi-target-x libggi-target-emu libggi-target-monotext toolame mpeg2dec avifile-divx-plugin avifile-xvid-plugin mencoder drip libavifile-0.7c2 mpg123-el flac123 mpc mpd gmpc sonata gxine totem-xine kaffeine flac beep-media-player-* xine-ui bmpx gstreamer0.8* vlc
win32codes下载:http: //archive.ubuntu.org.cn/ubuntu-cn/dists/edgy/main/binary-i386/media/w32codecs_20060611 -1plf6.10_i386.deb;ftp://211.86.156.210/debian- multimedia/pool/main/w/w32codecs/w32codecs_20061022-0.0_i386.deb

九、安装RAR压缩/解压缩程序
$ sudo apt-get install rar
建立软链接:
$ sudo ln -fs /usr/bin/rar /usr/bin/unrar
这样,以后只要在命令行输入unrar,就可以解压或者压缩文件了,安装完成后,归档管理器也同时集成了rar组件。

十、Firefox插件
1. DownThemAll
2. Super DragAndGo
3. Fasterfox
4. MediaWarp https://addons.mozilla.org/en-US/firefox/addon/1879
5. FlashGot:https://addons.mozilla.org/en-US/firefox/addon/220
6. 更快速的打开网页,在firefox浏览器地址拦里输入about:config 找下面的选项进行修改吧: network.dns.disableIPv6 -> true
network.http.pipelining -> true
network.http.pipelining.maxrequests -> 8(8-24这是我自己的设置) network.http.proxy.pipelining -> true

十一、安装3D桌面:
Compiz Fusion,Emerald,Avant Window Navigato,Screenlets
1. 安装显卡驱动
如果是新装的Ubuntu,还没有安装显卡驱动,可以先到系统–>系统管理–>受限驱动管理器中启用受限制的驱动。
解决启动compiz后最大化、最小化、关闭按钮的标题栏消失的问题:
$ sudo nvidia-xconfig –add-argb-glx-visuals #解决没有窗口边框的问题
$ sudo gedit /etc/X11/xorg.conf
在"Module"段加入子段: SubSection "extmod"
Option "omit xfree86-dga"
EndSubSection
在"Device"段中加入:
Option "AddARGBVisuals" "True"
Option "AddARGBGLXVisuals" "True"
Option "DisableGLXRootClipping" "True"
Option "AllowGLXWithComposite" "True"
Option "RenderAccel" "True"
在配置文件的最后加入:
Section "Extensions"
Option "Composite" "Enable"
EndSection
2. 添加密钥和软件源(步骤2使用7.04的朋友需要做的!!!而使用7.10的朋友不需要做!!因此,跳过)

3. 下载安装Compiz和Compiz Fusion
$ sudo apt-get install compiz compiz-gnome
$ sudo apt-get install compizconfig-settings-manager
$ sudo apt-get install compiz-fusion-*

4. 启动Compiz Fusion了。同时按下Alt+F2,输入compiz –replace即可启动。
5. 定位到系统–>首选项–>CompizConfig Settings Manager,打开后可以对效果进行自定义:
立方体效果: 同时按下ctrl+Alt+鼠标左键(Button 1)。这个和Beryl下是一样的。
火焰字: 在左侧类别栏目的Effect下,勾选"在屏幕上绘制火焰".同时按下Shift+Win(Super)+B1,退出火焰字同时按下Shift+Win+C。
层叠效果: 在左侧类别栏目的Windows Management下,勾选Shift-Switcher。Win+Tab(和Vista下一样)。
桌面缩放: Win+鼠标滚轮。
屏幕飞雪: 在左侧类别栏目的Extra下勾选飞雪.同时按下Win+F3。

6. Compiz Fusion系统托盘管理图标的安装
$ sudo apt-get install git git-core compiz-dev
$ git-clone git://anongit.opencompositing.org/users/crdlb/fusion-icon
$ cd fusion-icon/
$ make
$ sudo make install
安装完毕后在主菜单的系统工具下可以找到compiz fusion icon启动即可。如果你希望在进入系统时自动启动可以系统–>首选项–>会话,新建一个新会话,填入fusion-icon就可以了。

7.
其他
1、解决PDF电子文档的中文乱码
$ sudo apt-get install xpdf-chinese-simplified xpdf-chinese-traditional

2、安装JAVA环境和JDK
$ sudo apt-get install sun-java6-jre
$ sudo apt-get install sun-java6-jdk
设置当前默认的java解释器
$ sudo update-alternatives –config java
输入有包含"sun"的行的前面的数字
安装浏览器的JAVA Plugin(可选)
$ sudo apt-get install sun-java6-plugin

3、安装编译工具
建议安装上 gcc,g++,make 等。
$ sudo apt-get install build-essential

4、因为经常会用到终端:所以把终端加到右键菜单:
$ sudo apt-get install nautilus-open-terminal
这就终端就在右键菜单了可以了!!

5. 有必要学会以root权限打开文件夹
$ sudo apt-get install nautilus-gksu
这样右键单击文件或文件夹,选择以管理员打开!!!

6. 查看隐藏文件是:Ctrl+ H

7、启用root(最高权限)帐户 $ sudo passwd root 输入你希望的root用户的密码

http://hi.baidu.com/xingxing_whu/blog/item/b3b9b2df7d848e1b49540334.html

Ubuntu的root密码是什么(How to set the password for root in UTUNBU)

Ubuntu安装后root不能够登录的并且也是没有默认的密码的,因为你还没给root设置密码,你第一个 user 是在 admin 组 ,所以他可以给 root 设置密码 。

给root用户设置密码。
具体步骤是打开终端(应用程序--附件--终端),然后输入下面的命令
sudo passwd root

回车后会出现让你输入密码和确认密码,例如这样
[sudo] password for you :---> 输入你的密码(你现在这个用户的密码),不回显
Enter new UNIX password: --- > 设置root 密码
Retype new UNIX password: --> 重复这样这样你的root的密码设置好了

第二步就是要启用root用户,因为root默认是不启用的。所以要在System->Administration->Users and Groups. Choose root and unlock it.

然后注销后就可以登录了!

http://wuxiong8665.blog.163.com/blog/static/93512200961184643307/

MPICH2简单的安装配置总结 (含Linux下的安装配置和单机运行) zz

MPICH2是MPI(Message-Passing Interface)的一个应用实现,支持最新的MPI-2接口标准,是用于并行运算的工具,在程序设计语言上支持C/C++和Fortran。最近因为有项目需要的计算量比较大,所以就学习使用了MPICH2,在此根据网络上查询的相关信息和我自己的实际使用经历,分别总结一下MPICH2在windows和linux下基本的安装使用方法。


软件下载


MPICH2的主页是http://www-unix.mcs.anl.gov/mpi/mpich2/index.htm,在这个页面上就能找到各平台最新版本MPICH2的下载地址,其中还包括源代码,我在开始作这个项目的时候最新版本是windows版mpich2-1.0.5p2,源代码mpich2-1.0.5p4。我们的项目是一个CentOS版linux下的程序,所以最终是要在linux下运行的,但是又舍不得windows,于是就打算可能的话就在windows下写程序,用MinGW加windows版的MPICH2编译调试,通过后再到wmware虚拟机组成的简单集群作测试。所以,为避免不必要的麻烦,就要统一一下windows和linux下的MPICH2版本,而且不打算用最新的,因此决定用mpich2-1.0.5版。但是,如果在主页上找的话是没有以前旧版本下载的链接的(至少我没找到),只有最新版本的http和ftp下载。这难不住我等有心之人,既然提供了ftp下载,那咱就直接到他ftp服务器上找,最新源代码链接的地址是ftp://ftp.mcs.anl.gov/pub/mpi/mpich2-1.0.5p4.tar.gz,把后面文件名去掉就应该是文件的ftp存放路径,把这个路径直接写到浏览器地址栏里回车(偶用的是FireFox2),就能看到他们服务器上这个目录的文件列表,里面就有1.0.5版的windows安装文件和源代码包,分别为ftp://ftp.mcs.anl.gov/pub/mpi/mpich2-1.0.5-win32-ia32.msi 和 ftp://ftp.mcs.anl.gov/pub/mpi/mpich2-1.0.5.tar.gz 。msi文件不用多说,这是windows下安装用的,源代码包我们拿来在linux下用。


文档下载


还是主页上就有MPICH2的安装和使用指南文档,主要有三个,分别是User's Guide,Installer's Guide和Windows Developer's Guide,都down下来看看很有用的说。具体开发用的有关MPI标准的文档在MPI论坛的网站里都有,地址是http://www.mpi-forum.org/,我觉得最有用的是MPI-2: Extensions to the Message-Passing Interface。


Windows下的安装配置


我用的参与计算的系统都是WindowsXP Pro SP2,安装的过程没什么太特别的,一般就是默认就可以,只是其中有个地方要填一个什么passphrase,上面提示说所有系统都要用相同的passphrase,照做就是了,在所有参与计算的结点机器上都填一样的passphrase就好了。另外就是需要.net framework 2的运行环境。

默认安装的位置是C:\Program Files\MPICH2,下面的bin目录下是系统配置运行需要的程序,为了方便在控制台使用,可以把C:\Program Files\MPICH2\bin加到系统的PATH变量中去。Include是头文件,开发的时候用,lib是链接程序的时候用的库文件。Jumpshot下有个pdf的文档,干什么用的可以看看这个文档,我没仔细看,感觉我暂时还用不上。Examples下面是一个样本程序,就是一个用MPI计算圆周率的程序,分别有C,C++和Fortran版,C/C++的应该可以用VS2003以上版本打开。同时,安装程序还会自动向系统注册一个服务MPICH2 Process Manager,我们从控制面板-管理工具-服务里就能找到,这是管理运行MPI程序的一个服务,安装好后就是自动启动的,所以一般也就不用动它。

安装完毕后开始菜单-程序中就添加了一个MPICH2目录,其中就有上面提到的Jumpshot,另外wmpiconfig.exe是用来配置运行环境的,我在网上有找到的说明都是以前旧版本的,和现在的差别比较大,感觉这新版本用的不爽,没搞明白这个程序该咋用,不过好像默认状态下不改什么就能正常使用,所以也就不管它了。wmpiregister.exe则是用来注册用户的,使用MPI之前需要在这个程序里注册一个系统里已经存在的用户,而且这个用户必需拥有管理员权限,拥有运行我们安装了的MPI系统的能力。比如我就在所有参与运算的机器上添加了一个管理员mpi,密码也是mpi。

接下来,我们就可以开始试着运行一下MPI的程序了。就用examples目录下面的那个计算圆周率的程序。如果要多机并行计算的话,就需要在所有机器上的相同位置放置要运行的程序,我的情况就是在所有机器的C盘下建了一个mpiexe的目录,并把cpi.exe拷到所有机器的这个目录下。然后,在其中的某台机器上进入控制台(运行MPI程序其实也可以用开始菜单的MPICH2下的wmpiexec.exe,这是个gui程序,但是我觉得用的不爽,不如直接在控制台下敲命令来得灵活),敲下命令mpiexec -hosts 2 192.168.10.142 192.168.0.23 c:\mpiexe\cpi.exe。mpiexec是安装目录下bin目录里的一个程序,在本文的例子中就是C:\Program Files\MPICH2\bin\mpiexec.exe,因为刚才说了,我把这个地址加入到PATH里了,所以可以在任何地方直接执行,它是用来启动MPI程序的,-hosts参数说明是启动多台机器并行运算,后面跟着的2就是说要在两台机器上执行程序,再后面的就是那两台机器的ip地址,其中第一个就是我启动程序的机器,当然,这个地方也可以写机器名,只要它的机器名能被正常的解析就可以,最后面的就是要运行的程序,也就是刚才提到的所有机器都要在相同位置放置的那个MPI程序。如果只是在本机运行,则命令为mpiexec –n 2 cpi.exe,-n表示是在本地运行,后面的2表示启动的进程数。程序运行后就会提示让你输入一个数字intervals,这个数字影响计算的精度,值越大精度越高,当然计算时间就越长了,然后程序会打印出计算的结果和花费的时间。

比如,我使用单机单进程运行,intervals设为99999999,耗时1.253849秒,而用两台机器双进程则只有0.628954秒,明显快很多,并行运算还是很有效果的。不过,如果我们把intervals改为9999,单机运行只用了0.000279秒,而两台机器却花了0.001548秒,这是因为并行运算过程中,参与运算的机器需要通过网络传递一些消息,如果计算量不大的话,花在了这上面的时间影响会比较明显,因而反不如单机版的来得快。

到现在我们的MPI运行环境就基本安装好了,当然,MPI还有很多其他的命令参数,只不过最常用估计也就这两条了,其他的用得着的时候就去查上面提到的文档,里面有比较详细的介绍。另外,如果按照以上的介绍进行安装配置,在运行多机并行MPI程序的时候却出现连接错误的话,八成是因为网络的问题,看看你的防火墙是不是开着,打开相应的端口,或者干脆关掉防火墙就好了。



Linux下的安装配置和单机运行

Linux下的操作要相对来说麻烦一点,这个麻烦从安装开始,呵呵。我用的系统是CentOS4.4,装在VMware Workstation里的,一共装了两个虚拟机,环境基本上完全一样。为运行MPI在两台虚拟机都创建了一个用户mpi,密码也是mpi,home路径也都是/home/mpi,然后继续都创建了一个目录/home/mpi/mpich2用来作MPI运行环境的安装路径,一个/home/mpi/mpich2/src来存放编译用的源代码。然后将源代码包mpich2-1.0.5.tar.gz下载到两台机器上,都解压缩到/home/mpi/mpich2/src中,然后到/home/mpi/mpich2/src下,指定安装路径,

./configure -prefix=/home/mpi/mpich2

make

make install

几分钟后安装完毕。需要提一下的是,我曾经试着用root用户来安装MPICH2,但是安装后好重启系统就出了问题,所以建议还是另外建个用户来装吧(ubuntu干脆就把root给禁了,不让你直接用root)。

安装后/home/mpi/mpich2下多出来一些目录和文件,要比windows多,lib是库文件,include是头文件,bin还是程序文件,所以还是要写到环境变量里,可以用命令export PATH /home/mpi/mpich2/bin:$PATH,但我是用root用户直接在/etc/profile最后面加了这么一句export PATH=/home/mpi/mpich2/bin:$PATH,一劳永逸。

MPI应用一个管理器来管理运行MPI程序,这个管理器就是mpd,但是在正式开始运行mpd前还需要一个基于安全考虑的配置文件,.mpd.conf,这个文件是要放在运行程序的用户的home目录下,本例子中就是/home/mpi/.mpd.conf,而且这个文件只能由这个用户读写,创建文件的命令是,

cd $HOME

touch .mpd.conf

chmod 600 .mpd.conf

然后在文件中写入这么一行,secretword=***,***在参与计算的计算机上必需完全一致。如果是root用户的话,这个文件应该是/etc/mpf.conf。

然后,我们就可以启动mpd管理器了,直接在控制台下使用mpd命令,或者是mpd &,让mpd在后台运行,若关闭启动的mpd,只需要命令mpdallexit即可。在启动mpd之后就可以运行MPI应用程序了,执行命令与windows下类似,如我们仍然是测试一下examples里的cpi程序可以这样来作,

cd ~/mpich2/examples

mpiexec -n 1 ./ cpi

参数含义同windows下的单机运行命令。另外,启动mpd后还可以用命令mpdtrace来察看当前运行的mpd情况。


SSH配置和多机并行

MPI的多机并行是用mpdboot来管理启动的,是由参与计算的其中一台机器通过mpdboot同时启动其他机器上的mpd管理器并运行相应MPI程序的,所以,需要赋予运行mpdboot的机器执行其他机器上程序的能力。MPICH2支持通过ssh和rsh来做到这一点,其中ssh是默认的,而且其安全性也优于rsh,因此,我在项目中是用的ssh。

首先,我们需要修改所有机器上的/etc/hosts文件,在里面添加上参与计算的机器名和ip地址,比如本文中有两台机器参加的例子里,hosts文件应当为:

127.0.0.1 localhost.localdomain localhost

192.168.10.142 node0

192.168.10.23 node1

这里的意思是说,主机名为node0的机器ip地址为192.168.10.142,主机名为node1的机器ip地址为192.168.10.23。

当然,其实这一步也可以跳过,因为我们也可以在操作过程中直接使用ip地址,只不过那样不太方便。另外就是,有些机器默认情况下第一行可能包括本机的主机名,比如在ip为192.168.10.142的node0上,hosts文件第一行是

127.0.0.1 localhost.localdomain localhost node0

这样可能会使得mpdboot工作不正常,所以还是最好给成上面的那种形式。

第二步是创建ssh密钥,命令行下:

#ssh-keygen -t rsa

-t rsa指的是密钥类型,具体请察看ssh相关资料,这里不多说。这样就在当前用户的home目录下生成了一个.ssh目录,本文中的就是/home/mpi/.ssh。

第三步,将/home/mpi/.ssh下的id_rsa.pub文件拷贝改名为authorized_keys,即

#cp id_rsa.pub authorized_keys

第四步,在其他所有机器上进行以下操作。

#ssh-keygen -t rsa 生成.ssh文件夹

#scp node0的IP:/home/mpi/.ssh/* ~/.ssh 拷贝node0上的.ssh文件夹覆盖本地的

第五步,在所有机器上建立与自己和所有其他机器的信任连接。

对每个节点执行:

#ssh node0

#ssh node1

根据提示键入yes即可。然后就可以在不需要用户名密码的情况下通过ssh登陆其他机器了,比如在node0上#ssh node1,

就可以直接进入node1。

接下来,在启动mpdboot的机器上创建一个参与计算的host列表文件,如文件mpd.hosts,每行是一个主机名,创建过程如

#cd ~

#touch mpd.hosts

#vi mpd.hosts

nod0

node1


现在,就可以启动运算集群了

#mpdboot -n 2 -f mpd.hosts

-n表示要启动的机器个数,一般是不大于mpd.hosts文件中的机器数,比如本文中的例子就是两台机器。这样,列表中的机器就会启动其本机上的mpd管理器。

然后,就可以开始运行MPI程序,进行运算了,同windows下一样,程序需要放在每台机器上的相同位置(如果用NFS就只需在一台机器上放置程序,其他机器作映射就行),比如都是程序/home/mpi/mpich2/examples/cpi,在运行mpdboot 的结点机器上:

#mpiexec -n 2 /home/mpi/mpich2/examples/cpi

-n表示要启动的进程个数,一般是不大于mpd.hosts文件中的机器数(或者cpu核心数?偶用的机器就是双核的了,所以单机的时候双进程比单进程效率好很多,但是三进程就不行)。

Mpd在运行过程中,可以通过mpdtrace显示参与计算的机器名,mpdtrace –l则是显示机器名以及其端口。


http://hi.baidu.com/andy1lee/blog/item/79d492a716661693d0435829.html

linux平台下mpich2的安装以及如何运行并行程序 zz

From : http://hi.baidu.com/%B2%DD%B8%F9%CE%DE%B4%FD/blog/item/dcb32844961d4348500ffe0f.html

网上有很多资料,一搜一大片,其实很多雷同,而且是根据官方文档翻译过来的。在下载mpich2时就能下载到相应的帮助文档,安装过程在mpich2-doc-install.pdf(或mpich2-doc-README.txt)中说的很是详尽了!我仅把自己的安装过程和遇到的问题写下来。

我的计算环境是一个32CPU的集群,想在自己的帐户下安装mpich2-1.0.6,我的帐户目录为/home/mat04/ 。首先将mpich2-1.0.6拷贝到一个目录下,假设为/home/mat04/software/,接着如下:

cd /home/mat04/software/
tar zvxf mpich2-1.0.6.tar.gz
cd mpich2-1.0.6
makdir ~/mpich2-1.0.6/
makdir ~/tmp
./configure --prefix=/home/mat04/mpich2-1.0.6/ 2>&1 tee configure.log
make 2>&1 tee configure.log
make install 2>&1 tee configure.log

几点说明:
1、 由于 ./configure使用了选项--prefix ,所以在前面要先创建目录makdir ~/mpich2-1.0.6/。2、 安装的过程需要一个临时文件夹,所以makdir ~/tmp,要是没有这个tmp目录的话, ./configure总是出错。
3、 ./configure,make,make insatll中使用的 2>&1 tee ***.log 是要产生相应的log文件。
4、 与windows版本不同的是:在include文件夹下有mpi.mod,而windows下的include文件夹下则没有mpi.mod文件。windows下需要自己写一个mpi.mod,在前面的文章里有阐述。

然后,就是要将/home/mat04/mpich2-1.0.6/bin写入环境变量PATH里:
vi ~/.bashrc
插入三行:
# mpich2 variables :PATH=/home/mat04/mpich2-1.0.6/bin:$PATH
export PATH
保存退出,再source .bashrc

测试一下:
which mpirun
which mpif77
which mpd
结果应该是:
~/mpich2-1.0.6/bin/mpirun
~/mpich2-1.0.6/bin/mpif77
~/mpich2-1.0.6/bin/mpd
那就安装完成了,简单~~~,可以写一个简单的helloworld程序测试一下,当然也可以用它的cpi。

遇到的问题:
安装完成后发现/home/mat04/mpich2-1.0.6/bin目录下没有mpif90,而mpif77,mpicc,mpicxx都有。我就是要写90程序啊,就需要mpif90。不知道出了什么问题,卸了重装,换mpich2-1.0.3,问题依旧。倒是mpich-1.2.7没问题,装完就什么都有了,但是没有mpdboot,这也是我需要的啊。

昨天我针对这个问题给mpich写了封信,不知道能不能回复呢,呵呵。等不了那个,就先问了问小董,他对mpi挺熟悉的。他说是fortran编译器的问题。因为在我的帐户下装了pgf和intel两个fortran编译器,安装mpich2时,./congfigure步骤中关于fortran90的选项时认的是pgf90,而没有认intel的ifort。若把环境变量中的pgf去掉,只剩下intel的,再./configure时自然认的是ifort,安装完后在bin/目录下也有了mpif90。但是为什么pgf90不行,ifort行呢?不知道,他说在机房的10号机上只有pgf时是可以的,如果pgf和intel同时存在,不知道会怎么样。经过这个问题,我就把pgf彻底注释掉了,以后只用一个intel编译器。

最后,在集群环境下如何运行mpi程序。

1、先写一个mpd.hosts文件,里面写上要起动的节点,在1号机上有8个能用的节点oscarnode01-oscarnode08,就在里面写上这8行:
oscarnode01
oscarnode02
oscarnode03
oscarnode04
oscarnode05
oscarnode06
oscarnode07
oscarnode08
当然也可以把主节点simserver01也写进去。

2、启动节点: mpdboot -n 2 -f mpd.hosts
2代表2台机器,在这里就是两个节点。此时启动的是mpd.hosts中最前面的两行,即1,2节点,因为1,2节点都已经坏掉了,所以应该把oscarnode03和oscarnode04写在最前面。若mpdboot -n 9 -f mpd.hosts则会出错了,因为没有那么多个节点可用啊~~~

3、提交任务:mpiexec -n 4 ./helloworld
两个节点有4个CPU,当然要使用4个进程了,就是提了8个进程,也是在这4个CPU上运行。

4、退出:mpdallexit

几点说明:
1、虽然mpd.hosts启动的是3,4节点,但要是站在主节点simserver01上提任务,主节点上便会有任务进程运行,尽管主节点simserver01并不在mpd.hosts中。所以要先ssh oscarnode04进入一个节点中,再提交任务。
2、在集群环境中提交castep,pwscf,vasp等任务多用作业管理程序,如PBS。自己写的mpi程序也应该用pbs提交,试了几次不太理想,没有完全成功,pbs脚本还是不太会写。等成功了再写一篇文章吧。

写完这篇文章后,看了看邮箱,mpich回信了,原因跟小董说的一样,就是configure没找到f90编译器,老外的工作效率就是高啊,前天下午发的邮件,昨天早晨就回了,我今天下午才看见,是我耽误了自己啊,呵呵,其实是我写信的时候觉得他不一定能给我回复啊。以下是他的回复:The configure.log file you sent was empty. But it is possible that configure could not find the Fortran 90 compiler. You can specify the Fortran 90 compiler by setting the environment variable F90 to the name of the compiler and then rerunning configure, make, make install. Also make sure the f90 compiler is in your default path, or provide the full path in the F90 environment variable.

Linux 中设置MPICH2的环境变量

有两种方法:

1、修改profile文件: #vi /etc/profile 在里面加入:
export PATH=/home/tliu/mpich2-install/bin:$PATH

2. 修改.bashrc文件: # vi /root/.bashrc 在里面加入:
export PATH=/home/tliu/mpich2-install/bin:$PATH

这两种方法一般需要重新注销系统才能生效,最后可以通过echo命令测试一下:
# echo $PATH 看看输出里面是不是已经有了/my_new_path这个路径了。

Tuesday, December 1, 2009

VirtualBox下Windows Host Ubuntu Guest共享文件夹的设置-2

作者RunningOn。
所谓Windows Host Ubuntu Guest,就是指VirtualBox安装在Windows下,VirtualBox里虚拟的操作系统是Ubuntu。这样的配置在大学里很常见,为了做实验又不想装独立Linux,一般就这样配置。本文里用的Windows是指Windows XP SP2,VirtualBox和Ubuntu都是最新的,即VirtualBox1.5.6和Ubuntu 8.04(Hardy Heron)。

本来Linux Guest安装共享文件夹比较麻烦,但在WindpwsXP和最新的VirtualBox和Ubuntu下,事情变得非常简单。如果你的系统不符合这些条件,那么安装方法请见VirtualBox的end user manual。废话不多说,让我们开始吧。共分三步:

1. 首先要安装虚拟电脑工具包
我假设你的VirtualBox1.5.6和Ubuntu8.04已经安装完毕,启动VirtualBox,运行Ubuntu并登录,然后在VirtualBox的菜单里选择"设备"->"安装虚拟电脑工具包",你会发现在Ubuntu桌面上多出一个光盘图标,这张光盘默认被自动加载到了文件夹/media/cdrom0,而且/cdrom自动指向这个文件夹。默认设置下文件管理器会自动打开这张光盘,可以看到里面有个"VBoxLinuxAdditions.run"文件。

打开一个命令行终端,依次输入"cd /cdrom"和"sudo sh ./VBoxLinuxAdditions.run",不含双引号,开始安装工具包。安装完毕,会用英文提示要重启Ubuntu,建议立刻重启。

重启后,比较明显的变化是鼠标是共享模式,并且剪贴板也和Windows共享了。如果有这些变化,说明虚拟电脑工具包已经装成功。

2. 下一步设置共享文件夹。
这里只介绍图形界面的设置方法,命令行方式的不做介绍,也不推荐,因为没必要。如果Ubuntu正在运行,请右键点击对应的VirtualBox窗口右下角的共享文件夹图标(在鼠标图标的左边),打开共享文件夹的设置窗口。如果Ubuntu没有运行,在VirtualBox界面左边的列表中选择ubuntu虚拟机,并在右边的列表中单击“共享文件夹”,也可以打开同样的共享文件夹设置窗口。在共享文件夹设置窗口中,单击右侧的"添加一个共享文件夹",路径选择你想要共享的Windows文件夹,共享名任取一个自己喜欢的,比如"myshare",选项read-only是指是否只允许ubuntu读这个文件夹,请根据需要选择这个选项。搞定后点确定并关闭窗口。

3. 再要在ubuntu下挂载这个共享文件夹。
在Ubuntu下的命令行窗口里输入"sudo mount -t vboxsf myshare /media/share",不含双引号。其中"myshare"是之前取的共享文件夹的名字,"/media/share"是要挂载到的目标文件夹,是我事先已经建好了的,最好是个空文件夹。

到此一般就搞定了,Windows下的共享文件夹,对应Ubuntu下的我建立的"/media/share",你可以试试在Ubuntu下能不能操作那个共享文件夹。

默认设置下是支持中文的,如果有乱码,请查找与磁盘挂载相关的文章。重启Ubuntu后需要重新挂载共享文件夹,即重新运行"sudo mount -t vboxsf myshare /media/share",想自动挂载,请查找修改/etc/fstab文件的文章,本文亦不做描述,另一解决办法是永远不重启Ubuntu,而是使用VirtualBox提供的快速休眠功能,即关闭VirtualBox时,选择"快速休眠"。大部分情况下,休眠就可以了,快且方便。