文档库 最新最全的文档下载
当前位置:文档库 › Linux系统下的多线程遵循POSIX线程接口

Linux系统下的多线程遵循POSIX线程接口

Linux系统下的多线程遵循POSIX线程接口
Linux系统下的多线程遵循POSIX线程接口

Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux 下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的多线程程序example1.c。

/* example.c*/

#include

#include

void thread(void)

{

int i;

for(i=0;i<3;i++)

printf("This is a pthread.\n");

}

int main(void)

{

pthread_t id;

int i,ret;

ret=pthread_create(&id,NULL,(void *) thread,NULL);

if(ret!=0){

printf ("Create pthread error!\n");

exit (1);

}

for(i=0;i<3;i++)

printf("This is the main process.\n");

pthread_join(id,NULL);

return (0);

}

我们编译此程序:

gcc example1.c -lpthread -o example1

运行example1,我们得到如下结果:

This is the main process.

This is a pthread.

This is the main process.

This is the main process.

This is a pthread.

This is a pthread.

再次运行,我们可能得到如下结果:

This is a pthread.

This is the main process.

This is a pthread.

This is the main process.

This is a pthread.

This is the main process.

前后两次结果不一样,这是两个线程争夺CPU资源的结果。上面的示例中,我们使用到了两个函数,pthread_create和pthread_join,并声明了一个pthread_t型的变量。

pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:

typedef unsigned long int pthread_t;

它是一个线程的标识符。函数pthread_create用来创建一个线程,它的原型为:

extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,

void *(*__start_routine) (void *), void *__arg));

第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINV AL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。函数pthread_join用来等待一个线程的结束。函数原型为:

extern int pthread_join __P ((pthread_t __th, void **__thread_return)); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:

extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__)); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。下面,我们来了解线程的一些常用属性以及如何设置这些属性。

修改线程的属性

在上一节的例子里,我们用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。

属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义,喜欢追根问底的人可以自己去查看。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、

对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM (绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。

#include

pthread_attr_t attr;

pthread_t tid;

/*初始化属性值,均设为默认值*/

pthread_attr_init(&attr);

pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_create(&tid, &attr, (void *) my_function, NULL);

线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE (非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。

#include

#include

pthread_attr_t attr;

pthread_t tid;

sched_param param;

int newprio=20;

pthread_attr_init(&attr);

pthread_attr_getschedparam(&attr, ¶m);

param.sched_priority=newprio;

pthread_attr_setschedparam(&attr, ¶m);

pthread_create(&tid, &attr, (void *)myfunction, myarg);

线程的数据处理

和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile 来定义,这是为了防止编译器在优化时(如gcc中使用-OX参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。

4.1 线程数据在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。

和线程数据相关的函数主要有4个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。

创建键的函数原型为:

extern int pthread_key_create __P ((pthread_key_t *__key,

void (*__destr_function) (void *))); 第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。

在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数createWindow,这个函数定义一个图形窗口(数据类型为Fl_Window *,这是图形界面开发工具FLTK中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。/* 声明一个键*/

pthread_key_t myWinKey;

/* 函数createWindow */

void createWindow ( void ) {

Fl_Window * win;

static pthread_once_t once= PTHREAD_ONCE_INIT;

/* 调用函数createMyKey,创建键*/

pthread_once ( & once, createMyKey) ;

/*win指向一个新建立的窗口*/

win=new Fl_Window( 0, 0, 100, 100, "MyWindow");

/* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/

setWindow(win);

/* 将窗口指针值绑定在键myWinKey上*/

pthread_setpecific ( myWinKey, win);

}

/* 函数createMyKey,创建一个键,并指定了destructor */

void createMyKey ( void ) {

pthread_keycreate(&myWinKey, freeWinKey);

}

/* 函数freeWinKey,释放空间*/

void freeWinKey ( Fl_Window * win){

delete win;

}

这样,在不同的线程中调用函数createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数pthread_getspecific 得到。在上面的例子中,我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下:

extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer)); extern void *pthread_getspecific __P ((pthread_key_t __key)); 这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。

4.2 互斥锁

互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。

我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。void reader_function ( void );

void writer_function ( void );

char buffer;

int buffer_has_item=0;

pthread_mutex_t mutex;

struct timespec delay;

void main ( void ){

pthread_t reader;

/* 定义延迟时间*/

https://www.wendangku.net/doc/4714410201.html,_sec = 2;

https://www.wendangku.net/doc/4714410201.html,_nec = 0;

/* 用默认属性初始化一个互斥锁对象*/

pthread_mutex_init (&mutex,NULL);

pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL); writer_function( );

}

void writer_function (void){

while(1){

/* 锁定互斥锁*/

pthread_mutex_lock (&mutex);

if (buffer_has_item==0){

buffer=make_new_item( );

buffer_has_item=1;

}

/* 打开互斥锁*/

pthread_mutex_unlock(&mutex);

pthread_delay_np(&delay);

}

}

void reader_function(void){

while(1){

pthread_mutex_lock(&mutex);

if(buffer_has_item==1){

consume_item(buffer);

buffer_has_item=0;

}

pthread_mutex_unlock(&mutex);

pthread_delay_np(&delay);

}

}

这里声明了互斥锁变量mutex,结构pthread_mutex_t为不公开的数据类型,其中包含一个系统分配的属性对象。函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值,PTHREAD_PROCESS_PRIV ATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIV ATE。后者用来设置互斥锁

类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。

pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。在上面的例子中,我们使用了pthread_delay_np函数,让线程睡眠一段时间,就是为了防止一个线程始终占据此函数。

上面的例子非常简单,就不再介绍了,需要提出的是在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,a线程先锁定互斥锁1,b线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。

4.3 条件变量

前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。

条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:

extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr)); 其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_ destroy(pthread_cond_t cond)。

