Linux开发(七):多线程通信与同步

2023-11-09

线程间无需特别的手段进行通信,因为线程间可以共享数据结构,也就是一个全局变量可以被两个线程同时使用。 不过要注意的是线程间需要做好同步。

目录

一、互斥锁

1、初始化

(1)动态初始化

(2)静态初始化 

2、加锁

3、解锁

4、销毁互斥锁

5、互斥锁属性

(1) 初始化属性

(2)销毁属性

(3)获取/设置互斥锁的共享属性

(4)获取/设置互斥锁的范围

(5)设置/获取互斥锁的类型

二、条件变量

1、初始化

(1)动态初始化

(2)静态初始化 

2、等待条件变量

3、唤醒条件变量

4、销毁条件变量

5、条件变量属性

6、条件变量与互斥锁使用示例

三、读写锁

 1、初始化

(1)动态初始化

(2)静态初始化 

2、读上锁

3、写上锁

4、释放读写锁 

5、销毁读写锁

5、读写锁属性

四、自旋锁

1、初始化

2、加锁

3、解锁

4、销毁 

五、屏障 

1、初始化

2、等待其他线程

3、销毁

4、屏障使用示例

六、信号量

1、初始化

2、等待信号量

3、发送信号量

4、销毁信号量


一、互斥锁

互斥锁也叫做互斥量,是一个二元变量,主要以排他的方式防止数据被并发访问。在访问共享资源前对互斥锁进行加锁,访问后释放互斥锁。若互斥锁已经被某线程加锁,其他试图再次对互斥锁加锁的线程都将会被阻塞直到当前线程释放该互斥锁。

如果释放互斥锁时有一个以上的线程等待加锁,那么这些线程都将会变成可运行状态,但只有一个变线程可以对互斥量加锁,其他线程抢占失败,只能再次阻塞等待它重新变成可用,这样,就可以保证一次只有一个线程可以向前执行。

1、初始化

初始化一个互斥锁(互斥量)mutex。

(1)动态初始化

#include <pthread.h>
/*动态初始化互斥锁*/
int pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t* attr);

函数参数:

mutex:传出参数,调用时应传 &mutex给该函数

attr:传入参数,互斥量属性。为NULL,则表示使用默认属性

返回值:

成功返回0,失败返回-1

(2)静态初始化 

/*静态态初始化互斥锁,mutex必须为全局变量*/
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALZER;

静态初始化时,mutex必须为全局变量,有三种类型可供选择:

  • PTHREAD_MUTEX_INITIALIZER ---创建快速互斥锁,默认类型
  • PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP---创建递归互斥锁,允许同一线程循环给互斥量上锁
  • PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP---创建检错互斥锁,如果该互斥量已经被上锁,那么后续的上锁将会失败而不会阻塞。

2、加锁

对已经完成初始化的互斥锁尝试加锁操作,使用pthread_mutex_lock函数对共享资源进行加锁时,如果加锁不成功,则线程就阻塞;而如果使用pthread_mutex_trylock,加锁不成功时不会阻塞,而是立即返回EBUSY错误。

#include <pthread.h>
/*以阻塞方式上锁*/
int pthread_mutex_lock(pthread_mutex_t* mutex);

/*以非阻塞方式上锁*/
int pthread_mutex_trylock(pthread_mutex_t* mutex);

函数参数:

mutex:已经初始化的互斥锁

返回值:

成功返回0,失败返回-1

3、解锁

对共享资源解锁,在解锁的同时,会将阻塞在该锁上的所有线程全部唤醒,至于哪个线程先被唤醒,取决于优先级、调度策略。默认情况下:先阻塞的线程会先被唤醒。解锁操作只能由占用该互斥锁的线程完成。

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t* mutex);

函数参数:

mutex:已经初始化的互斥锁

返回值:

成功返回0,失败返回-1

4、销毁互斥锁

销毁一个互斥锁,并释放资源

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t* mutex);

