文档库 最新最全的文档下载
当前位置:文档库 › linux内核多线程

linux内核多线程

linux内核多线程
linux内核多线程

Linux内核多线程(转)

Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合

理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度

由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它

只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只

在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。

内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加"[]"的,这些进程就是内核线程。

创建内核线程最基本的两个接口函数是:

kthread_run(threadfn, data, namefmt, ...)

kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)

这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。

kthread_run 事实上是一个宏定义:

/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @namefmt: printf-style name for the thread. * * Description: Convenient wrapper for kthread_create() followed by * wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM). */#define kthread_run(threadfn, data, namefmt, ...)

\

({

struct task_struct *__k

= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__);

if (!IS_ERR(__k))

wake_up_process(__k);

__k;

})

kthread_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的

好处是用kthread_run()创建的线程可以直接运行。外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用

kthread_create()创建线程,如果创建成功,再调用

wake_up_process()唤醒新创建的线程。

kthread_create()根据参数向kthread_create_list中发送一个请求,并唤醒kthreadd,之后会调用

wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在

kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用

schedule()调度出去。kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。

kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。

在介绍完如何创建线程之后,下面来介绍另外两个基本的函

数:

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志

should_stop,并等待线程主动结束,返回线程的返回值。在调用kthread_stop()结束线程之前一定要检查该线程是否还在运行(通过

kthread_run 返回的task_stuct

是否有效),否则会造成灾难性的后果。kthread_run的返回值tsk。不能用tsk是否为NULL进行检查,而要用IS_ERR()宏定义检查,

这是因为返回的是错误码,大致从0xfffff000~0xffffffff。kthread_should_stop()返回should_stop标志(参见struct kthread )。它用于创建的线程检查结束标志,并决定是否退出。

kthread() (注:原型为:static int kthread(void

*_create) )的实现在kernel/kthread.c中,头文件是

include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()

中,不断检查一个kthread_create_list

链表。kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链

表,并调用create_kthread()创建相应的线程。

create_kthread()则进一步调用更深层的kernel_thread()创建

线程,入口函数设在kthread()中。

外界调用kthread_stop()删除线程。kthread_stop首先设置结束标志should_stop,然后调用

wake_for_completion(&kthread->exited)上,这个其实是新线程task_struct上的

vfork_done,会在线程结束调用do_exit()时设置。

附:

struct kthread { int should_stop; struct completion exited;};int kthreadd(void *unused)

{ struct task_struct *tsk = current; /* Setup a clean context for our children to inherit. */

set_task_comm(tsk, "kthreadd");

ignore_signals(tsk); set_cpus_allowed_ptr(tsk,

cpu_all_mask);

set_mems_allowed(node_states[N_HIGH_MEMORY]); current->flags |= PF_NOFREEZE |

PF_FREEZER_NOSIG;

for (;;)

{ set_current_state(TASK_INTERRUPTIBL E); if (list_empty(&kthread_create_list)) schedule();

__set_current_state(TASK_RUNNING);

spin_lock(&kthread_create_lock); while (!list_empty(&kthread_create_list))

{ struct kthread_create_info *create; create = list_entry(kthread_create_list.next,

struct kthread_create_info, list);

list_del_init(&create->list);

spin_unlock(&kthread_create_lock);

create_kthread(create);

spin_lock(&kthread_create_lock); } spin_unlock(&kthread_create_lock); }

return 0;

}/** * kthread_stop - stop a thread created by

kthread_create(). * @k: thread created by kthread_create().

* * Sets kthread_should_stop() for @k to return true, wakes it, and * waits for it to exit. This can also be called after kthread_create() * instead of calling wake_up_process(): the thread will exit without * calling threadfn(). * * If threadfn() may call do_exit() itself, the caller must ensure * task_struct can't go away. * * Returns the result of threadfn(), or %-EINTR if wake_up_process() * was never called. */int kthread_stop(struct task_struct *k)

{ struct kthread *kthread; int ret;

trace_sched_kthread_stop(k);

get_task_struct(k);

kthread = to_kthread(k); barrier(); /* it might have exited */ if (k->vfork_done != NULL)

{ kthread->should_stop = 1;

wake_up_process(k);

wait_for_completion(&kthread->exited); } ret = k->exit_code; put_task_struct(k);

trace_sched_kthread_stop_ret(ret); return ret;}Linux 内核多线程(二)

内核多线程是在项目中使用到,自己也不熟悉,遇到一个很囧的问题,导致cpu运行100%。