函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:

extern int pthread_cond_wait __P ((pthread_cond_t *__cond,

pthread_mutex_t *__mutex)); 线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。

另一个用来阻塞线程的函数是pthread_cond_timedwait(),它的原型为:

extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,

pthread_mutex_t *__mutex, __const struct timespec *__abstime)); 它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。

函数pthread_cond_signal()的原型为:

extern int pthread_cond_signal __P ((pthread_cond_t *__cond)); 它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。下面是使用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简单的例子。

pthread_mutex_t count_lock;

pthread_cond_t count_nonzero;

unsigned count;

decrement_count() {

pthread_mutex_lock (&count_lock);

while(count==0)

pthread_cond_wait( &count_nonzero, &count_lock);

count=count -1;

pthread_mutex_unlock (&count_lock);

}

increment_count(){

pthread_mutex_lock(&count_lock);

if(count==0)

pthread_cond_signal(&count_nonzero);

count=count+1;

pthread_mutex_unlock(&count_lock);

}

count值为0时,decrement函数在pthread_cond_wait处被阻塞,并打开互斥锁count_lock。此时,当调用到函数increment_count时,pthread_cond_signal()函数改变条件变量,告知decrement_count()停止阻塞。读者可以试着让两个线程分别运行这两个函数,看看会出现什么样的结果。

函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。

4.4 信号量

信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数,它们都在头文件/usr/include/semaphore.h中定义。

信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()

用来初始化一个信号量。它的原型为:

extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value)); sem 为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。

函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。

函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。

函数sem_destroy(sem_t *sem)用来释放信号量sem。

实验七:Linux多线程编程(实验分析报告)

实验七:Linux多线程编程(实验报告)

————————————————————————————————作者:————————————————————————————————日期:

