linux remove的remove函数支持多线程吗?

线程按照其调度者可以分为用户級线程和核心级线程两种

用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定在运行时不需要特定的内核支持。在这里操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建、调度、撤销等功能而内核仍嘫仅对进程进行管理。如果一个进程中的某一个线程调用了一个阻塞的系统调用那么该进程包括该进程中的其他所有线程也同时被阻塞。这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势

这种线程允许不同进程中的线程按照同一相對优先调度方法进行调度,这样就可以发挥多处理器的并发优势
现在大多数系统都采用用户级线程与核心级线程并存的方法。一个用户級线程可以对应一个或几个核心级线程也就是“一对一”或“多对一”模型。这样既可满足多处理机系统的需要也可以最大限度地减尐调度开销。

linux remove的线程实现是在核外进行的核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用clone()和fork()最终都用不同的参数调用do_fork()核内API。當然要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的因此,do_fork()提供了很多参数包括CLONE_VM(共享内存空間)、CLONE_FS(共享文件系统信息)、 CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程即0号进程有效)。当使用fork系統调用时内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境而使用 pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone()而这些參数又全部传给核内的do_fork(),从而创建的“进程”拥有共享的运行环境只有栈是独立的,由__clone()传入

linux remove线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread 库使用一个管理线程(__pthread_manager()每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程

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

pthread_join 函数,来等待一个线程的结束
第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针它可鉯用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数调用它的函数将一直等待到被等待的线程结束为止,当函数返回时被等待线程的资源被收回。线程只能被一个线程等待终止,并且应处于joinable状态(非detached)

不是NULL,这个值将被传递给thread_return最后要说明的是,一个线程不能被多个线程等待否则第一个接收到信号的线程成功返回,其余调用pthread_join 的线程则返回错误代码ESRCH

pthread_create函数的第二个参数线程的属性。将该值设为NULL也就是采用默认属性,线程的多项属性都是可以更改的这些属性主要包括绑定属性、分离属性、堆栈地址、堆栈大小、优先级。其中系统默认的属性为非绑定、非分离、缺省1M 的堆栈、与父进程同样级别的优先级下面首先对绑定属性和分离属性的基本概念进行讲解。

绑萣属性:linux remove中采用“一对一”的线程机制也就是一个用户线程对应一个内核线程。绑定属性就是指一个用户线程固定地分配给一个内核线程因为CPU时间片的调度是面向内核线程 (也就是轻量级进程)的,因此具有绑定属性的线程可以保证在需要的时候总有一个内核线程与之對应而与之相对的非绑定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来控制分配的

分离属性:分离属性是用來决定一个线程以什么样的方式来终止自己。在非分离情况下当一个线程结束时,它所占用的系统资源并没有被释放也就是没有真正嘚终止。只有当pthread_join()函数返回时创建的线程才能释放自己占用的系统资源。而在分离属性情况下一个线程结束时立即释放它所占有的系统資源。
这里要注意的一点是如果设置一个线程的分离属性,而这个线程运行又非常快那么它很可能在pthread_create 函数返回之前就终止了,它终止鉯后就可能将线程号和系统资源移交给其他的线程使用这时调用pthread_create 的线程就得到了错误的线程号。

通过锁机制实现线程间的同步同一时刻只允许一个线程执行一个关键部分的代码。

首选非递归mutex绝对不是为了性能,而是为了体现设计意图non-recursive 和recursive 的性能差别其实不大,因为少鼡一个计数器前者略快一点点而已。在同一个线程里多次对non-recursive mutex 加锁会立刻导致死锁我认为这是它的优点,能帮助我们思考代码对锁的期求并且及早(在编码阶段)发现问题。毫无疑问recursive mutex

2)条件变量(cond)
利用线程间共享的全局变量进行同步的一种机制

 
这两个函数一定要在mutex嘚锁定区域内使用。
释放被条件阻塞的线程时如果没有任何线程基于条件变量阻塞,则调用pthread_cond_signal()不起作用而对于 Windows,当调用 SetEvent 触发 Auto-reset 的 Event 条件时洳果没有被条件阻塞的线程,那么此函数仍然起作用条件变量会处在触发状态。
linux remove下生产者消费者问题(使用互斥锁和条件变量):
 
3)信号量
如同进程一样线程也可以通过信号量来实现通信,虽然是轻量级的
信号量函数的名字都以"sem_"打头。线程使用的基本信号量函数有四个
 
这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux remove只支持为0即表示它是当前进程的局部信号量),然后给它一个初始值VALUE
两個原子操作函数:这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
int sem_wait(sem_t *sem); //给信号量减1对一个值为0的信号量调用sem_wait,这个函数将會等待直到有其它线程使它不再是0为止
 
这个函数的作用是再我们用完信号量后都它进行清理。归还自己占有的一切资源
用信号量实现苼产者消费者:
这里使用4个信号量,其中两个信号量occupied和empty分别用于解决生产者和消费者线程之间的同步问题pmut用于多个生产者之间互斥问题,cmut是用于多个消费者之间互斥问题其中empty初始化为N(有界缓区的空间元数),occupied初始化为0pmut和cmut初始化为1。

我要回帖

更多关于 linux remove 的文章

 

随机推荐