函数参数:

mutex:已经初始化的互斥锁

返回值:

成功返回0,失败返回-1

5、互斥锁属性

(1) 初始化属性

将互斥锁的属性初始化为缺省值,在执行过程中,线程系统会为每个属性对象分配存储空间。

// 初始化属性
int pthread_mutexattr_init(pthread_mutexattr_t *attr);

函数参数:

attr:待初始化的互斥锁属性

返回值:

成功返回0,失败返回-1

(2)销毁属性

销毁互斥锁属性对象,只要执行过初始化的属性对象,必须成对执行销毁操作,否则将会导致内存泄漏。

// 销毁属性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);

函数参数:

attr:待销毁的互斥锁属性

返回值:

成功返回0,失败返回-1

(3)获取/设置互斥锁的共享属性

获取/设置互斥锁的共享属性

// 获取/设置互斥锁的共享属性
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);

函数参数:

attr:待操作的互斥锁属性对象

pshared:互斥锁的共享属性,有以下两种取值类型:

  • PTHREAD_PROCESS_PRIVATE(默认):由同一个进程创建的线程才能够处理该互斥锁
  • PTHREAD_PROCESS_SHARED:多个进程中的线程之间共享互斥锁

返回值:

成功返回0,失败返回-1

(4)获取/设置互斥锁的范围

当持有互斥量的进程终止时,不同属性将有不同的响应。

// 获取/设置持有互斥锁的进程终止时的处理
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robust);
int pthread_mutexattr_setrobust(pthread_mutexattr_t *attr,int robust);

函数参数:

attr:待操作的互斥锁属性对象

robust:互斥锁属性的健壮性,取值有以下两种:

  • PTHREAD_MUTEX_STALLED(默认):意味着持有互斥量的进程终止时不会有特别的动作
  • PTHREAD_MUTEX_ROBUST:在进程终止后,等待中的线程如果调用了pthread_mutex_lock,会从阻塞中返回EOWNERREAD,而不是0.

返回值:

成功返回0,失败返回-1

(5)设置/获取互斥锁的类型

销毁互斥锁属性对象,只要执行过初始化的属性对象,必须成对执行销毁操作,否则将会导致内存泄漏。

// 获取/设置互斥锁的类型
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);

函数参数:

attr:待销毁的互斥锁属性

type:互斥锁的类型,有以下四种类型选择:

  • PTHREAD_MUTEX_NORMAL ---标准互斥量类型,不做特殊的错误检查和死锁检测。如果线程在不首先解除互斥锁的情况下,尝试重新锁定该互斥锁,则会产生死锁;尝试解除由其他线程锁定的互斥锁会产生不确定的行为;如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为。
  • PTHREAD_MUTEX_ERRORCHECK ---此类型的互斥锁可提供错误检查。如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则会返回错误。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
  • PTHREAD_MUTEX_RECURSIVE ---递归类型,此种类型允许同一线程在互斥量解锁之前对该互斥量进行多次加锁。递归互斥量维护锁的计数,在解锁次数与加锁次数不同的情况下,不会释放锁。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。 如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
  • PTHREAD_MUTEX_DEFAULT---此类型提供默认特性,操作系统实现时会把它映射到上面三种中的一种上。linux系统映射到第一种标准类型上。

返回值:

成功返回0,失败返回-1

二、条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,始终与互斥锁一起使用:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。
条件变量可以以原子方式阻塞线程,直到某个特定条件为真,如果条件为假,线程通常会基于条件变量阻塞,并以原子方式释放等待条件变化的互斥锁。如果另一个线程更改了条件,该线程可能会向相关的条件变量发出信号,从而使一个或多个等待的线程执行以下操作:

  1. 唤醒
  2. 再次获取互斥锁
  3. 重新评估条件

1、初始化

初始化一个条件变量。

(1)动态初始化

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

函数参数:

cond:要初始化的条件变量

