三种专门用于线程同步的机制:POSIX信号量,互斥量和条件变量.
在Linux上信号量API有两组,一组是System V IPC信号量,即PV操作,另外就是POSIX信号量,POSIX信号量的名字都是以sem_开头.
phshared参数指定信号量的类型,若其值为0,就表示这个信号量是当前进程的局部信号量,否则该信号量可以在多个进程之间共享.value值指定信号量的初始值,一般与下面的sem_wait函数相对应.
其中比较重要的函数sem_wait函数会以原子操作的方式将信号量的值减一,如果信号量的值为零,则sem_wait将会阻塞,信号量的值可以在sem_init函数中的value初始化;sem_trywait函数是sem_wait的非阻塞版本;sem_post函数将以原子的操作对信号量加一,当信号量的值大于0时,其他正在调用sem_wait等待信号量的线程将被唤醒.
这些函数成功时返回0,失败则返回-1并设置errno.
生产者消费者模型:
生产者对应一个信号量:sem_t procer;
消费者对应一个信号量:sem_t customer;
sem_init(&procer,2)----生产者拥有资源,可以工作;
sem_init(&customer,0)----消费者没有资源,阻塞;
在访问公共资源前对互斥量设置(加锁),确保同一时间只有一个线程访问数据,在访问完成后再释放(解锁)互斥量.
互斥锁的运行方式:串行访问共享资源;
信号量的运行方式:并行访问共享资源;
互斥量用pthread_mutex_t数据类型表示,在使用互斥量之前,必须使用pthread_mutex_init函数对它进行初始化,注意,使用完毕后需调用pthread_mutex_destroy.
pthread_mutex_init用于初始化互斥锁,mutexattr用于指定互斥锁的属性,若为NULL,则表示默认属性。除了用这个函数初始化互斥所外,还可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用于销毁互斥锁,以释放占用的内核资源,销毁一个已经加锁的互斥锁将导致不可预期的后果。
pthread_mutex_lock以原子操作给一个互斥锁加锁。如果目标互斥锁已经被加锁,则pthread_mutex_lock则被阻塞,直到该互斥锁占有者把它给解锁.
pthread_mutex_trylock和pthread_mutex_lock类似,不过它始终立即返回,而不论被操作的互斥锁是否加锁,是pthread_mutex_lock的非阻塞版本.当目标互斥锁未被加锁时,pthread_mutex_trylock进行加锁操作;否则将返回EBUSY错误码。注意:这里讨论的pthread_mutex_lock和pthread_mutex_trylock是针对普通锁而言的,对于其他类型的锁,这两个加锁函数会有不同的行为.
pthread_mutex_unlock以原子操作方式给一个互斥锁进行解锁操作。如果此时有其他线程正在等待这个互斥锁,则这些线程中的一个将获得它.
三个打印机轮流打印:
输出结果:
如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量就是用于在线程之间同步共享数据的值.条件变量提供了一种线程之间通信的机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.
条件变量会在条件不满足的情况下阻塞线程.且条件变量和互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生.
其中pthread_cond_broadcast函数以广播的形式唤醒所有等待目标条件变量的线程,pthread_cond_signal函数用于唤醒一个等待目标条件变量线程.但有时候我们可能需要唤醒一个固定的线程,可以通过间接的方法实现:定义一个能够唯一标识目标线程的全局变量,在唤醒等待条件变量的线程前先设置该变量为目标线程,然后采用广播的方式唤醒所有等待的线程,这些线程被唤醒之后都检查该变量以判断是否是自己.
采用条件变量+互斥锁实现生产者消费者模型:
运行结果:
阻塞队列+生产者消费者
运行结果:
‘贰’ linux 线程中,线程宿主函数是什么意思宿体函数又是什么意思二者有什么区别最好能举个例子。
宿主函数是你调用建立线程的函数,而宿体函数是你线程运行起来后执行的函数
‘叁’ Linux的C编程线程问题
这么多内容,没时间御没写大贺,给你点资料,自己去看了写滚拆派吧:
http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/index.html
http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/index.html
http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part3/index.html
http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/index.html
http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part5/index.html
‘肆’ 嵌入式与Linux(五):Linux线程
姓名:王央京 学号:18050100052 学院:电子工程学院
转自:https://blog.csdn.net/qq_22847457/article/details/89371217
【嵌牛导读】本文介绍了Linux线程的相关信息
【嵌牛鼻子】Linux线程
【嵌牛提问】在了解Linux系统后,能否具体介绍线程的概念?
【嵌牛正文】
类Unix系统中,早期是没有“线程”概念的,80年代才引答握态入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。一个进程可以有多个线程,这个进程本身也叫做线程只不过是主线程。通常主线程分配任务给子线程做。程序设计时候就可以某一时刻不止做一件事情,每一个线程处理各自独立的任务。
多个线程可以访问相同的存储地址空间和文件描述符。同一进程内的线程共享以下数据:全局内存、进程指令、打开的文件、信号处理函数和信号处置、当前工作目录、用户ID和用户组ID、大多数数据。每个线程有各自的线程ID、寄存器集合(包括程序计数器和栈指针)、栈、errono、信号掩码、优先级。
线程的优点有提高程序并发性、开销小和数据通信、共享数据方便等。线程的缺点有库函数不稳定、调试编写困难、gdb不支持、对信号支持不好等。除此之外,多线程内如果其中一个线程出现了 除0、野指针 等问题会造成该线程崩溃,进而导致整个进程终止。同时,线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。
从上述分析来看,线程的优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。
线程有一套完整的与其有关的函数库调用,它们中的绝大多数函数名都以pthread_开头。为了使用这些函数库调用,我们必须定义宏_REENTRANT,在程序中包含头文件pthread.h,并且在编译程序皮哗时需要用选项-lpthread来链接线程库。其中常用的函数库如下:
1. pthread_self函数获取线程ID,其作用对应进程中getpid()函数。
2. pthread_create函数创建一个新线程,其作用对应进程中fork()函数。
3. pthread_exit函数将单个线程退出,其作用对应进程中exit()函数
4. pthread_join函数阻塞等待线程退出,获取线程退出状态其作用,对清源应进程中waitpid()函数。
5. pthread_cancel函数杀死(取消)线程其作用,对应进程中kill()函数。
6. pthread_detach函数实现线程分离。
‘伍’ Linux多线程程序中有哪些变量类型,被映射到哪个地址空间,有几个运行实例
在 Linux 多线程编程中,通常会使用以下几种变量类型:
全局变量:定义在所有函数之外的变量,作用域在整个程序中都可见。全局变量被映射到进程的数据段中,所有线程都可以访问它们。在多线程程序中,需要注意全局变量的并发访问问题,避免出现竞争条件。
局部变量:定义在函数内部的变量,作用域仅限于函数内部。每个线程都有自己的栈空间,亏祥局部变量被分配在栈上,每个线程都有自己独立的栈空间,互不干扰。
线程私有变量:每个线程都有自己的私有变量。可以使用 pthread_key_create() 函数创建一个线程私有变量,使用 pthread_getspecific() 和 pthread_setspecific() 函数来设置和获取线程私有变量的值。线程私有变量被映射到进程的线程局部存储段(Thread Local Storage, TLS)中,每个线程都有自己独立的 TLS,互不干扰。
共享变量:被多个线程共享的变量。在多线程程序中,需要使用锁(如互斥锁、读写锁)等机制来保护共享变量,避免出现竞争条件橡铅。共享变量被映射到进程的数据段中,所有线程都可以访问它们。
需要注意的是,在多线程程序中,这些变量类型在地址空间中的位置和数量都是相对复杂的,因为每个线程都有自己独梁空好立的栈空间和 TLS,这些变量的地址在不同的线程中可能是不同的。因此,在多线程程序中,需要使用适当的同步机制来保护这些变量,以确保程序的正确性和可靠性。
‘陆’ linux内核创建内核线程有哪些方法
1.头文件
#include <linux/sched.h> //wake_up_process()
#include <linux/kthread.h> //kthread_create()、kthread_run()
#include <err.h> //IS_ERR()、PTR_ERR()
2.实现
2.1创建线程
在模块初始化时,可以进行线程的创建。使用下面的函数和宏定义:
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);
#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; \
})
例如:
static struct task_struct *test_task;
static int test_init_mole(void)
{
int err;
test_task = kthread_create(test_thread, NULL, "test_task");
if(IS_ERR(test_task)){
printk("Unable to start kernel thread. ");
err = PTR_ERR(test_task);
test_task = NULL;
return err;
}
wake_up_process(test_task);
return 0;
}
mole_init(test_init_mole);
2.2线程函数
在线程函数里,完成所需的业务逻辑工作。主要框架如下所示:
int threadfunc(void *data){
…
while(1){
set_current_state(TASK_UNINTERRUPTIBLE);
if(kthread_should_stop()) break;
if(){//条件为真
//进行业务处理
}
else{//条件为假
//让出CPU运行其他线程,并在指定的时间内重新被调度
schele_timeout(HZ);
}
}
…
return 0;
}
2.3结束线程
在模块卸载时,可以结束线程的运行。使用下面的函数:
int kthread_stop(struct task_struct *k);
例如:
static void test_cleanup_mole(void)
{
if(test_task){
kthread_stop(test_task);
test_task = NULL;
}
}
mole_exit(test_cleanup_mole);
3.注意事项
(1) 在调用kthread_stop函数时,线程函数不能已经运行结束。否则,kthread_stop函数会一直进行等待。
(2) 线程函数必须能让出CPU,以便能运行其他线程。同时线程函数也必须能重新被调度运行。在例子程序中,这是通过schele_timeout()函数完成的。
4.性能测试
可以使用top命令来查看线程(包括内核线程)的CPU利用率。命令如下:
top –p 线程号
可以使用下面命令来查找线程号:
ps aux|grep 线程名
可以用下面的命令显示所有内核线程:
ps afx
注:线程名由kthread_create函数的第三个参数指定
在分析usb_hub_init()的代码的时候,忽略掉了一部份.
代码片段如下所示:
int usb_hub_init(void)
{
……
khubd_task = kthread_run(hub_thread, NULL, "khubd");
……
}
Kthread_run() 是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就是后面跟的第一个参数.在这里,也就是hub_thread().另外,顺带 提一句,要终止kthread_run()创建的线程,可以调用kthread_stop().
‘柒’ linux下C语中用到的线程编程函数
[desktop:~]$ man pthread_mutex
pthread_mutexattr_destroy pthread_mutexattr_settype
pthread_mutexattr_getprioceiling pthread_mutex_destroy
pthread_mutexattr_getprotocol pthread_mutex_getprioceiling
pthread_mutexattr_getpshared pthread_mutex_init
pthread_mutexattr_gettype pthread_mutex_lock
pthread_mutexattr_init pthread_mutex_setprioceiling
pthread_mutexattr_setprioceiling pthread_mutex_timedlock
pthread_mutexattr_setprotocol pthread_mutex_trylock
pthread_mutexattr_setpshared pthread_mutex_unlock
用manpages一下都能查出来。
‘捌’ linux线程pthread_cleanuo_push 函数问题
pthread_cleanup_push来注册清理函数rtn,这个函数有一个参数arg。在以下三种情形之一发生时,注册的清理函数被执行:
1)调用pthread_exit。
2)作为对取消线程请求(pthread_cancel)的响应。
3)以非0参数调用pthread_cleanup_pop。
注意:
1)如果线程只是由于简单的返回而终止的,则清除函数不会被调用。
2)如果pthread_cleanup_pop被传递0参数,则清除函数不会被调用,但是会清除处于栈顶的清理函数。
‘玖’ 用Linux,pthread相关函数创建一个调度策略为NORMAL,priority=15的线程,
NORMAL的调度策略是什么?
创建一个线程很简单,用pthread_create就可以做到,郑橘具体使用方法你man下或者搜索都有。
需要提到的是你要改变调度策略和优先氏丛嫌级,我查了下歼手,用sched_setscheler就可以做到了:
int sched_setscheler(pid_t pid, int policy, const struct sched_param *param);
policy是设置调度策略,param是设置优先级。
‘拾’ Linux的线程清理函数
第一个问题,出现两个pthread_cleanup_push吗,因为每次只态宴能增加一个退出时处州闭明理的函数,第二个参数为NULL,表示调用处理函数时,函数没有参数
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
你给他们的参数为0,所以清除函数不会被册告调用。