实验七:Linux多线程编程(4课时) 实验目的:掌握线程的概念;熟悉Linux下线程程序编译的过程;掌握多线程程序编写方法。 实验原理:为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。 1 多线程概念 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间。 使用多线程的理由之二是线程间方便的通信机制。同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。2多线程编程函数 Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义: typedef unsigned long int pthread_t; 它是一个线程的标识符。 函数pthread_create用来创建一个线程,它的原型为: extern int pthread_create((pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。 函数pthread_join用来等待一个线程的结束。函数原型为: extern int pthread_join(pthread_t th, void **thread_return); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。 函数pthread_exit的函数原型为: extern void pthread_exit(void *retval); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。 3 修改线程的属性 线程属性结构为pthread_attr_t,它在头文件/usr/include/pthread.h中定义。属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。 设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。 另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。 4 线程的数据处理

LINUX 内核的几种锁介绍

spinlock(自旋锁)、 mutex(互斥量)、 semaphore(信号量)、 critical section(临界区) 的作用与区别 Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。 Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。 Binary semaphore与Mutex的差异: 在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore 可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。 另一个概念是spin lock,这是一个内核态概念。spin lock与semaphore的主要区别是spin lock是busy waiting,而semaphore是sleep。对于可以sleep 的进程来说,busy waiting当然没有意义。对于单CPU的系统,busy waiting 当然更没意义(没有CPU可以释放锁)。因此,只有多CPU的内核态非进程空间,

并行计算1

并行计算 实 验 报 告 学院名称计算机科学与技术学院专业计算机科学与技术 学生姓名 学号 年班级 2016年5 月20 日

一、实验内容 本次试验的主要内容为采用多线程的方法计算pi的值,熟悉linux下pthread 形式的多线程编程,对实验结果进行统计并分析以及加速比曲线分析,从而对并行计算有初步了解。 二、实验原理 本次实验利用中值积分定理计算pi的值 图1 中值定理计算pi 其中公式可以变换如下: 图2 积分计算pi公式的变形 当N足够大时,可以足够逼近pi,多线程的计算方法主要通过将for循环的计算过程分到几个线程中去,每次计算都要更新sum的值,为避免一个线程更新sum 值后,另一个线程仍读到旧的值,所以每个线程计算自己的部分,最后相加。三、程序流程图 程序主体部分流程图如下:

多线程执行函数流程图如下: 四、实验结果及分析

令线程数分别为1、2、5、10、20、30、40、50和100,并且对于每次实验重复十次求平均值。结果如下: 图5 时间随线程的变化 实验加速比曲线的计算公式类似于 结果如下: 图5 加速比曲线 实验结果与预期类似,当线程总数较少时,线程数的增多会对程序计算速度带来明显的提升,当线程总数增大到足够大时,由于物理节点的核心数是有限的,因此会给cpu带来较多的调度,线程的切换和最后结果的汇总带来的时间开销较大,所以线程数较大时,增加线程数不会带来明显的速度提升,甚至可能下降。 五、实验总结

本次试验的主要内容是多线程计算pi的实现,通过这次实验,我对并行计算有了进一步的理解。上学期的操作系统课程中,已经做过相似的题目,因此程序主体部分相似。不同的地方在于,首先本程序按照老师要求应在命令行提供参数,而非将数值写定在程序里,其次是程序不是在自己的电脑上运行,而是通过ssh和批处理脚本等登录到远程服务器提交任务执行。 在运行方面,因为对批处理任务不够熟悉,出现了提交任务无结果的情况,原因在于windows系统要采用换行的方式来表明结束。在实验过程中也遇到了其他问题,大多还是来自于经验的缺乏。 在分析实验结果方面,因为自己是第一次分析多线程程序的加速比,因此比较生疏,参考网上资料和ppt后分析得出结果。 从自己遇到的问题来看,自己对批处理的理解和认识还比较有限,经过本次实验,我对并行计算的理解有了进一步的提高,也意识到了自己存在的一些问题。 六、程序代码及部署 程序源代码见cpp文件 部署说明: 使用gcc编译即可,编译时加上-pthread参数,运行时任务提交到服务器上。 编译命令如下: gcc -pthread PI_3013216011.cpp -o pi pbs脚本(runPI.pbs)如下: #!/bin/bash #PBS -N pi #PBS -l nodes=1:ppn=8 #PBS -q AM016_queue #PBS -j oe cd $PBS_O_WORKDIR for ((i=1;i<=10;i++)) do ./pi num_threads N >> runPI.log

Linux多线程编程的基本的函数

Posix线程编程指南(一) 线程创建与取消 这是一个关于Posix线程编程的专栏。作者在阐明概念的基础上,将向您详细讲述Posix线程库API。本文是第一篇将向您讲述线程的创建与取消。 线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。 1.2 创建线程 POSIX通过pthread_create()函数创建线程,API定义如下: 与fork()调用创建一个进程的方法不同,pthread_create()创建的线程并不具备与主线程(即调用pthread_create()的线程)同样的执行序列,而是使其运行 start_routine(arg)函数。thread返回创建的线程ID,而attr是创建线程时设置的线程属性(见下)。pthread_create()的返回值表示线程创建是否成功。尽管arg是void *类型的变量,但它同样可以作为任意类型的参数传给start_routine()函数;同时,start_routine()可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由pthread_join()获取。 1.3 线程创建属性 pthread_create()中的attr参数是一个结构指针,结构中的元素分别对应着新线程的运行属性,主要包括以下几项: __detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为 PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。

Linux中直接IO机制的介绍

Linux 中直接 I/O 机制的介绍https://www.wendangku.net/doc/4714410201.html,/developerworks/cn/linux/l-cn-...

https://www.wendangku.net/doc/4714410201.html,/developerworks/cn/linux/l-cn-...

当应用程序需要直接访问文件而不经过操作系统页高速缓冲存储器的时候,它打开文件的时候需要指定 O_DIRECT 标识符。 操作系统内核中处理 open() 系统调用的内核函数是 sys_open(),sys_open() 会调用 do_sys_open() 去处理主要的打开操作。它主要做了三件事情:首先,它调用 getname() 从进程地址空间中读取文件的路径名;接着,do_sys_open() 调用 get_unused_fd() 从进程的文件表中找到一个空闲的文件表指针,相应的新文件描述符就存放在本地变量 fd 中;之后,函数 do_?lp_open() 会根据传入的参数去执行相应的打开操作。清单 1 列出了操作系统内核中处理 open() 系统调用的一个主要函数关系图。 清单 1. 主要调用函数关系图 sys_open() |-----do_sys_open() |---------getname() |---------get_unused_fd() |---------do_filp_open() |--------nameidata_to_filp() |----------__dentry_open() 函数 do_?ip_open() 在执行的过程中会调用函数 nameidata_to_?lp(),而 nameidata_to_?lp() 最终会调用 __dentry_open()函数,若进程指定了 O_DIRECT 标识符,则该函数会检查直接 I./O 操作是否可以作用于该文件。清单 2 列出了 __dentry_open()函数中与直接 I/O 操作相关的代码。 清单 2. 函数 dentry_open() 中与直接 I/O 相关的代码 if (f->f_flags & O_DIRECT) { if (!f->f_mapping->a_ops || ((!f->f_mapping->a_ops->direct_IO) && (!f->f_mapping->a_ops->get_xip_page))) { fput(f); f = ERR_PTR(-EINVAL); } } 当文件打开时指定了 O_DIRECT 标识符,那么操作系统就会知道接下来对文件的读或者写操作都是要使用直接 I/O 方式的。 下边我们来看一下当进程通过 read() 系统调用读取一个已经设置了 O_DIRECT 标识符的文件的时候,系统都做了哪些处理。函数read() 的原型如下所示: ssize_t read(int feledes, void *buff, size_t nbytes) ; 操作系统中处理 read() 函数的入口函数是 sys_read(),其主要的调用函数关系图如下清单 3 所示: 清单 3. 主调用函数关系图 sys_read() |-----vfs_read() |----generic_file_read() |----generic_file_aio_read() |--------- generic_file_direct_IO()

并行计算第一次实验报告

并行计算上机实验报告题目:多线程计算Pi值 学生姓名 学院名称计算机学院 专业计算机科学与技术时间

一. 实验目的 1、掌握集群任务提交方式; 2、掌握多线程编程。 二.实验内容 1、通过下图中的近似公式,使用多线程编程实现pi的计算; 2、通过控制变量N的数值以及线程的数量,观察程序的执行效率。 三.实现方法 1. 下载配置SSH客户端 2. 用多线程编写pi代码 3. 通过文件传输界面,将文件上传到集群上 4.将命令行目录切换至data,对.c文件进行编译 5.编写PBS脚本,提交作业 6.实验代码如下: #include

#include #include #include #include #include static double PI=0; static int N=0; static int numOfThread=0; static int length=0; static int timeUsed=0; static int numOfThreadArray[]={1,2,4,6,8,10,12,14,16,20,24,30}; static int threadArraySize=12; static int nTime=4; static int repeatTime=30; static double totalTime=0; struct timeval tvpre, tvafter; pthread_mutex_t mut; clockid_t startTime,endTime;

实验四 Linux进程互斥

实验四 Linux进程互斥 一、实验目的 熟悉Linux下信号量机制,能够使用信号量实现在并发进程间的互斥和同步。 二、实验题目 使用共享存储区机制,使多个并发进程分别模拟生产者-消费者模式同步关系、临界资源的互斥访问关系,使用信号量机制实现相应的同步和互斥。 三、背景材料 (一)需要用到的系统调用 实验可能需要用到的主要系统调用和库函数在下面列出,详细的使用方法说明通过“man 2 系统调用名”或者“man 3 函数名”命令获取。 fork() 创建一个子进程,通过返回值区分是在父进程还是子进程中执行; wait() 等待子进程执行完成; shmget() 建立一个共享存储区; shmctl() 操纵一个共享存储区; s hmat() 把一个共享存储区附接到进程内存空间; shmdt() 把一个已经附接的共享存储区从进程内存空间断开; semget() 建立一个信号量集; semctl() 操纵一个信号量集,包括赋初值; semop() 对信号量集进行wait和signal操作; signal() 设置对信号的处理方式或处理过程。 (二)模拟生产者-消费者的示例程序 本示例主要体现进程间的直接制约关系,由于使用共享存储区,也存在间接制约关系。进程分为服务进程和客户进程,服务进程只有一个,作为消费者,在每次客户进程改变共享存储区内容时显示其数值。各客户进程作为生产者,如果共享存储区内容已经显示(被消费),可以接收用户从键盘输入的整数,放在共享存储区。 编译后执行,第一个进程实例将作为服务进程,提示: ACT CONSUMER!!! To end, try Ctrl+C or use kill. 服务进程一直循环执行,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。 其他进程实例作为客户进程,提示: Act as producer. To end, input 0 when prompted. 客户进程一直循环执行,直到用户输入0。 示例程序代码如下: #include #include #include #include #include #include #include #include

22进程、线程与并行计算(windows 编程技术)

第22章 进程、线程与并行计算 进程是正在运行的程序,线程是轻量级的进程。多任务的并发执行会用到多线程(multithreading ),而CPU 的多核(mult-core )化又将原来只在巨型机和计算机集群中才使用的并行计算带入普通PC 应用的多核程序设计中。 本章先介绍进程与线程的概念和编程,再给出并行计算的基本概念和内容。下一章讨论基于多核CPU 的并行计算的若干具体编程接口和方法。 22.1 进程与线程 进程(process )是执行中的程序,线程(thread )是一种轻量级的进程。 22.1.1 进程与多任务 现代的操作系统都是多任务(multitask )的,即可同时运行多个程序。进程(process )是位于内存中正被CPU 运行的可执行程序实例,参见图22-1。 图22-1 程序与进程 目前的主流计算机采用的都是冯·诺依曼(John von Neumann )体系结构——存储程序计算模型。程序(program )是在内存中顺序存储并以线性模式在CPU 中串行执行的指令序列。对于传统的单核CPU 计算机,多任务操作系统的实现是通过CPU 分时(time-sharing )和程序并发(concurrency )完成的。即在一个时间段内,操作系统将CPU 分配给不同的程序,虽然每一时刻只有一个程序在CPU 中运行,但是由于CPU 的速度非常快,在很短的时间段中可在多个进程间进行多次切换,所以用户的感觉就像多个程序在同时执行,我们称之为多任务的并发。 22.1.2 进程与线程 程序一般包括代码段、数据段和堆栈,对具有GUI (Graphical User Interfaces ,图形用户界面)的程序还包含资源段。进程(process )是应用程序的执行实例,即正在被执行的程进程(内存中) 可执行文件(盘上) 运行

linux通讯

线程+定时实现linux下的Qt串口编程 2010-06-26 10:49 转: 线程+定时实现linux下的Qt串口编程 作者:lizzy115 时间:2010,5,14 说明:本设计采用的是线程+定时实现linux下的Qt串口编程,而非网上资料非常多的Qt编写串口通信程序全程图文讲解系列,因为Qt编写串口通信程序全程图文讲解系列是很好实现,那只是在windows下面的,可是在linux 下面实现串口的通信并非如此,原因在于QextSerialBase::EventDriven跟QextSerialBase::Polling这两个事件的区别,EventDriven属于异步,Polling 属于同步,在windows下面使用的是EventDriven很容易实现,只要有数据就会触发一个串口事件,网上说linux下面需要的是Polling,可是还是不行的,只要串口有数据的时候他会在QByteArray temp = myCom->readAll(); 这句一直读取数据,没能退出,直到断掉串口的时候才能把接受到的串口数据通过 ui->textBrowser->insertPlainText(temp);打印在界面上,一直没能解决这个问题,所以只好采用线程+定时实现linux下的Qt串口编程进行设计。 一、安装环境: 系统平台:Ubuntu-8.04,内核2.6.24-27-generic,图形界面 二、软件需求及下地地址: Qt版本 qt-linux-SDK-4.6.2 注意:此处使用的是qt-linux-SDK-4.6.2版本,编译通过了,之后需要把他移植到qt-embedded-linux-opensource-src-4.5.3.tar.gz,通过qte编译后移植到开发板中,采用的测试开发板为Micro2440, 下载地址:略 三、程序编写过程 程序编程流程: 先新建一个工程空白工程,再建立Ui文件,通过designer进行Ui 界面设计,设计完保存,编译生成ui_mainwindow.h头文件,编写线程头文件及线程处理.cpp文件,建立串口处理头文件及 .cpp文件,最后完成main.cpp 文件。

linux下的多线程编程常用函数

Linux下pthread的实现是通过系统调用clone()来实现的。clone()是Linux所特 有的系统调用,他的使用方式类似fork. int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg); 返回值:若是成功建立线程返回0,否则返回错误的编号 形式参数: pthread_t *restrict tidp 要创建的线程的线程id指针 const pthread_attr_t *restrict attr 创建线程时的线程属性 void* (start_rtn)(void) 返回值是void类型的指针函数 void *restrict arg start_rtn的行参 进行编译的时候要加上-lpthread 向线程传递参数。 例程2: 功能:向新的线程传递整形值 #include #include #include void *create(void *arg) { int *num; num=(int *)arg; printf("create parameter is %d \n",*num); return (void *)0; } int main(int argc ,char *argv[]) { pthread_t tidp; int error; int test=4; int *attr=&test; error=pthread_create(&tidp,NULL,create,(void *)attr); if(error) { printf("pthread_create is created is not created ... \n"); return -1; } sleep(1); printf("pthread_create is created ...\n");

操作系统课内实验大纲(2014)

操作系统原理课内实验大纲(2014版) 实验一:用户接口实验 实验目的 1)理解面向操作命令的接口Shell。 2)学会简单的shell编码。 3)理解操作系统调用的运行机制。 4)掌握创建系统调用的方法。 操作系统给用户提供了命令接口和程序接口(系统调用)两种操作方式。用户接口实验也因此而分为两大部分。首先要熟悉Linux的基本操作命令,并在此基础上学会简单的shell 编程方法。然后通过想Linux内核添加一个自己设计的系统调用,来理解系统调用的实现方法和运行机制。在本次实验中,最具有吸引力的地方是:通过内核编译,将一组源代码变成操作系统的内核,并由此重新引导系统,这对我们初步了解操作系统的生成过程极为有利。 实验内容 1)控制台命令接口实验 该实验是通过“几种操作系统的控制台命令”、“终端处理程序”、“命令解释程序”和“Linux操作系统的bash”来让实验者理解面向操作命令的接口shell和进行简单的shell 编程。 查看bash版本。 编写bash脚本,统计/my目录下c语言文件的个数 2)系统调用实验 该实验是通过实验者对“Linux操作系统的系统调用机制”的进一步了解来理解操作系统调用的运行机制;同时通过“自己创建一个系统调用mycall()”和“编程调用自己创建的系统调用”进一步掌握创建和调用系统调用的方法。 编程调用一个系统调用fork(),观察结果。