attr:条件变量属性,通常传NULL,表示使用默认属性

返回值:

成功返回0,失败返回非0值

(2)静态初始化 

#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

静态初始化时,cond必须为全局变量

2、等待条件变量

阻塞等待一个条件变量,具体而言有以下两个动作:

  • 阻塞等待条件变量cond满足;
  • 解锁互斥锁mutex,相当于pthread_mutex_unlock(&mutex);

以上两个动作被合并为一个原子操作,这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化。

当函数接收到“条件成立”的信号后,它并不会立即结束对线程的阻塞,而是先完成对互斥锁的“加锁”操作,然后才解除阻塞。

#include <pthread.h>
/*阻塞等待某个条件变量*/
int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex);

/*超时等待某个条件变量*/
int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex,struct timespec* abstime);

 函数参数:

cond:已初始化的条件变量

mutex:与条件变量配合使用的互斥锁

abstime:超时等待的绝对时间,结构体为:

struct timespec {
    time_t tv_sec; /* 秒 */
    long tv_nsec; /* 纳秒*/ 
}

返回值:

成功返回0,失败返回非0值

3、唤醒条件变量

唤醒至少一个阻塞在条件变量上的线程,以下两个函数都能解除线程的“阻塞”状态,区别在于:

  • pthread_cond_signal() 函数至少解除一个线程的“阻塞”状态,如果等待队列中包含多个线程,优先解除哪个线程将由操作系统的线程调度程序决定;
  • pthread_cond_broadcast() 函数可以解除等待队列中所有线程的“阻塞”状态。

由于互斥锁的存在,解除阻塞后的线程也不一定能立即执行。当互斥锁处于“加锁”状态时,解除阻塞状态的所有线程会组成等待互斥锁资源的队列,等待互斥锁“解锁”。

#include <pthread.h>
/*通知等待的第一个线程*/
int pthread_cond_signal(pthread_cond_t* cond);

/*通知等待的所有线程*/
int pthread_cond_broadcast(pthread_cond_t* cond);

函数参数:

cond:已初始化的条件变量

返回值:

成功返回0,失败返回非0值

4、销毁条件变量

销毁一个条件变量,值得注意的是销毁后的条件变量还可以调用 pthread_cond_init() 函数重新初始化后使用。

int pthread_cond_destroy(pthread_cond_t *cond);

函数参数:

cond:已初始化的条件变量

返回值: 

成功返回0,失败返回非0值

5、条件变量属性

 条件变量的属性结构体为:pthread_condattr_t,内容和互斥锁相差无几,如果需要精细化管理,请自行百度,这里就不在累述。

6、条件变量与互斥锁使用示例

#include  <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int pro = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;