这是写的第一个内核线程程序,通过全局变量来实现两个内核线程之间的通信。但是这里遇到致命错误,就是:每当wait_event_interruptible()被wake_up_interruptible

唤醒之后线程就进入死循环。后面发现是线程不会主动的自己调度,需要显式的通过schedule 或者

schedule_timeout()来调度。如果不加tc = 0

这一行,wait_event_intrruptible()就一直不会睡眠(参见前面的文章“等待队列”),不会被调度放弃CPU,因此进入死循环。这个过程可以通过分析wait_event_intrruptible()的源代码来看出。

#include <linux/init.h> #include

<linux/module.h> #include <linux/kthread.h> #include <linux/wait.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct task_struct * _tsk; static struct task_struct * _tsk1;static int tc = 0;static wait_queue_head_t

log_wait_queue; static int thread_function(void *data) { do { printk(KERN_INFO "IN thread_function thread_function: %d times \n", tc);

wait_event_interruptible(log_wait_queue,tc == 10);

tc = 0; ///必须加这一行,内核才会进行调度。内核线程不像应用程序会主动调度,我们需要显式的使用调度函数,

想要在thread_function_1中去重置tc的值是不可能的,因为线程不会被调度,该线程会一直占用CPU

printk(KERN_INFO "has been woke up !\n"); }while(!kthread_should_stop()); return tc;

} static int thread_function_1(void *data)