编程调用创建的系统调用foo(),观察结果。 自己创建一个系统调用mycall(),实现功能:显示字符串到屏幕上。 编程调用自己创建的系统调用。 实验要求 1)按照实验内容,认真完成各项实验,并完成实验报告。 2)实验报告必须包括:程序清单(含注释)、实验结果、实验中出现的问题、观察到 的现象的解释和说明,以及实验体会。

浅谈多核CPU、多线程与并行计算

0.前言 最近发觉自己博客转帖的太多,于是决定自己写一个原创的。笔者用过MPI 和C#线程池,参加过比赛,有所感受,将近一年来,对多线程编程兴趣一直不减,一直有所关注,决定写篇文章,算是对知识的总结吧。有说的不对的地方,欢迎各位大哥们指正:) 1.CPU发展趋势 核心数目依旧会越来越多,依据摩尔定律,由于单个核心性能提升有着严重的瓶颈问题,普通的桌面PC有望在2017年末2018年初达到24核心(或者16核32线程),我们如何来面对这突如其来的核心数目的增加?编程也要与时俱进。笔者斗胆预测,CPU各个核心之间的片内总线将会采用4路组相连:),因为全相连太过复杂,单总线又不够给力。而且应该是非对称多核处理器,可能其中会混杂几个DSP处理器或流处理器。 2.多线程与并行计算的区别 (1)多线程的作用不只是用作并行计算,他还有很多很有益的作用。 还在单核时代,多线程就有很广泛的应用,这时候多线程大多用于降低阻塞(意思是类似于 while(1) { if(flag==1) break;