void init()
{
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);
}
void* producer(void* date)
{
	while(1)
	{
		pthread_mutex_lock(&mutex);
		pro++;
		printf("生产者生产了1个,现在共%d个\n",pro);
		if(pro == 3)
		{
			pthread_cond_signal(&cond);			
		}

		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
	
}

void* contomer(void* date)
{
	
	while(1)
	{
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		pro -= 3;
		printf("消费者吃了3个\n");
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
	
}
int main()
{
	void* ret;
	pthread_t tid1,tid2;
	
	init();
	pthread_create(&tid1,NULL,producer,0);
	pthread_create(&tid2,NULL,contomer,0);
	
	pthread_join(tid1,&ret);
	pthread_join(tid2,&ret);
	return 0;
}

输出为:

这里写图片描述

从结果可以看出,当生产者生产了3个产品后,立即通知消费者来消费,紧接着,生产者继续生产,无线循环下去。里面需要注意的是消费者在调用pthread_cond_wait()后进入等待状态时,会把互斥锁解锁,并阻塞等待加锁后,才会继续消费。 

三、读写锁

读写锁的操作与互斥锁基本相同,但占用资源相对于互斥锁要少很多,操作逻辑为:

当前读写锁的状态 线程发出“读”请求 线程发出“写”请求
无锁 允许占用 允许占用
读锁 允许占用 阻塞线程执行
写锁 阻塞线程执行 阻塞线程执行

 1、初始化

初始化一个读写锁。

(1)动态初始化

#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t*rwlock, const pthread_rwlockattr_t *attr);

函数参数:

rwlock:传出参数,要初始化的读写锁

attr:读写锁属性,通常传NULL,表示使用默认属性

返回值:

成功返回0,失败返回非0值

(2)静态初始化 

#include <pthread.h>
pthread_rwlock_t myRWLock = PTHREAD_RWLOCK_INITIALIZER;

静态初始化时,myRWLock必须为全局变量

2、读上锁

以读方式请求读写锁,当读写锁处于“无锁”或者“读锁”状态时,以上两个函数都能成功获得读锁;当读写锁处于“写锁”状态时:

  • pthread_rwlock_rdlock() 函数会阻塞当前线程,直至读写锁被释放;
  • pthread_rwlock_tryrdlock() 函数不会阻塞当前线程,直接返回 EBUSY
  • pthread_rwlock_timerdlock函数最多阻塞abstime.tv_sec秒,如果没有获取到则会直接返回错误
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_timerdlock(pthread_rwlock_t *rwlock,const struct timespec * abstime);

函数参数:

rwlock:已初始化的读写锁

 abstime:超时等待的绝对时间,结构体为:

struct timespec {
    time_t tv_sec; /* 秒 */
    long tv_nsec; /* 纳秒*/ 
}

返回值:

成功返回0,失败返回非0值

3、写上锁

以写方式请求读写锁,当读写锁处于“无锁”状态时,三个函数都能成功获得写锁;当读写锁处于“读锁”或“写锁”状态时:

  • pthread_rwlock_wrlock() 函数将阻塞线程,直至读写锁被释放;
  • pthread_rwlock_trywrlock() 函数不会阻塞线程,直接返回 EBUSY。
  • pthread_rwlock_timewrlock函数最多阻塞abstime.tv_sec秒,如果没有获取到则会直接返回错误
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock); 
int pthread_rwlock_timewrlock(pthread_rwlock_t *rwlock,const struct timespec * abstime)

函数参数:

rwlock:已初始化的读写锁

abstime:超时等待的绝对时间,结构体为:

struct timespec {
    time_t tv_sec; /* 秒 */
    long tv_nsec; /* 纳秒*/ 
}

返回值:

成功返回0,失败返回非0值

4、释放读写锁 

无论是处于“无锁”、“读锁”还是“写锁”的读写锁,都可以使用如下函数释放读写锁:

int pthread_rwlock_unlock (pthread_rwlock_t* rwlock);

函数参数:

rwlock:要释放的读写锁

返回值:

成功返回0,失败返回非0值

5、销毁读写锁

当读写锁不再使用时,记得要销毁。

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);

函数参数:

rwlock:要销毁的读写锁

返回值:

成功返回0,失败返回非0值

5、读写锁属性

 条件变量的属性结构体为:pthread_rwlockattr_t,内容和互斥锁相差无几,如果需要精细化管理,请自行百度,这里就不在累述。

四、自旋锁

自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

简单来说:自旋锁如果发现要使用的资源被占用就会一直查询这个资源使用的状态直到这个资源被其他线程释放。

从这里我们可以看到自旋锁的一个缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。

所以自旋锁适用于短时期的轻量级加锁。

1、初始化

初始化自旋锁。

int pthread_spin_init(pthread_spinlock_t *lock, int pshared);

函数参数:

lock:要初始化的自旋锁

pshared:设置自旋锁的共享属性,有以下两个值可供选择:

  • PTHREAD_PROCESS_PRIVATE):由同一个进程创建的线程才能够处理该锁
  • PTHREAD_PROCESS_SHARED:多个进程中的线程之间共享该锁

