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开发(七):多线程通信与同步 的相关文章

  • Pthreads - 高内存使用率

    我正在用 C 编写一些东西 在 256Mb 系统上的 Linux 中创建大量 Pthread 我通常有 200Mb 的免费空间 当我使用少量线程运行该程序时 它可以工作 但是一旦我让它创建大约 100 个线程 它就会出现错误 因为系统内存不
  • 如何通过ssh获取远程命令的退出代码

    我正在通过 ssh 从远程计算机运行脚本 ssh some cmd my script 现在 我想在本地计算机上存储 shell 脚本的退出状态 我该怎么做 假设没有任何问题ssh其本身 其退出状态是在远程主机上执行的最后一个命令的退出状态
  • 我想在 Red Hat Linux 服务器中执行 .ps1 powershell 脚本

    我有一个在窗口中执行的 ps1 powershell 脚本 但我的整个数据都在 Linux 服务器中 有什么可能的方法可以让我在红帽服务器中执行 powershell 脚本 powershell脚本是 Clear Host path D D
  • 如何将一个文本文件拆分为多个 *.txt 文件?

    我有一个文本文件file txt 12 MB 包含 something1 something2 something3 something4 有没有办法分开file txt分成 12 个 txt 文件 比方说file2 txt file3 t
  • 任何退出 bash 脚本但不退出终端的方法

    当我使用exitshell 脚本中的命令 该脚本将终止终端 提示符 有什么方法可以终止脚本然后停留在终端中吗 我的剧本run sh预计通过直接获取或从另一个脚本获取来执行 编辑 更具体地说 有两个脚本run2 sh as run sh ec
  • 对于任何真实数据集,数据压缩比的最小可能值是多少

    我在写信ZLIB类似于嵌入式硬件压缩器的 API 它使用 deflate 算法来压缩给定的输入流 在进一步讨论之前 我想解释一下数据压缩率 数据压缩率定义为未压缩大小与压缩大小之间的比率 压缩比通常大于一 这意味着压缩数据通常比未压缩数据小
  • 使用 shell 脚本发送 HTML 邮件

    如何使用 shell 脚本发送 HTML 电子邮件 首先 您需要撰写消息 最低限度由这两个标头组成 MIME Version 1 0 Content Type text html 以及适当的消息正文 p Hello world p 获得后
  • 如何从 Bash 命令行在后台 Vim 打开另一个文件?

    我正在从使用 Gvim 过渡到使用控制台 Vim 我在 Vim 中打开一个文件 然后暂停 Vim 在命令行上运行一些命令 然后想返回到 Vim Ctrl Z 在正常模式下 暂停 Vim 并返回到控制台 fg可用于将焦点返回到 Vim job
  • Bash:将字符串添加到文件末尾而不换行

    如何将字符串添加到文件末尾而不换行 例如 如果我使用 gt gt 它将添加到文件末尾并换行 cat list txt yourText1 root host 37 echo yourText2 gt gt list txt root hos
  • bash while 循环的布尔表达式中的 -lt 意味着什么?

    我猜测它代表小于基于输出 但是我在哪里可以找到有关此语法的文档 bin bash COUNTER 0 while COUNTER lt 10 do echo The counter is COUNTER let COUNTER COUNTE
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • 如何在 Linux 和 C 中使用文件作为互斥体?

    我有不同的进程同时访问 Linux 中的命名管道 并且我想让此访问互斥 我知道可以使用放置在共享内存区域中的互斥体来实现这一点 但作为一种家庭作业 我有一些限制 于是 我想到的是对文件使用锁定原语来实现互斥 我做了一些尝试 但无法使其发挥作
  • BASH:输入期间按 Ctrl+C 会中断当前终端

    我的 Bash 版本是 GNU bash version 4 3 11 1 release x86 64 pc linux gnu 我有一段这样的代码 while true do echo n Set password read s pas
  • 在 x86 汇编语言中获取文件大小的简单方法

    假设我已经在汇编中打开了一个文件 并且在寄存器 eax 中有该文件的文件句柄 我将如何获取文件的大小 以便为其分配足够的缓冲区空间 我在这里研究了另一个讨论 建议使用sys fstat 28 系统调用来获取文件统计信息但无法实现它 My a
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何才能将 TCP 连接返回到同一端口?

    机器是 RHEL 5 3 内核 2 6 18 有时我在 netstat 中注意到我的应用程序有连接 建立了 TCP 连接本地地址 and 国外地址是一样的 其他人也报告了同样的问题 症状与链接中描述的相同 客户端连接到本地运行的服务器的端口
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的

随机推荐

  • 我是废物...

    我是废物
  • 【数据手册】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 互斥