sleep(1); } 这样的代码)带来的CPU资源闲置,注意这里没有浪费CPU资源,去掉sleep(1)就是纯浪费了。 阻塞在什么时候发生呢?一般是等待IO操作(磁盘,数据库,网络等等)。此时如果单线程,CPU会干转不干实事(与本程序无关的事情都算不干实事,因为执行其他程序对我来说没意义),效率低下(针对这个程序而言),例如一个IO操作要耗时10毫秒,CPU就会被阻塞接近10毫秒,这是何等的浪费啊!要知道CPU是数着纳秒过日子的。 所以这种耗时的IO操作就用一个线程Thread去代为执行,创建这个线程的函数(代码)部分不会被IO操作阻塞,继续干这个程序中其他的事情,而不是干等待(或者去执行其他程序)。 同样在这个单核时代,多线程的这个消除阻塞的作用还可以叫做“并发”,这和并行是有着本质的不同的。并发是“伪并行”,看似并行,而实际上还是一个CPU在执行一切事物,只是切换的太快,我们没法察觉罢了。例如基于UI 的程序(俗话说就是图形界面),如果你点一个按钮触发的事件需要执行10秒钟,那么这个程序就会假死,因为程序在忙着执行,没空搭理用户的其他操作;而如果你把这个按钮触发的函数赋给一个线程,然后启动线程去执行,那么程序就不会假死,继续响应用户的其他操作。但是,随之而来的就是线程的互斥和同步、死锁等问题,详细见有关文献。 现在是多核时代了,这种线程的互斥和同步问题是更加严峻的,单核时代大都算并发,多核时代真的就大为不同,为什么呢?具体细节请参考有关文献。我