返回值:

成功返回0,失败返回非0值

2、加锁

对自旋锁尝试加锁:

  • pthread_spin_lock() 函数将阻塞线程,直至锁被释放;
  • pthread_spin_trylock() 函数不会阻塞线程,如果没有获取到就返回 0
int pthread_spin_lock ( pthread_spinlock_t *lock) ;
// 尝试获取指定的自旋锁,如果没有获取到就返回 0
int pthread_spin_trylock(pthread_spinlock_t *lock);

函数参数:

lock:要操作的自旋锁

返回值:

成功返回0,失败返回非0值 

3、解锁

对自旋锁解锁

int pthread_spin_unlock(pthread_spinlock_t *lock);

函数参数:

lock:要操作的自旋锁

返回值:

成功返回0,失败返回非0值

4、销毁 

将自旋锁销毁

int pthread_spin_destroy(pthread_spinlock_t *lock);

函数参数:

lock:要操作的自旋锁

返回值:

成功返回0,失败返回非0值

五、屏障 

屏障(barrier)是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有合作线程都到达某一点,然后从该点继续执行。

举一个简单的例子,要排序1亿个数字,我们假如使用4个线程去做,每个线程排序数据的1/4,等待4个线程全部完成排序后,主线程再做合并即可。

1、初始化

将屏障变量进行初始化。

int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t * attr, unsigned int count);

函数参数:

barrier:待初始化的屏障变量

att:屏障属性,为NULL,则表示使用默认属性

count:指定允许所有线程再wait后继续运行时,必须到达屏障的线程数目

返回值:

成功返回0,失败返回非0值

2、等待其他线程

调用pthread_barrier_wait的线程在屏障计数count未满足条件时,会进入休眠状态。如果该线程是最后一个调用pthread_barrier_wait的线程,即满足了屏障计数,所有的线程都被唤醒,继续执行。

int pthread_barrier_wait(pthread_barrier_t *barrier);

函数参数:

barrier:已初始化的屏障变量

返回值:

成功返回0,失败返回非0值

3、销毁

销毁一个屏障变量

int pthread_barrier_destory(pthread_barrier_t * barrier);

函数参数:

barrier:已初始化的屏障变量

返回值:

成功返回0,失败返回非0值

4、屏障使用示例

伪代码为:

#include <pthread.h>
// 屏障变量
static pthread_barrier_t barrier;
 
void* fun1(void *arg) {
    // 进行排序
    // 等待其他线程完成操作
    pthread_barrier_wait(&barrier);
}
 
void* fun2(void *arg) {
    // 进行排序
    // 等待其他线程完成操作
    pthread_barrier_wait(&barrier);
}

void* fun3(void *arg){
    // 进行排序
    // 等待其他线程完成操作
    pthread_barrier_wait(&barrier);
}

int main() {
    pthread_t thread1;
    pthread_t thread2;
    pthread_t thread3;
    const int thread_num = 4;
 
    //init
    pthread_barrier_init(&barrier, NULL, thread_num);
 
    pthread_create(&thread1, NULL, fun1, NULL);
    pthread_detach(thread1);

    pthread_create(&thread2, NULL, fun2, NULL);
    pthread_detach(thread2);

    pthread_create(&thread3, NULL, fun3, NULL);
    pthread_detach(thread3);
 
    // 主线程中也排序其中1/4任务 
     
    pthread_barrier_wait(&barrier);
    
    // 所有排序完成
    
    pthread_barrier_destroy(&barrier);
    return 0;
}

六、信号量

在学习信号量之前,我们必须先知道——Linux提供两种信号量:

  1. 内核信号量,由内核控制路径使用
  2. 用户态进程使用的信号量,这种信号量又分为:
    1. POSIX信号量:
      1. 有名信号量:其值保存在文件中, 所以它既可以用于线程间同步,也可以用于进程间的同步
      2. 无名信号量:其值保存在内存中,一般用在线程间同步。
    2. 系统V信号量:用于进程间同步