{ do { printk(KERN_INFO "IN thread_function_1 thread_function: %d times\n", ++tc); if(tc == 10 &&

waitqueue_active(&log_wait_queue))

{ wake_up_interruptible(&a mp;log_wait_queue); }

msleep_interruptible(1000);

}while(!kthread_should_stop()); return tc; } static int hello_init(void) { printk(KERN_INFO "Hello, world!\n");

init_waitqueue_head(&log_wait_queue); _tsk = kthread_run(thread_function, NULL, "mythread"); if (IS_ERR(_tsk)) { //需要使用IS_ERR()来判断线程是否有

效,后面会有文章介绍IS_ERR()

printk(KERN_INFO "first create kthread failed!\n"); } else { printk(KERN_INFO "first create ktrhead

ok!\n"); } _tsk1 =

kthread_run(thread_function_1,NULL, "mythread2"); if (IS_ERR(_tsk1)) { printk(KERN_INFO "second create kthread failed!\n"); } else

{ printk(KERN_INFO "second create ktrhead

ok!\n"); } return 0; }

static void hello_exit(void)

{ printk(KERN_INFO "Hello, exit!\n"); if

(!IS_ERR(_tsk)){ int ret = kthread_stop(_tsk); printk(KERN_INFO "First thread function has

stopped ,return %d\n", ret); } if(!IS_ERR(_tsk1)) { int ret = kthread_stop(_tsk1);

printk(KERN_INFO "Second thread function_1 has stopped ,return %d\n",ret); }}

module_init(hello_init); module_exit(hello_exit);

说明:这个程序的目的就是,使用一个线程

(thread_function_1)通知另外一个线程(thread_function)某个条件(tc == 10)满足(比如接收线程收到10帧然后通

知处理线程处理接收到的数据)

运行结果:

程序加载并运行(tc 的值等于10 之后就会唤醒另外一个线程,之后tc又从10开始计数):

程序卸载(程序卸载其实还是要很注意的,很多程序在卸载的时候回出现各种问题后面文章会提到):

这里介绍另一种线程间通信的方式:completion机制。Completion机制是线程间通信的一种轻量级机制:允许一个线程告诉另一个线程工作已经完成。为使用completion, 需要包含头文件<linux/completion.h>。

可以通过以下方式来创建一个completion :

DECLARE_COMPLETION(my_completion);

或者, 动态创建和初始化:

struct completion my_completion;

init_completion(&my_completion);

等待completion 是一个简单事来调用: void

wait_for_completion(struct completion *c);

注意:这个函数进行一个不可打断的等待. 如果你的代码调用wait_for_completion 并且

没有人完成这个任务, 结果会是一个不可杀死的进程。

completion 事件可能通过调用下列之一来发出:

void complete(struct completion *c);

void complete_all(struct completion *c);

如果多于一个线程在等待同一个completion 事件, 这2

个函数做法不同. complete 只

唤醒一个等待的线程, 而complete_all 允许它们所有都继续。

下面来看使用completion机制的实现代码:

#include <linux/init.h> #include

<linux/module.h> #include <linux/kthread.h> #include <linux/wait.h>#include

<linux/completion.h>

MODULE_LICENSE("Dual BSD/GPL");

static struct completion comp; static struct task_struct * _tsk; static struct task_struct * _tsk1;static int tc = 0; static int thread_function(void *data)

{ do { printk(KERN_INFO "IN thread_function thread_function: %d times \n", tc);

wait_for_completion(&comp);

//tc = 0; ///在哪里都行

printk(KERN_INFO "has been woke

up !\n"); }while(!kthread_should_stop()); return tc; } static int thread_function_1(void *data)

{ do { printk(KERN_INFO "IN thread_function_1 thread_function: %d times\n", ++tc); if(tc == 10)

{ complete(&comp);

tc = 0; }

msleep_interruptible(1000);

}while(!kthread_should_stop()); return tc; } static int hello_init(void)

{ printk(KERN_INFO "Hello, world!\n");

init_completion(&comp); _tsk =

kthread_run(thread_function, NULL, "mythread"); if (IS_ERR(_tsk)) { printk(KERN_INFO "first create kthread failed!\n"); } else

{ printk(KERN_INFO "first create ktrhead

ok!\n"); } _tsk1 =

kthread_run(thread_function_1,NULL, "mythread2"); if (IS_ERR(_tsk1)) { printk(KERN_INFO "second create kthread failed!\n"); } else

{ printk(KERN_INFO "second create ktrhead

ok!\n"); } return 0; } static void

hello_exit(void)

{ printk(KERN_INFO "Hello, exit!\n"); if

(!IS_ERR(_tsk)){ int ret = kthread_stop(_tsk); printk(KERN_INFO "First thread function has

stopped ,return %d\n", ret); } if(!IS_ERR(_tsk1)) { int ret = kthread_stop(_tsk1);

printk(KERN_INFO "Second thread function_1 has stopped ,return %d\n",ret); }}

module_init(hello_init); module_exit(hello_exit);

运行结果:

自己创建的内核线程,当把模块加载到内核之后,可以通过:ps –ef

命令来查看线程运行的情况。通过该命令可以看到该线程的pid和ppid等。也可以通过使用kill –s 9 pid

来杀死对应pid的线程。如果要支持kill命令自己创建的线

程里面需要能接受kill信号。这里我们就来举一个例,支持kill命令,同时rmmod的

时候也能杀死线程。

#include <linux/kernel.h>#include

<linux/module.h>#include <linux/init.h>#include <linux/param.h>#include <linux/jiffies.h>#include <asm/system.h>#include

<asm/processor.h>#include

<asm/signal.h>#include <linux/completion.h>

// for DECLARE_COMPLETION()#include

<linux/sched.h> #include

<linux/delay.h> // mdelay()#include

<linux/kthread.h> MODULE_LICENSE("GPL"); static DECLARE_COMPLETION(my_completion);

static struct task_struct *task;

int flag = 0;

int my_fuction(void *arg)

{ printk(" in %s()\n", __FUNCTION__);

allow_signal(SIGKILL); //使得线程可以接收SIGKILL信号mdelay(2000); printk(" my_function complete()\n");

printk("should stop: %d\n",kthread_should_stop());

while (!signal_pending(current)

&& !kthread_should_stop()) {//使得线程可以可以被杀死,也可以再rmmod的时候结束printk(" jiffies is %lu\n", jiffies);

set_current_state(TASK_INTERRUPTIBLE);

schedule_timeout(HZ * 5); printk("should stop: %d\n",kthread_should_stop()); }

printk("Leaving my_function\n"); flag = 1; //flag很关键!return 0;} static int __init init(void)

{ task = kthread_run(my_fuction,NULL,"my_function"); printk("<1> init wait_for_completion()\n"); return 0;} static void __exit finish(void)

{ int ret; if(!flag)

{ if (!IS_ERR(task))

{ int ret = kthread_stop(task);

printk(KERN_INFO "First thread function has

stopped ,return %d\n",

ret); } } printk("task_struct: 0x%x",task); printk(" Goodbye\n");} module_init(init);module_exit(finish);

运行结果(执行kill之后):

运行结果(rmmod之后):

说明:程序运行后线程循环执行每隔5个内核ticks 就答应一次当前的jiffies值。可以通过kthread_stop()来结束,也可以通过kill命令来结束。

程序中使用了flag 变量来控

制是否使用

kthread_stop()函数有两个原因:首先,当线程创建成功之后IS_ERR()不能检测出线程是否还在运行,因为此时task是一个正常的地址

而不是错误码(后面会说明IS_ERR的原理);其次,线程不能被杀次两次,如果使用kill命令之后线程已经被杀死,但是在此使用

kthread_stop()函数就会出严重问题,因为此时线程已经被杀死,task指向的地址已经无效,struct kthread

也已经不存在,操作此时使用kthread_stop()设置

should_stop是没有意义的。同样可以得出结论,当线程结束之后使用

kthread_should_stop()来查看线程运行状态也会造成内核异

常。

IS_ERR()函数的原理:

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

static inline long IS_ERR(const void *ptr)

{

return IS_ERR_VALUE((unsigned long)ptr);

}

内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来。

所幸的是,内核返回的指针一般是指向页面的边界(4K边界),即ptr & 0xfff == 0这样ptr的值不可能落在(0xfffff000,0xffffffff)之间,而一般内核的出错代码也是一个小负数,在-1000到0之间,转变成unsigned long,正好在(0xfffff000,0xffffffff)之间。因此可以用(unsigned long)ptr > (unsigned long)-1000L也就等效于(x) >= (unsigned

long)-MAX_ERRNO

其中MAX_ERRNO 为4095来判断内核函数的返回值是一

个有效的指针,还是一个出错代码。涉

及到的任何一个指针,必然有三种情况,一种是有效指针,一种是NULL,空指针,一种是错误指针,或者说无效指针.而所谓的

错误指针就是指其已经到达了

最后一个page.比如对于32bit的系统来说,内核空间最高地

址0xffffffff,那么最后一个page就是指的

0xfffff000~0xffffffff(假设4k一个page).这段地址是被保留的,如果超过这个地址,则肯定是错误的。

这里主要实现两个线程间通信,当flag = 10 之后通知另外一个线程(也就是“Linux内核多线程(二)”中的程序的各种平台实现)。

首先是C++ 11 的方式:

#include <thread>

#include <iostream>

#include <mutex>

#include <queue>

#include <condition_variable>

#include <atomic>using namespace std;

const int M = 10;int main()

{

mutex lockBuffer;

int flag = 0;

bool stop = false;

int count = 0; condition_variable_any

recv_task_cond; condition_variable_any RecieveTask_cond; thread recv_task([&]() {

while(true)

{ std::this_thread::sleep_for (chrono::milliseconds (1000));

lockBuffer.lock ();

if(stop)

{

lockBuffer.unlock();

RecieveTask_cond.notify_one();

break;

} if (flag == M)

{ cout<< "recv task try to wake up RecieveTask! "<<endl;

count++;

lockBuffer.unlock ();

RecieveTask_cond.notify_one ();

} else

{

实验七: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多线程编程的基本的函数

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下的多线程编程常用函数

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");

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?

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

跟我学Linux编程-13-多线程编程-性能

多线程编程-性能 在上一章节中,我们介绍了线程互斥锁的使用。通过互斥锁,使得每个线程只能串行地运行临界区代码,从而有效地避免了多线程冲突。串行的代码运行方式削弱了多线程并发运行的特性,因此线程锁也潜在地降低了程序性能,如何杜绝线程冲突,又尽可能不影响程序效率,是我们每一个线程从员需要认真考虑的事情。 减少线程性能下降的方法有如下几点: 1 尽可能减小互斥锁的颗粒,使串行代码的比例减小从而提高效率,这在上一章节末已提过。 2 加锁解锁之间的代码,运行时间尽可能减少,避免有sleep或死循环一类的超时等待。 3 对不同的冲突资源使用不同的线程锁,避免不相关的线线之间因锁反而产生关联。 4 使用pthread_mutex_trylock代替pthread_lock在检测和处理锁冲突,实现单线程对多个对像的非阻塞处理。 今天我们重点介绍pthread_mutex_trylock的使用,并通过实例的来展现其用法,探究其提高程序效果的原理。 首先,我们来看pthread_lock与pthread_mutex_trylock的函数原型: int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock( pthread_mutex_t *mutex); 可以看到,这两个函数的原型非常想像,功能也比较类似,都是尝试对给定的锁对像进行加锁,如果成功,则线程获得该锁,并返回0;不同点在于当锁已被其他线程占有的情况下,pthread_mutex_lock会阻塞,直至锁被其它线程释放并且本线程获得锁;如pthread_mutex_trylock则不然,如果锁当前已被其他线程占有,则立刻返回失败(非0值),我们的程序可判读返回值并进行下一步的处理(如处理另一个任务),避免线程被阻塞,从而提高了线程并发度,提升程序性能。我们接下来看例子: #include #include #include typedef struct { int m_cnt[3]; pthread_mutex_t m_mutex; } count_t; #define COUNT_CNT 20 #define CYC_CNT 10000 count_t g_counts[COUNT_CNT]; void *thread_task1(void *arg)

跟我学Linux编程-12-多线程编程-同步

多线程编程-同步 在上一章节中,我们通过程序示例,见证了单线程世界中不可能发生的事件(一个数既是奇数又是偶数)在多线程环境中是怎样分分钟发生的,我通过细分程序执行步骤,分析了奇异事件发生的过程,并探明了其原因:一个线程在对全局变量gcnt进行两次判读的过程中,另一个线刚好改变了这个变量的值。在多线程编程术语中,称这两个线程同时进入了临界区域。 所谓临界区域,是指多线程环境下两个及以上线程同时执行可能会导致冲突的一段代码。在上一章节的示例中,这几行代码就是一个临界区域: gcnt++; if (gcnt % 2) { if (!(gcnt % 2)) printf("[%d] : %d\n", id, gcnt); } 冲突之所以会发生,是因为临界区域的代码,通常需要很多个CPU指令周期才能完成,其运行过程随时可能被打断(进行了线程调试),CPU去运行另外的线程,如果这个线程刚好也进入了临界区域,则异常的程序状态极可能会发生。 如果当某个线程进入临界区域,在其退出区域之前,其他的线程无论如何也不能进入该区域,那么冲突就不会发生。Linux提供了这种保证多线程进入临界区域互斥的机制,这正是本章节所要介绍的内容:线程锁。 我们今天的示例程序还是在上一章节的示例上改进而来的,我们的任务就是使用线程锁,保证“一个数既是奇数又是偶数”的奇异事件在多线程环境下也不发生,代码如下: #include #include #include int gcnt = 0; pthread_mutex_t g_mutex; void *thread_task(void *arg) { int id = (int)arg; while (1) { pthread_mutex_lock(&g_mutex); gcnt++; if (gcnt % 2)

linux多线程编程

2.终止线程 (2) 3. 等待线程终止 (2) pthread_exit和pthread_join进一步说明: (3) 4.分离线程 (7) 5.获取线程标识符 (8) 6.比较线程ID (8) 7.一次性初始化 (8) 8. 设置线程的调度策略和优先级 (9) 9. 获取线程的优先级 (11) 10.取消线程 (12) 取消线程,是否会释放线程的所有资源?例子: (14) 设置取消类型 (16) 11.初始化属性 (17) 12.设置分离状态 (18) 13.设置范围 (18) 14. 设置继承的调度策略 (18) 16. 设置调度参数 (19) 17.初始化互斥锁 (21) 18.销毁互斥锁 (21) 19.锁定互斥锁 (22) 20.解除锁定互斥锁 (23) 21. 互斥锁的类型: (23) 22. 初始化互斥锁属性对象 (23) 23. 销毁互斥锁属性对象 (23) 24.设置互斥锁类型的属性 (24) 互斥锁动态初始化和静态初始化区别: (26) 销毁互斥锁:事实上没做任何销毁操作,如下: (27) 非递归类型的互斥锁解锁和加锁操作: (27) 29.初始化条件变量 (27) 30.基于条件变量阻塞 (27) 31.解除阻塞一个线程 (28) 31.解除阻塞所有线程 (29) 33. 在指定的时间之前阻塞 (30) 32.唤醒丢失问题 (31) 33. 计数信号量概述 (31) 34. 初始化信号 (31) 35. 增加信号 (31) 36. 基于信号计数进行阻塞 (32) 37.多线程链表添加删除例子(使用条件变量实现互斥): (32) 38.为线程特定数据创建键 (34) 39. 删除线程特定数据键 (35) 40.设置线程特定数据 (35) 41. 获取线程特定数据 (35)

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.

Linux多线程编程问题

Linux 多线程编程问题 1重入问题 传统的UNIX没有太多考虑线程问题,库函数里过多使用了全局和静态数据,导致严重的线程重入问题。 1.1–D_REENTRANT /-pthread和errno的重入问题。 所先UNIX的系统调用被设计为出错返回-1,把错误码放在errno中(更简单而直 接的方法应该是程序直接返回错误码,或者通过几个参数指针来返回)。由于线程 共享所有的数据区,而errno是一个全局的变量,这里产生了最糟糕的线程重入问 题。比如: do { bytes = recv(netfd, recvbuf, buflen, 0); } while (bytes != -1 && errno != EINTR); 在上面的处理recv被信号打断的程序里。如果这时连接被关闭,此时errno应该不 等于EINTR,如果别的线程正好设置errno为EINTR,这时程序就可能进入死循环。 其它的错误码处理也可能进入不可预测的分支。 在线程需求刚开始时,很多方面技术和标准(TLS)还不够成熟,所以在为了 解决这个重入问题引入了一个解决方案,把errno定义为一个宏: extern int *__errno_location (void); #define errno (*__errno_location()) 在上面的方案里,访问errno之前先调用__errno_location()函数,线程库提供这个 函数,不同线程返回各自errno的地址,从而解决这个重入问题。在编译时加 -D_REENTRANT就是启用上面的宏,避免errno重入。另外-D_REENTRANT 还影响一些stdio的函数。在较高版本的gcc里,有很多嵌入函数的优化,比如把printf(“Hello\n”); 优化为 puts(“hello\n”); 之类的,有些优化在多线程下有问题。所以gcc引入了–pthread 参数,这个 参数出了-D_REENTRANT外,还校正一些针对多线程的优化。 因为宏是编译时确定的,所以没有加-D_REENTRANT编译的程序和库都有errno 重入问题,原则上都不能在线程环境下使用。不过在一般实现上主线程是直接使用 全局errno变量的,也就是__errno_location()返回值为全局&errno,所以那些没加 -D_REENTRANT编译的库可以在主线程里使用。这里仅限于主线程,有其它且只 有一个固定子线程使用也不行,因为子线程使用的errno地址不是全局errno变量 地址。 对于一个纯算法的库,不涉及到errno和stdio等等,有时不加_REENTRANT也是 安全的,比如一个纯粹的加密/解谜函数库。比较简单的判断一个库是否有errno问 题是看看这个库是使用了errno还是__errno_location():

实验七: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操作系统-实验-Linux-多线程编程

《操作系统》实验报告

用gcc编译的时候需要加-lpthread,否则会出现以下错误: 五、思考题 多进程编程与多线程编程有何区别? 相同点:Linux下不管是多线程编程还是多进程编程,最终都是用do_fork 实现 不同点:父子进程变量是互不影响的,由于父子进程地址空间是完全隔开的,变量的地址可以是完全相同的。Linux下编程多用多进程编程少用多线程编程。多线程比多进程成本低,但性能更低:多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不;多线程是平面交通系统,造价低,但红绿灯太多,老堵车。 1、多进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。 2、多线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。两者都可以提高程序的并发度,提高程序运行效率和响应时间。 3、线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在5MP机器上运行,而进程则可以跨机器迁移。

代码: #include #include #include #include #include char globe_buffer[100]; void *read_buffer_thread(void *arg); int main() { int res,i; pthread_t read_thread; for(i=0;i<20;i++) globe_buffer[i]=i; printf("\nxiancheng thread : write buffer finish\n"); sleep(3); res = pthread_create(&read_thread, NULL, read_buffer_thread, NULL); if (res != 0) { printf("Read Thread creat Error!"); exit(0); } sleep(1); printf("waiting for read thread to finish...\n"); res = pthread_join(read_thread, NULL); if (res != 0) { printf("read thread join failed!\n"); exit(0); } printf("read thread xiancheng OK, have fun!! exit ByeBye\n"); return 0; } void *read_buffer_thread(void *arg) { int i,x;

Linux多线程编程小结

Linux多线程编程小结 愤怒的小狐狸----博客专栏 前一段时间因为开题的事情一直耽搁了我搞Linux的进度,搞的我之前学的东西都遗忘了,很烦躁的说,现在抽个时间把之前所学的做个小节。文章内容主要总结于《Linux程序设计第3版》。 1.Linux进程与线程 Linux进程创建一个新线程时,线程将拥有自己的栈(因为线程有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号句柄和当前目录状态。 Linux通过fork创建子进程与创建线程之间是有区别的:fork创建出该进程的一份拷贝,这个新进程拥有自己的变量和自己的PID,它的时间调度是独立的,它的执行几乎完全独立于父进程。 进程可以看成一个资源的基本单位,而线程是程序调度的基本单位,一个进程内部的线程之间共享进程获得的时间片。 2._REENTRANT宏 在一个多线程程序里,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据。 为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。 _REENTRANT为我们做三件事情,并且做的非常优雅: (1)它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname 变成gethostbyname_r。 (2)stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。 (3)在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。

Linux多线程编程和Linux 2.6下的NPTL

Linux多线程编程和Linux 2.6下的NPTL 这几天由于工作需要,琢磨了一下Linux下的多线程的相关资料。Linux下最常用的多线程支持库为Pthread库,它是glibc库的组成部分。但是关于Pthread的说明文档非常缺乏,特别是对POSIX多线程规范的介绍以及pthread库中多线程实现方式的介绍实在是少之又少。而多线程编程对于系统程序员而言是必须掌握的技术,因此总是让学习中的程序员觉得头痛不以。我自己也没有太多多线程编程的经验,在这里只是把自己收集到的一些关于Linux上多线程还算新的资料进行汇总来抛砖引玉,以便相互学习交流。 这里顺便提一下市面上有的一本介绍多线程的书《Posix 多线程编程》,它是英文版《Programming with POSIX Muiltthread》中译本,这也是半年前我所能找到的唯一专题介绍多线程编程的书。我个人感觉这本书的前面1/3之一的内容写的还是不错的,但是后面的东西就非常晦涩并且有很多明显的文字错误。看看这本书的翻译者是好几个人,估计每个人的翻译能力不同造成了这本书的虎头蛇尾。因此我不建议大家去买这本书作为圣经收藏。这本书前半步的内容主要围绕Posix的多线程,介绍的比较精彩的就是几个多线程编程模型,把多线程的互斥和同步机制介绍的挺酣畅的,推荐一看。这些内容并非这本书首创,早在《UNIX网络编程》第二卷进程间通信就有了这些经典的介绍,但是能系统的把这些机制结合到多线程编程中来还是有可圈可点之处的。此外毕竟《UNIX网络编程》两卷内容太老,书也太厚了,并不是大多数程序员所能坐下来细细看的。这里我还想表达一下对微软在技术上的不足斥责。在msdn中platform sdk部分中的windows多线程编程的内容真是简陋的可笑,只有傻兮兮的建立和退出线程的函数,关于互斥,条件的介绍一概全无。只能在它的sample代码中自己去找,sample 代码里面的线程同步方式居然是做一个死循环来死等,也不知道它把windows卖这么多钱是干什么吃的。MFC中多线程的封装倒是看上去像那么一回事情了,但是我想象不出在如此简陋的系统api上微软到底是如何实现出MFC上线程功能的。拥护windows的人不要在这里砸鸡蛋,最好也能写一篇windows上的多线程介绍除了。这比砸鸡蛋来得有意义多了。好了,书归正传继续说Linux上的多线程。 在Linux上,从内核角度而言,基本没有什么线程和进程的区别--大家都是进程。一个进程的多个线程只是多个特殊的进程他们虽然有各自的进程描述结构,却共享了同一个代码上下文。在Linux上,这样的进程称为轻量级进程Light weight process。致此,就是关于线程的总体概念了,我们往往就在了解这个概念的情况下开始我们的多线程编程之旅。这对于多线程编程入门已经足够了,然而事实上线程却要复杂的多。首先多线程间的优先级调度,内存资源(栈)分配和信号投递就不是简单的共享同一个进程代码上下文所能所能解决的。其次,效率的问题:如何有效的使用多cpu资源(2.4内核的多线程就无法使用多个cpu,一个进程的线程都被限制在同一个cpu上运行)。因此多线程库Pthread的实现并不是一件简单的事情,它建立在特有的线程模型之上。 在Linux 2.4内核中,Linux内核中使用了一个内核线程来处理用户态进程中的多个线程的上下文切换(线程切换)。由于内核中并没有什么线程组的概念,即一个进程的多个线程,因此必须依靠在pthread库中实现一个额外的线程来管理其他用户线程(即用户程序生成的线程)的建立,退出,资源分配和回收以及线程的切换。由于当时硬件并没有线程寄存器之类的冬冬来支持多线程,因此线程的切换性能和低下,并且需要引入复杂的机制在进程的栈中为各个线程划分出各自的栈数据所在位置,并且在切换时进行栈数据拷贝。而最大的问题是内核中缺乏对线程间的同步机制的支持,因此pthread库不得不在底层依靠信号方式来实现同步,因此线程互斥中

Linux下多线程编程-Pthread与Semaphore的使用

简单的多线程编程 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.

多线程程序设计 for Linux

声明:本文是网上整理的资料,版权属其作者本人所 有。 第一章线程基础知识 一.什么是线程在一个程序里的多个执行路线就叫做线程。更准确的定义是:线程是“一个 进程内部的一个控制序列”。 典型的unix进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程以后,在程序设计时可以把进程设计成在同一时刻能够做不止一件事,每个线程处理各只独立的任务。 二.线程的优点 (1)通过为每种事件类型的处理分配单独的线程,能够简化处理异步时间的代码。 (2)多个线程可以自动共享相同的存储地址空间和文件描述符。 (3)有些问题可以通过将其分解从而改善整个程序的吞吐量。 (4)交互的程序可以通过使用多线程实现相应时间的改善,多线程可以把程序中处理用户输入输出的部分与其它部分分开。 三.线程的缺点线程也有不足之处。编写多线程程序需要更全面更深入的思考。 在一个多线 程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的。调试一个多线程程序也比调试一个单线程程序困难得多四.线程的结构线程包含了表示进程内执行环境必需的信息,其中包括进程中标识线程的 线程ID,一组寄存器值、栈、调度优先级和策略、信号屏蔽子,errno变量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本,程序的全局内存和堆内存、栈以及文件描述符。 五.线程标识 就像每个进程有一个进程ID一样,每个线程也有一个线程ID,进程ID在 整个系统中是唯一的,但线程不同,线程ID只在它所属的进程环境中有效。线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t 数据类型,所以可以移植的操作系统不能把它作为整数处理。因此必须使用函数来对来对两个线程ID进行比较。 1.

多线程同步的三种方式 Linux

多线程同步的三种?方式: Linux 线程同步有最常?用的是:互斥锁、条件变量量和信号量量。?一、通过锁机制实现线程间的同步。初始化锁。在Linux下,线程的互斥量量数据类型是pthread_mutex_t。在使?用前,要对它进?行行初始化。静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 动态分配:int pthread_mutex_init(pthread_mutex_t mutex, const pthreadmutexattr_t mutexattr); 加锁。对共享资源的访问,要对互斥量量进?行行加锁,如果互斥量量已经上了了锁,调?用线程会阻塞,直到互斥量量被解锁。 int pthread_mutex_lock(pthread_mutex mutex); int pthread_mutex_trylock(pthread_mutex_t mutex);解锁。在完成了了对共享资源的访问后,要对互斥量量进?行行解锁。 int pthread_mutex_unlock(pthread_mutex_t mutex);销毁锁。锁在是使?用完成后,需要进?行行销毁以释放资源。 int pthread_mutex_destroy(pthread_mutex mutex); ?二、条件变量量(cond) 与互斥锁不不同,条件变量量是?用来等待?而不不是?用来上锁的。条件变量量?用来?自动阻塞?一个线程,直到某特殊情况发?生为?止。通常条件变量量和互斥锁同时使?用。条件变量量分为两部分: 条件和变量量。条件本身是由互斥量量保护的。线程在改变条件状态前先要锁住互斥量量。条件变量量使我们可以睡眠等待某种条件出现。条件变量量是利利?用线程间共享的全局变量量进?行行同步的?一种机制,主要包括两个动作:?一个线程等待"条件变量量的条件成?立"?而挂起;另?一个线程使"条件成?立"(给出条件成?立信号)。条件的检测是在互斥锁的保护下进?行行的。如果?一个条件为假,?一个线程?自动阻塞,并释放等待状态改变的互斥锁。如果另?一个线程改变了了条件,它发信号给关联的条件变量量,唤醒?一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量量可以被?用来实现这两进程间的线程同步。初始化条件变量量。静态态初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER; 动态初始化,int pthread_cond_init(pthread_cond_t cond, pthreadcondattrt cond_attr); 等待条件成?立。释放锁,同时阻塞等待条件变量量为真才?行行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有?一个线程wait) int pthread_cond_wait(pthread_cond_t cond, pthreadmutext mutex); int pthread_cond_timewait(pthread_cond_t cond,pthread_mutex mutex,const timespec abstime);激活条件变量量。 pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程) int pthread_cond_signal(pthread_cond_t cond); int

相关文档 最新文档