传统并行计算框架与MR的区别

现在MapReduce/Hadoop以及相关的数据处理技术非常热,因此我想在这里将MapReduce的优势汇总一下,将MapReduce与传统基于HPC集群的并行计算模型做一个简要比较,也算是对前一阵子所学的MapReduce知识做一个总结和梳理。 随着互联网数据量的不断增长,对处理数据能力的要求也变得越来越高。当计算量超出单机的处理能力极限时,采取并行计算是一种自然而然的解决之道。在MapReduce出现之前,已经有像MPI这样非常成熟的并行计算框架了,那么为什么Google还需要MapReduce,MapReduce相较于传统的并行计算框架有什么优势,这是本文关注的问题。 文章之初先给出一个传统并行计算框架与MapReduce的对比表格,然后一项项对其进行剖析。 MapReduce和HPC集群并行计算优劣对比 ▲ 在传统的并行计算中,计算资源通常展示为一台逻辑上统一的计算机。对于一个由多个刀片、SAN构成的HPC集群来说,展现给程序员的仍旧是一台计算机,只不过这台计算拥有为数众多的CPU,以及容量巨大的主存与磁盘。在物理上,计算资源与存储资源是两个相对分离的部分,数据从数据节点通过数据总线或者高速网络传输到达计算节点。对于数据量较小的计算密集型处理,这并不是问题。而对于数据密集型处理,计算节点与存储节点之间的I/O将成为整个系统的性能瓶颈。共享式架构造成数据集中放置,从而造成I/O传输瓶颈。此外,由于集群组件间耦合、依赖较紧密,集群容错性较差。 而实际上,当数据规模大的时候,数据会体现出一定的局部性特征,因此将数据统一存放、统一读出的做法并不是最佳的。 MapReduce致力于解决大规模数据处理的问题,因此在设计之初就考虑了数据的局部性原理,利用局部性原理将整个问题分而治之。MapReduce集群由普通PC机构成,为无共享式架构。在处理之前,将数据集分布至各个节点。处理时,每个节点就近读取本地存储的数据处理(map),将处理后的数据进行合并(combine)、排序(shuffle and sort)后再分发(至reduce节点),避免了大量数据的传输,提高了处理效率。无共享式架构的另一个好处是配合复制(replication)策略,集群可以具有良好的容错性,一部分节点的down机对集群的正常工作不会造成影响。 硬件/价格/扩展性 传统的HPC集群由高级硬件构成,十分昂贵,若想提高HPC集群的性能,通常采取纵向扩展的方式:即换用更快的CPU、增加刀片、增加内存、扩展磁盘等。但这种扩展方式不能支撑长期的计算扩展(很容易就到顶了)且升级费用昂贵。因此相对于MapReduce集群,HPC集群的扩展性较差。 MapReduce集群由普通PC机构成,普通PC机拥有更高的性价比,因此同等计算能力的集群,MapReduce集群的价格要低得多。不仅如此,MapReduce集群