信号量其实也是一种锁,线程获取不到信号量的时候进入睡眠,直至有信号量释放出来时,才会被唤醒,进入临界区继续执行。信号量有二值信号量和计数信号量两种,其中二值信号量比较常用。
二值信号量表示信号量只有两个值,即0和1。信号量为1时,表示临界区可用,信号量为0时,表示临界区不可访问。

信号量API参考博客:https://blog.csdn.net/Chiang2018/article/details/123425121

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux开发(七):多线程通信与同步 的相关文章

  • 从 systemd bash 内联脚本创建 filename_$(date %Y-%m-%d)

    我正在尝试执行systemd计时器并希望将执行脚本的输出保存在每个日期的文件中 这是我的ExecStart脚本中的 service file ExecStart bin bash c echo date Y m d gt gt home u
  • vagrant ssh -c 并在连接关闭后保持后台进程运行

    我正在编写一个脚本来启动和后台流浪机器内的进程 似乎每次脚本结束和 ssh 会话结束时 后台进程也会结束 这是我正在运行的命令 vagrant ssh c cd vagrant src nohup python hello py gt he
  • 让 TeXstudio 在 linux mint 中工作:找不到文件“url.sty”。

    刚刚切换到 Linux Mint 以前的顽固 Windows 用户 我在尝试安装 TeXstudio 时遇到一些问题 Sudo apt get install texstudio 给了我一个正确的安装 至少 我是这么认为的 但是当我尝试构建
  • Web 应用程序的带宽和流量模拟器?

    您能否建议如何创建一个测试环境来模拟 Web 应用程序中的各种类型的带宽和流量 或者也许是一个针对本地主机执行此操作的开源程序 我认为在编写网络应用程序时这是一个非常重要的主题 但这不是一个常见的主题 我能想象创建这种环境的唯一方法是在本地
  • C:如果文件描述符被删除,阻塞读取应该返回

    我正在以阻塞的方式从设备 文件描述符中读取 可能会发生这样的情况 在不同的线程中 设备被关闭并且文件描述符被删除 不幸的是 读取没有返回或注意到并且一直阻塞 作为一种解决方法 我可以使用 select 作为超时来执行 while 循环 如果
  • PyPI 上的轮子平台约束有什么限制吗?

    是否有任何地方 PEP 或其他地方 声明关于 Linux 轮子上传范围的限制 PyPI http pypi io 应该有 具体来说 上传是否被认为是可接受的做法linux x86 64轮子到 PyPI 而不是manylinux1 x86 6
  • 如何将后台作业的输出分配给 bash 变量?

    我想在 bash 中运行后台作业并将其结果分配给一个变量 我不喜欢使用临时文件 并且希望同时运行多个类似的后台任务 root root var echo hello world root root echo var hello world
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 找出 Linux 上的默认语言

    有没有办法从C语言中找出Linux系统的默认语言 有 POSIX API 可以实现这个功能吗 例如 我想要一个人类可读格式的字符串 即德语系统上的 German 或 Deutsch 法语系统上的 French 或 Francais 等 有类
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • Fortran 中的共享库,最小示例不起作用

    我试图了解如何在 Linux 下的 Fortran 中动态创建和链接共享库 我有两个文件 第一个 liblol f90 看起来像这样 subroutine func print lol end subroutine func 我用它编译gf
  • 如何从 Linux 内核模块获取使用计数?

    我对正在开发的内核模块的使用计数有疑问 我想打印它以进行调试 如何从模块代码中获取它 有问题的内核版本 Linux 2 6 32 module refcount http lxr linux no linux v2 6 34 1 inclu
  • 使用 gcc 理解共享库

    我试图理解 C 中共享库的以下行为 机器一 cat one c include
  • 如何设置Java线程的CPU核心亲和力?

    我搜索了以前关于类似主题的帖子 但找不到合适的答案 因此提出这个问题 非常感谢您帮助回答 我知道在 Linux 中通过任务集命令设置进程与特定 CPU 核心的关联性 但我想设置 Java 线程与特定 cpu 核心的亲和力 以便属于同一进程的
  • 在 debian wheezy amd64 上安装 ia32-libs

    我正在使用 Debian 7 喘息 amd64 uname a Linux tzwm debian 3 2 0 4 amd64 1 SMP Debian 3 2 51 1 x86 64 GNU Linux 我想安装ia32 libs在我的
  • ubuntu 的 CSS 更少(并且自动编译)? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我尝试过 simples 但现在 l
  • 如何在 Linux/OS X 上温和地终止 Firefox 进程

    我正在使用 Firefox 进行一些自动化操作 尽管我可以从 shell 打开 Firefox 窗口 但我无法正确终止它 如果我kill火狐进程与kill 3 or kill 2当我下次打开新的 Firefox 窗口时 命令会询问我是否要在
  • 使用c在linux上分块读写

    我有一个 ASCII 文件 其中每一行都包含一个可变长度的记录 例如 Record 1 15 characters Record 2 200 characters Record 3 500 characters Record n X cha
  • 为什么docker容器提示“权限被拒绝”?

    我使用以下命令来运行 docker 容器 并从主机映射目录 root database 到容器 tmp install database docker run it name oracle install v root database t
  • 如何在文件夹中的 xml 文件中 grep 一个单词

    我知道我可以使用 grep 在这样的文件夹中的所有文件中查找单词 grep rn core 但我当前的目录有很多子目录 我只想搜索当前目录及其所有子目录中存在的所有 xml 文件 我怎样才能做到这一点 我试过这个 grep rn core

随机推荐

  • 我是废物...

    我是废物
  • 【数据手册】CH340G芯片使用介绍

    1 概述 CH340是一系列USB总线适配器 它通过USB总线提供串行 并行或IrDA接口 CH340G集成电路提供通用的MODEM信号 允许将UART添加到计算机上 或将现有的UART设备转换为USB接口 2 特征 全速USB接口 兼容U
  • Bugku-CTF (web 持续更新) ——新手ctf记录

    目录 1 滑稽 2 计算器 3 GET 4 POST GET和POST的区别 5 矛盾 6 alert 7 你必须让他停下 8 game1 9 网站被黑 10 本地管理员 X Forwarded For 11 eval 12 变量1 13
  • Linux下的bochs安装

    强烈建议使用ubuntu系统 apt get指令太好用了 安装各种依赖相当简单 1 首先到bochs网站上下载一个linux版本bochs 在安装之前需要安装一些依赖 sudo apt get install build essential
  • JavaScript原生实现事件监听及手动触发

    事件监听 标签中的onxxx 比如
  • AOM联盟:AV1完成1.0版定稿

    在2018年第一季度结束前 AOM联盟完成了AV1 1 0版定稿 这是这一新兴生态的最新胜利 文 包研 图 AOM官网发布消息 今天 AV1的规格 参考代码和绑定格式向开发者开放 来源 AOM官网 北京时间3月28日晚间消息 AOM联盟宣布
  • Docker存储卷(一)详解

    目录 什么是存储卷 为什么要用存储卷 Docker存储卷的特性 Docker为容器提供了两种存放数据的资源 storage driver Data Volume bind mount 实例 docker managed volume 实例
  • getevent/sendevent 使用说明

    这两天准备写一下input子系统的分析 过程中发现了两个好工具 呵呵 就是本文介绍的主角 getevent用于获取当前系统input设备的一些参数和实时事件的数据 sendevent用于发送input事件 这样在调试的时候遇到有的样机按键坏
  • Tensorflow分布式训练原理

    以下文章摘录自 机器学习观止 核心原理与实践 京东 https item jd com 13166960 html 当当 http product dangdang com 29218274 html 由于博客系统问题 部分公式 图片和格式
  • python中imread用法_Python matplotlib.pyplot.imread()用法及代码示例

    Matplotlib是Python中的一个库 它是数字的 NumPy库的数学扩展 Pyplot是Matplotlib模块的基于状态的接口 该模块提供了MATLAB like接口 matplotlib pyplot imread 功能 mat
  • Android 11 设置开机默认系统横屏显示

    实现 默认横屏有两套方案 第一种方式 目录 device rockchip rk356x BoardConfig mk SF PRIMARY DISPLAY ORIENTATION 90 第二种方式 Android系统默认是竖屏显示的 想要
  • Git fetch、pull以及merge之间的区别

    笔者当时学习git的时候对fetch以及pull命令之间的区别疑惑不解 被困扰了许久 其实还是对git的原理理解不深才会有这种情况 git对每次提交都会生成一个cmmit id 我们工作区间版本改变其实就是HEAD指针指向的commit i
  • [Git & GitHub] Windows下安装git,从0开始搭建git环境(配置环境变量+设置git-ssh key...配置)(超全版)

    目录 前提准备 安装Git Git配置 配置环境变量 git配置 ssh认证配置过程 配置邮箱和用户名 个人身份 文本换行符配置 前提准备 下载地址 点击此处 点击Windows进行下载 若下载比较慢 点击此处 安装Git 下载之后 双击G
  • Android安全检测-Intent Scheme URLs攻击风险

    一 漏洞原理 利用intent scheme URLs 意图协议URL 可以通过web页面发送intent来启动App应用 攻击者可构造特殊格式的URL直接向系统发送意图 启动App应用的Activity组件或者发送异常数据 导致应用的敏感
  • python3.7添加dlib模块

    1 下载dlib安装包 安装dlib真是费劲 dlib下载地址 http dlib net files 我下载的是dlib 19 14 zip 然后解压安装dlib 在安装dlib前需要安装Boost和Cmake dlib19之后你需要安装
  • Windows DWrite 组件 RCE 漏洞 (CVE-2021-24093) 分析

    聚焦源代码安全 网罗国内外最新资讯 概述 Windows图形组件DWrite库存在数组越界写漏洞 CVE 2021 24093 可导致远程代码执行 当DWrite库解析恶意构造的字体文件时 计算内存分配长度时出现错误 触发越界写 而且字体文
  • simulink电力电子仿真(2)单相桥式半控整流电路实验

    simulink电力电子仿真 2 单相桥式半控整流电路实验 返回目录 主要是赶上了疫情 然后期末要疯狂补实验报告 就索性写一下吧 万一以后再做电力电路仿真 可能会有用的 也希望可以帮助别人 器件的选择及位置 MATLAB的版本 2018a
  • <Linux开发>系统移植 -之- linux构建BusyBox根文件系统及移植过程详细记录

    Linux开发 系统移植 之 linux构建BusyBox根文件系统及移植过程详细记录 前言 本章节讲解的是构建移植BusyBox根文件系统到linux开发板 主要是基于正点原子Linux开发板操作 接下来讲解具体过程记录 BusyBox源
  • sql概念模型和逻辑模型

    一 概念模式和E R图 概念模型的表示方法很多 目前比较常用的是实体联系模型 简称E R模型 E R模型主要用E R图来表示 实体间的联系有 一对一联系 一对多联系 多对多联系 E R模型用矩形框表示现实世界中的实体 用菱形框表示实体间的联
  • Linux开发(七):多线程通信与同步

    线程间无需特别的手段进行通信 因为线程间可以共享数据结构 也就是一个全局变量可以被两个线程同时使用 不过要注意的是线程间需要做好同步 目录 一 互斥锁 1 初始化 1 动态初始化 2 静态初始化 2 加锁 3 解锁 4 销毁互斥锁 5 互斥