Pyhton 如何实现多线程并行计算

Pyhton 如何实现多线程并行计算 一、串行计算 先看一段简单的代码: import time t0=time.time() for k in range(30): values=k*k print(values) time.sleep(1) t1=time.time() print('运行时间为:',int(t1-t0)) 上面的例子中,我们用一个for循环计算自然数的平方。这里我们一个自然数计算完才能接着计算另外一个数。这种计算方式我们称为“串行计算”。早期为什么采用这种串行计算呢?因为以前个人电脑CPU是单核的,硬件的条件决定了程序的处理方式。 能不能几个数同时计算?好比如在银行的营业厅排队,如果只开一个窗口办理业务,你需要等前面一个人办完,才轮到你,如果能开多个窗口,显然会快很多。这种开多个窗口处理业务的想法,在计算机中的应用就是“并行计算”。多个窗口对应的就是计算机有多个核。(理解了“并行计算”,就容易进一步理解分布式计算。) 二、多核与线程 个人电脑的处理器最早是单核的。 多内核(multicore chips)是指在一枚处理器(chip)中集成两个或多个完整的计算引擎(内核)。 2005年4月,英特尔仓促推出简单封装双核的奔腾D和奔腾四至尊版840。 但真正的“双核元年”,则被认为是2006年。这一年的7月23日,英特尔基于酷睿(Core)架构的处理器正式发布。2006年11月,又推出面向服务器、工作站和高端个人电脑的至强(Xeon)5300和酷睿双核和四核至尊版系列处理器。 进入2009年,处理器已经由双核升级到四核时代,在斯坦福大学召开的Hot Chips大会上,IBM、富士通、AMD和Intel等众多芯片制造商展示出其六核、八核等多核服务器处

linux线程

关于linux线程 在许多经典的操作系统教科书中, 总是把进程定义为程序的执行实例, 它并不执行什么, 只是维护应用程序所需的各种资源. 而线程则是真正的执行实体.为了让进程完成一定的工作, 进程必须至少包含一个线程. 如图1. 进程所维护的是程序所包含的资源(静态资源), 如: 地址空间, 打开的文件句柄集, 文件系统状态, 信号处理handler, 等; 线程所维护的运行相关的资源(动态资源), 如: 运行栈, 调度相关的控制信息, 待处理的信号集, 等; 然而, 一直以来, linux内核并没有线程的概念. 每一个执行实体都是一个task_struct结构, 通常称之为进程. 如图2. 进程是一个执行单元, 维护着执行相关的动态资源. 同时, 它又引用着程序所需的静态资源.通过系统调用clone创建子进程时, 可以有选择性地让子进程共享父进程所引用的资源. 这样的子进程通常称为轻量级进程.linux上的线程就是基于轻量级进程, 由用户态的pthread库实现的.使用pthread以后, 在用户看来, 每一个task_struct就对应一个线程, 而一组线程以及它们所共同引用的一组资源就是一个进程.但是, 一组线程并不仅仅是引用同一组资源就够了, 它们还必须被视为一个整体.对此, POSIX标准提出了如下要求: 1, 查看进程列表的时候, 相关的一组task_struct应当被展现为列表中的一个节点; 2, 发送给这个"进程"的信号(对应kill系统调用), 将被对应的这一组task_struct所共享, 并且被其中的任意一个"线程"处理; 3, 发送给某个"线程"的信号(对应pthread_kill), 将只被对应的一个task_struct接收, 并且由它自己来处理; 4, 当"进程"被停止或继续时(对应SIGSTOP/SIGCONT信号), 对应的这一组task_struct 状态将改变; 5, 当"进程"收到一个致命信号(比如由于段错误收到SIGSEGV信号), 对应的这一组task_struct将全部退出; 6, 等等(以上可能不够全); linuxthreads

Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。 线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。 Hello World(线程创建、结束、等待) 创建线程 pthread_create 线程创建函数包含四个变量,分别为: 1. 一个线程变量名,被创建线程的标识 2. 线程的属性指针,缺省为NULL即可 3. 被创建线程的程序代码 4. 程序代码的参数 For example: - pthread_t thrd1? - pthread_attr_t attr? - void thread_function(void argument)? - char *some_argument? pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument); 结束线程 pthread_exit 线程结束调用实例:pthread_exit(void *retval); //retval用于存放线程结束的退出状态 线程等待 pthread_join pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:pthread_t th //th是要等待结束的线程的标识 void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。 调用实例:pthread_join(thrd1, NULL); example1: 1 /************************************************************************* 2 > F i l e N a m e: t h r e a d_h e l l o_w o r l d.c 3 > A u t h o r: c o u l d t t(f y b y) 4 > M a i l: f u y u n b i y i@g m a i l.c o m 5 > C r e a t e d T i m e: 2013年12月14日 星期六 11时48分50秒 6 ************************************************************************/ 7 8 #i n c l u d e 9 #i n c l u d e 10 #i n c l u d e

11 12 v o i d p r i n t_m e s s a g e_f u n c t i o n (v o i d *p t r)? 13 14 i n t m a i n() 15 { 16 i n t t m p1, t m p2?

分布式与并行计算报告

并行计算技术及其应用简介 XX (XXX,XX,XXX) 摘要:并行计算是实现高性能计算的主要技术手段。在本文中从并行计算的发展历程开始介绍,总结了并行计算在发展过程中所面临的问题以及其发展历程中出现的重要技术。通过分析在当前比较常用的实现并行计算的框架和技术,来对并行计算的现状进行阐述。常用的并行架构分为SMP(多处理系统)、NUMA (非统一内存存储)、MPP(巨型并行处理)以及集群。涉及并行计算的编程模型有MPI、PVM、OpenMP、TBB及Cilk++等。并结合当前研究比较多的云计算和大数据来探讨并行计算的应用。最后通过MPI编程模型,进行了并行编程的简单实验。 关键词:并行计算;框架;编写模型;应用;实验 A Succinct Survey about Parallel Computing Technology and It’s Application Abstract:Parallel computing is the main technology to implement high performance computing. This paper starts from the history of the development of Parallel Computing. It summarizes the problems faced in the development of parallel computing and the important technologies in the course of its development. Through the analysis of framework and technology commonly used in parallel computing currently,to explain the current situation of parallel computing.Framework commonly used in parallel are SMP(multi processing system),NUMA(non uniform memory storage),MPP(massively parallel processing) and cluster.The programming models of parallel computing are MPI, PVM, OpenMP, TBB and Cilk++, etc.Explored the application of parallel computing combined with cloud computing and big data which are very popular in current research.Finally ,through the MPI programming model,a simple experiment of parallel programming is carried out. Key words:parallel computing; framework; programming model; application; experiment 1引言 近年来多核处理器的快速发展,使得当前软件技术面临巨大的挑战。单纯的提高单机性能,已经不能满足软件发展的需求,特别是在处理一些大的计算问题上,单机性能越发显得不足。在最近AlphaGo与李世石的围棋大战中,AlphaGo就使用了分布式并行计算技术,才能获得强大的搜索计算能力。并行计算正是在这种背景下,应运而生。并行计算或称平行计算时相对于串行计算来说的。它是一种一次可执行多个指令的算法,目的是提高计算速度,及通过扩大问题求解规模,解决大型而复杂的计算问题。可分为时间上的并行和空间上的并行。时间上的并行就是指流水线技术,而空间上的并行则是指用多个处理器并发的执行计算。其中空间上的并行,也是本文主要的关注点。 并行计算(Parallel Computing)是指同时使用多种计算资源解决计算问题的过程,是提高计算机系统计算速度和处理能力的一种有效手段。它的基本思想是用多个处理器来协同求解同一问题,即将被求解的问题分解成若干个部分,各部分均由一个独立的处理机来并行计算。并行计算系统既可以是专门设计的,含有多个处理器的超级计算机,也可以是以某种方式互联的若干台的独立计算机构成的集群。通过并行计算集群完成数据的处理,再将处理的结果返回给用户。 目前常用的并行计算技术中,有调用系统函数启动多线程以及利用多种并行编程语言开发并行程序,常用的并行模型有MPI、PVM、OpenMP、TBB、Cilk++等。利用这些并行技术可以充分利用多核资源适应目前快速发展的社会需求。并行技术不仅要提高并行效率,也要在一定程度上减轻软件开发人员负担,如近年来的TBB、Cilk++并行模型就在一定程度上减少了开发难度,提高了开发效率,使得并行软件开发人员把更多精力专注于如何提高算法本身效率,而非把时间和精力放在如何去并行一个算法。

相关文档 最新文档