操作系统 --- 进程/线程 同步

2023-11-16

操作系统 --- 进程/线程 同步

资源竞争 — race condition

  • 在多线程/多进程中,容易出现race condition
  • race condition: 多个进程或线程对同一个资源进行同时进行修改时会出现混乱
  • 如下图P1和P2的代码转换为汇编之后实际是三行代码
  • 正常情况应该是P1对count修改完P2才能对count进行修改
  • 但是实际情况是当P1正在对count进行修改时P2也对count进行修改,出现了race condition
    在这里插入图片描述
    Producer-Consumer Problem
  • 在counter++和counter–时有可能出现race condition
    在这里插入图片描述

临界区 — critical section

  • critical section的代码会使用critical resource(大部分是shared variable,也可能是IO device或者network connection)
  • 临界区问题: 如何避免临界区的race condition
    在这里插入图片描述

解决临界区问题 ---- 实现进程同步

进程同步的三个原则

互斥(mutual exclusive)

  • 我们需要保证不能有两个进程或线程同时进入critical section,来避免race condition. 也就是critical section need to be protected
  • 也就是要实现互斥(mutual exclusive): 同一时间只能有一个任务进入critical section
  • 所以执行critical section的进程或线程不能被打断,需要实现原子操作(atomic execution)
  • 原子操作(atomic execution): 代码不被打断,一次执行完毕

有限等待(bounded waiting)

  • 在一个进程或线程申请进入临界区并被授权进入临界区之后,此进程或线程能进入临界区的次数是有限的(也就是不能永远占用临界区,要让别的进程或线程也能使用临界区)

前进(progress)

  • 当无进程或线程处于临界区,可允许一个请求进入临界区的进程或线程立即进入自己的临界区

进程同步的简单解决方案 — 锁(lock)

Intuitive Solution

  • 对于进程/线程同步最直接的想法就是加一个shared variable来控制critical section
loop{
	if not lock {
		lock = true;
		critical_section
		lock = false;
		
	}
}
  • 但是问题是 lock 也是shared variable,而对于lock的读写是分开的,所以也存在race condition问题
  • 会导致两个进程或线程同时进入critical section
    在这里插入图片描述

Software solution

  • 以下是两个著名的解决race condition的算法 (通过改进intuitive solution)
Dekker算法
wants_to_enter[0] = false;
wants_to_enter[1] = false;
turn = 0; //or 1

p0:
	//当一个进程或线程想进入critical section时,首先将自己的wants_to_enter[0]变量设置为true
	wants_to_enter[0] = true;
	//查看对方是否想进入, 如果对方不想进入,则自己进入critical section
	while(wants_to_enter[1]) {
		//如果对方想进入,则查看是谁的turn
		if (turn != 0) {
			//如果是对方的turn,则表示自己不想进入
			wants_to_enter[0] = false;
			//然后等待
			while (turn != 0) {
				//busy wait;
			}
			//对方从critical section出来了,turn是自己了,将 wants_to_enter[0] = true
			//表示自己想进入
			wants_to_enter[0] = true;
		}
	}
	
	//critical section
	turn = 1;
	wants_to_enter[0] = false;
	//reminder section
  • 两个进程放在一看:
  • 两个人同时用洗手间,同时表示想进去(wants_to_enter)
    然后看是谁的turn,没有轮到的那个人则表示自己不想进,此时另外一个人就可以进去了
    进去的那个人用完之后表示自己不想进了,然后将turn设置为对方
    在这里插入图片描述
Peterson算法
  • 比dekker算法简单一些
wants_to_enter[A] = true;
turn = B;
while (want_to_enter[B] == true && turn == B) {
	//wait;
}
//critical section

wants_to_enter[A] = false;
  • 只有当对方不想进或者是我的turn的时候进入critical section
    在这里插入图片描述
Software Solution的优缺点

优点:

  • 在用户空间运行,不需要硬件的协助

缺点:

  • 在现代计算机中,不一定能成功,因为software solution依赖于sequential consistency. 而现代计算机为了优化,在编译时会打乱sequential consistency
  • sequential consistency: 简单来说就是按顺序执行代码
  • 同时Software solution只能保证在单核计算机中成功,当多个任务在不同的核运行时,可能会同时进入critical section
  • 所以以上的两种算法已经被淘汰,只有历史意义

Hardware assisted solution

Test_and_Set( )
loop{
	if not lock {
		lock = true;
		critical_section
		lock = false;
		
	}
}
  • 在上面这个代码中,因为对于lock的读和写不是atomic instruction所以会出现race condition
  • 为了解决这个问题,现代计算机提供了atomic read and write指令(汇编指令). 也就是read和write连在一起是一个整体,是atomic的. 这样就在硬件的帮助下解决了这个问题
  • 这个指令叫做 “Test and Set” (TSL)
//pseudo code
//如果lock == true,则继续lock,然后返回true
//如果lock == false,则将lock设为true,然后返回false
function Test_and_Set(lock) {
	current_value = lock;
	lock = true;
	return current_value;
}
//使用test_and_set解决lock问题
//如果lock ==  true则返回true,一直while循环等待
//如果lock == false则返回false并将lock设置为true, 打破循环进入critical section
function Lock(*lock) {
	whileTest_and_Set(lock));
}

function Unlock(*lock) {
	lock = false;
}
compare_and_swap( ) aka CAS
  • 如果系统没有提供Test_and_Set( ),则可以使用compare_and_swap( )
  • compare_and_swap(ptr, old, new), 根据ptr查看所指向的内存里储存的值
  • ptr: 内存地址, old: 备份旧数据, new: 基于旧数据构造新数据
  • 如果 *ptr == old ,说明当前没有其它进程或线程在操作,所以,我们把new这个值写入ptr所指向的内存中
  • 如果 *ptr != old,说明原来备份的旧数据已经被改动, 需要根据被改动的旧数据重新计算新数据
  • 概括:CPU去更新一个值,但如果想改的值不再是原来的值,操作就失败,因为很明显,有其它操作先改变了这个值
  • CPU会保证上述操作是atomic

使用compare_and_swap解决lock问题

//如果lock == fasle,则设置为true,然后返回true
//如果lock == true, 则直接return false
function Lock(*lock) {
 while(!compare_and_swap(&lock, false, true));
 return
}

function Unlock(*lock) {
 lock = false
}
TS和CAS的优缺点 – 自旋锁和ABA问题
  • 自旋锁
  • 从上面代码可以看到,TS和CAS都是通过一个while loop不断查看lock的值,这种情况被叫做busy waiting也就是CPU一直在执行这个进程,开销很大。这种锁被叫叫做自旋锁(spinlock)
  • 当然自旋锁也有好处,就是在进程等待时不会有context switch.而context switch的时间消耗比较大
  • CAS的ABA问题
  • CAS还有个问题就是ABA问题,比如第一次拿到内存里的值时是A,然后被其他线程修改为B, 然后又修改为A, 而此时去比较内存里的值会发现没有变,但是实际上还是有改动
  • 举个通俗点的例子,你倒了一杯水放桌子上,干了点别的事,然后同事把你水喝了又给你重新倒了一杯水,你回来看水还在,拿起来就喝,如果你不管水中间被人喝过,只关心水还在,还好 ; 但是假若你是一个比较讲卫生的人,那你肯定就不高兴了
  • ABA问题的解决思路: 使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A了
互斥锁(mutex lock)
  • 为了解决spinlock CPU开销大的问题,我们可以让一个进程或线程进入阻塞状态如果这个进程或线程需要进入的critical section是locked,然后等待CPU调度唤醒再次查看critical section的状态
  • 这种锁被叫做互斥锁(mutex lock)

data structure of mutex:

//pesudo code
struct {
	int lock; //mutex value
	PCB *PCB_block_q; //waitlist of process or threads, 放入阻塞的线程,等待CPU唤醒
}

常用的C library mutex函数

#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
					   const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destory(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//以上所有函数成功时返回值为0,失败会返回error code

//pthread_mutex_t的定义
 typedef struct {
         struct pthread_queue queue; //waitlist of process or threads
         char lock; //mutex value
         struct pthread *owner;
         int flags;
 #ifdef _POSIX_THREADS_PRIO_PROTECT
         int prioceiling;
         pthread_protocol_t protocol;
         int prev_max_ceiling_prio;
 #endif
 } pthread_mutex_t;
  

pthread mutex example

#include <stdio.h>
#include <pthread.h>
//假设我们有这样的一个数据结构可以被多个线程访问
struct foo {
	int 			f_count; //记录有多少个线程访问了这个数据结构
	pthread_mutex_t f_lock; //一个mutex lock用来保护这个数据结构
	int 			f_id;
};

//初始化数据结构
strcut foo* foo_alloc(int id) {
	struct foo *fp;
	if ((fp = malloc(sizeof(struct foo))) != NULL) {
		fp->f_count = 1; //初始值为1, 暂时没有其他线程访问
		fp->f_id = id;
		//初始化mutex,如果初始化失败则释放内存,返回
		if (pthread_mutex_init(&fp->f_look, NULL) != 0) {
			free(fp);
			return(NULL);
		}
	}
	return(fp);
}

//如果一个线程正在访问数据结构fp, foo_hold会increment f_count,用mutex锁保护f_count
void foo_hold(struct foo *fp) {
	//先上锁
	pthread_mutex_lock(&fp->f_lock);
	fp->f_count++;
	//然后解锁
	pthread_mutex_unlock(&fp->f_lock);
}

//如果一个线程完成访问,foo_rele会decrement f_count
void foo_rele(struct foo *fp) {
	//先上锁
	pthread_mutex_lock(&fp->f_lock);
	//如果f_count等于0,则说明没有线程在访问,所以先解锁, 然后销毁锁,然后释放fp
	if (--fp->f_count == 0) {
		pthread_mutex_unlock(&fp->f_lock);
		pthread_mutex_destroy(&fp->f_lock);
		free(fp);
	}
	else {
		//其余情况直接解锁
		pthread_mutex_unlock(&fp->f_lock);
	}
}

死锁,活锁,饥饿(deadlock, livelock and starvation)

死锁(deadlock)
  • 死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

发生死锁的条件:

  • 当前进程或线程拥有其他进程或线程需要的资源
  • 当前进程或线程等待其他进程或线程已拥有的资源
  • 都不放弃自己拥有的资源,也就是不能被其他进程或线程剥夺,只能在使用完以后由自己释放

Example 1:

mutex m
function {
	lock(m) //成功拿到锁
	lock(m) //拿不到锁,因为已经被自己拿了,所以会无限等待下去
	//critical section
	unlock(m)
	unlock(m)
}

Example 2:
在这里插入图片描述

  • task A成功拿到M1的锁,同时task B成功拿到M2的锁
  • task A等待获取M2的锁,同时task B等待获取M1的锁
  • task A只有获得M2的锁才能往下继续然后释放M1的锁
  • task B只有获得M1的锁才能往下继续然后释放M2的锁

避免死锁:

  • 在所有进程或线程中使用相同的加锁顺序
  • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
  • 通过算法实现死锁检测机制
//Example: 在所有进程或线程中使用相同的加锁顺序
Thread A:
functionA {
    //some code
	lock(A)
	lock(B)
	// ....
}

Thread B:
functionB {
    //some code
	lock(A) //不能是lock(B) 然后lockA
	lock(B)
	// ....
}
活锁(livelock)
  • 是指线程1可以使用资源,但它很礼貌,让其他线程先使用资源,线程2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源。
饥饿(starvation)
  • 是指一个可运行的进程尽管能继续执行,但被调度器无限期地忽视,而不能被调度执行的情况

乐观锁和悲观锁(Optimistic Locking and Pessimistic Locking)

  • 乐观锁和悲观锁严格的说不是一种锁,而是一种策略
  • 加锁是一种悲观的策略,它总是认为每次访问共享资源的时候,总会发生冲突,所以宁愿牺牲性能(时间)来保证数据安全。
  • 无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加锁,因此线程将不断执行,不需要停止。一旦碰到冲突,就重试当前操作直到没有冲突为止。无锁的策略之一就是使用CAS

更好的解决进程同步的方法— 信号量(semaphore)

什么是semaphore

  • semaphore的本质是一个计数器
  • semaphore主要用于调度进程, 维护对共享资源的顺序
  • 所以利用semaphore可以解决很多进程同步的问题,如mutex,生产者消费者问题,read/writer问题等

semaphore的实现

  • 数据结构semaphore包括一个 counter用来表示目前的资源数量,和一个queue用于存放等待中的线程
  • semaphore的实现包括两个函数: post(signal), wait
  • wait对counter进行递减操作,表示消费掉资源一个, 如果递减完counter为负说明目前没有资源可供消费,则需要将目前进程放进等待队列, 然后block掉
  • post对counter进行递增操作,表示资源增加一个,如果递增完counter小于等于0则说明有线程在排队等待消费资源,所以需要队列中的资源移出,然后唤醒
//pseudo code
struct {
	int counter;//表示目前的资源数量
	queue q;//用于存放等待中的线程
} sem_t 

//v operation
signal(sem_t *s) {
	s.counter++;
	//counter小于等于0则说明有线程在排队等待消费资源,所以需要队列中的资源移出,然后唤醒线程	
	if (s.counter <= 0) {
		remove(s.q, p);
		wakeup(q);
	}
}

//p operation
wait(sem_t *s) {
	s.counter--;
	//counter为负说明目前没有资源可供消费,则需要将目前进程放进等待队列, 然后block掉
	if (s.counter < 0) {
		add this thread to s.q;
		block();
	}
}

semaphore的应用

semaphore控制代码执行顺序
  • 以下是两个任务,可以利用semaphore实现statement1在statement2之前运行
  • sem_t synch.counter = 0;
  • wait(synch)会对counter – 为 -1, 所以T2会被放进queue,block掉
  • signal(synch) 会对counter++为0,所以会唤醒T2,让T2继续执行
    在这里插入图片描述
semaphore实现mutex功能
  • 当counter等于1时,semaphore实现了mutext的功能 (也就是mutex就是semaphore的value等于1的情况)
  • 假设A首先执行wait(M), 则counter = 0,B和C会被放进queue,而counter被B和C递减两次变为 -2
  • A执行完signal之后,会对counter++变为-1,然后唤醒B
  • B执行完signal之后,会对counter++变为0,然后唤醒C
  • C执行完signal之后,会对counter++变为1
    在这里插入图片描述
semaphore解决消费者/生产者问题
  • 有多个消费者和生产者要对下图的buffer进行操作
    在这里插入图片描述
sem_t has_space = N;
sem_t has_item = 0;
sem_t mutex = 1;

producer() {
	int item;
	while (True) {
		item = produce_item( );
		//producer需要查看buffer里还有没有space
		wait(has_space);
		//has_item和has_space的作用是负责资源分配,
		//但是不能保证不会有多个produce和conumer同时进入critical section所以需要加mutex
		wait(mutex);
		
		//critical section
		buffer[in] = item;
		in = (in + 1) % N;
		
		signal(mutex);
		//生产完成后,递增资源数量
		signal(has_item);
	}
}

consumer() {
	int item;
	while(True) {
		//consumer需要查看buffer里有没有item
		wait(has_item);
		//has_item和has_space的作用是负责资源分配,
		//但是不能保证不会有多个produce和consumer同时进入critical section所以需要加mutex
		wait(mutex);
		
		//critical section
		item = buffer[out];
		out = (out + 1) % N;
		
		signal(mutex);
		//消费完成后,递增space数量
		signal(has_space);
	}
}

  • 注意上面wait(has_item)和wait(mutex)的顺序不能互换,否则l两个mutex会形成deadlock
  • 假设producer先拿到mutex锁但是没有space了,所以会被block掉等待space, 但是此时consumer在等待获得mutex锁才能进入critical section消费资源
    在这里插入图片描述
semaphore解决writer/reader问题 — reader preference
  • 多个reader和writer同时对资源进行读写,如何同步
  • 第一种方法: reader preference
  • 多个reader可以同时读取资源,reader之间不需要竞争
  • reader和writer不能同时访问资源
  • 即使有writer在等待,reader也可以访问资源
  • 只有当所有reader访问完之后writer才能访问资源
  • 只有第一个reader需要和writer竞争
  • 也就是第一个reader代表所有reader和writer竞争资源,当reader成功竞争到资源之后,剩下的所有reader可以直接读取资源,而writer只能等待所有reader完成之后才能访问资源,所以最后一个reader访问完毕后需要解锁
int reader_count = 0; //用于记录reader的数量
sem_t resource_mutex = 1 //保护资源的mutex
sem_t reader_count_mutex = 1//保护变量reader_count的mutex,因为reader_count也是共享变量
reader() {
	wait(reader_count_mutex);	
	/这部分也是critial section, 因为有count
	reader_count++;
	if (reader_count == 1) {
		wait(resource_mutex);
	}
	///
	signal(reader_count_mutex);
	
	 //critical section
	 //read;
	 
	wait(reader_count_mutex);
	//这部分也是critial section, 因为有count
	reader_count--
	if (reader_count == 0) {
		signal(resource_mutex);
	}
	/
	signal(reader_count_mutex);
}


writer () {
	wait(resource_mutex)//critical section
	//write
	signal(resource_mutex);
}
  • reader preference存在的问题是,如果不断的有reader进来,则writer会进入饥饿状态
  • 所以reader preference需要改进为 ---- writer preference
semaphore解决writer/reader问题 — writer preference
  • 为了解决reader preference下writer进入饥饿状态的情况
  • 主要思路是降低reader访问资源的机率
  • 假设资源放在会议室里,在之前reader preference的模式下会议室只有一道门(resource_mutex), 而现在需要在会议室外再加一道门
  • 所有的reader需要互相竞争进入第一道门, 而除去第一个writer需要和reader竞争以外,其余所有的writer都可以直接进入第一道门
  • 然后按照reader preference分配第二道门的权限
int reader_count = 0; //第二道门:需要记录reader的数量
int writer_count = 0;//第一道门: 需要记录writer的数量
sem_t first_door_mutex = 1 //第一道门的mutex
sem_t resource_mutex = 1 //保护资源的mutex(第二道门的mutex)
sem_t reader_count_mutex = 1//保护变量reader_count的mutex,因为reader_count也是共享变量
sem_t writer_count_mutex = 1//保护变量writer_count的mutex,因为writer_count也是共享变量

reader() {
	wait(first_door_mutex);
	wait(reader_count_mutex);
	reader_count++;
	if (reader_count == 1) {
		wait(resource_mutex);
	}
	signal(reader_count_mutex);	
	signal(first_door_mutex);
	//read critical section
	
	reader_count--;
	if (reader_count == 0)
		signal(reource_mutex);
	signal(reader_count_mutex);
	
}

writer() {
	wait(writer_count_mutex);
	writer_count++;
	if (writer_count == 1)
		wait(first_door_mutex);
	signal(writer_count_mutex);
	
	wait(reource_mutex);
	//write --- critcial section
	signal(reource_mutex);
	
	wait(writer_count_mutex);
	writer_count--;
	if (writer_count == 0) {
		signal(first_door_mutex);
	}
	signal(writer_count_mutex);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

操作系统 --- 进程/线程 同步 的相关文章

  • 拆分字符串以仅获取前 5 个字符

    我想去那个地点 var log src ap kernelmodule 10 001 100 但看起来我的代码必须处理 ap kernelmodule 10 002 100 ap kernelmodule 10 003 101 等 我想使用
  • Unix 命令列出包含字符串但*不*包含另一个字符串的文件

    如何递归查看包含一个字符串且不包含另一个字符串的文件列表 另外 我的意思是评估文件的文本 而不是文件名 结论 根据评论 我最终使用了 find name html exec grep lR base maps xargs grep L ba
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 从 PL/SQL 调用 shell 脚本,但 shell 以 grid 用户而非 oracle 身份执行

    我正在尝试使用 Runtime getRuntime exec 从 Oracle 数据库内部执行 shell 脚本 在 Red Hat 5 5 上运行的 Oracle 11 2 0 4 EE CREATE OR REPLACE proced
  • Elasticsearch 无法写入日志文件

    我想激活 elasticsearch 的日志 当我运行 elasticsearch 二进制文件时 我意识到我在日志记录方面遇到问题 无法加载配置 这是输出 sudo usr share elasticsearch bin elasticse
  • 在 Linux 上更快地分叉大型进程?

    在现代 Linux 上达到与 Linux 相同效果的最快 最好的方法是什么 fork execve combo 从一个大的过程 我的问题是进程分叉大约 500MByte 大 并且一个简单的基准测试只能从进程中实现约 50 个分叉 秒 比较最
  • Bash 解析和 shell 扩展

    我对 bash 解析输入和执行扩展的方式感到困惑 对于输入来说 hello world 作为 bash 中的参数传递给显示其输入内容的脚本 我不太确定 Bash 如何解析它 Example var hello world displaywh
  • 如何通过替换为空页映射来取消映射 mmap 文件

    Linux 用户空间有没有办法用空页面 映射自 dev null 或者可能是一个空页面 重复映射到从文件映射的页面的顶部 对于上下文 我想找到这个 JDK bug 的修复 https bugs openjdk java net browse
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • 从 csv 文件中删除特定列,保持输出上的相同结构[重复]

    这个问题在这里已经有答案了 我想删除第 3 列并在输出文件中保留相同的结构 输入文件 12 10 10 10 10 1 12 23 1 45 6 7 11 2 33 45 1 2 1 2 34 5 6 I tried awk F 3 fil
  • Jenkins中找不到环境变量

    我想在詹金斯中设置很多变量 我试过把它们放进去 bashrc bash profile and profile of the jenkins用户 但 Jenkins 在构建发生时找不到它们 唯一有效的方法是将所有环境变量放入Jenkinsf
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • 我可以从命令行打印 html 文件(带有图像、css)吗?

    我想从脚本中打印带有图像的样式化 html 页面 谁能建议一个开源解决方案 我使用的是 Linux Ubuntu 8 04 但也对其他操作系统的解决方案感兴趣 你可以给html2ps http user it uu se jan html2
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您
  • 添加要在给定命令中运行的 .env 变量

    我有一个 env 文件 其中包含如下变量 HELLO world SOMETHING nothing 前几天我发现了这个很棒的脚本 它将这些变量放入当前会话中 所以当我运行这样的东西时 cat env grep v xargs node t
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 如何在Linux内核源代码中打印IP地址或MAC地址

    我必须通过修改 Linux 内核源代码来稍微改变 TCP 拥塞控制算法 但为了检查结果是否正确 我需要记录 MAC 或 IP 地址信息 我使用 PRINTK 函数来打印内核消息 但我感觉很难打印出主机的MAC IP地址 printk pM
  • Linux 内核标识符中前导和尾随下划线的含义是什么?

    我不断遇到一些小约定 比如 KERNEL Are the 在这种情况下 是内核开发人员使用的命名约定 还是以这种方式命名宏的语法特定原因 整个代码中有很多这样的例子 例如 某些函数和变量以 甚至 这有什么具体原因吗 它似乎被广泛使用 我只需
  • NPTL 和 POSIX 线程有什么区别?

    NPTL 和 POSIX 线程之间的基本区别是什么 这两者是如何演变的 POSIX 线程 pthread 不是一个实现 它是几个函数的 API 规范 纸上的标准 英文 其名称以pthread 以及定义在
  • Linux 可执行文件与 OS X“兼容”吗?

    如果您在基于 Linux 的平台上用 C 语言编译一个程序 然后将其移植以使用 MacOS 库 它会工作吗 来自编译器的核心机器代码在 Mac 和 Linux 上兼容吗 我问这个问题的原因是因为两者都是 基于 UNIX 的 所以我认为这是真

随机推荐

  • 【面经】外企德科-华为精英研发项目-笔试编程题

    微信搜索 编程笔记本 获取更多干货 微信搜索 编程笔记本 获取更多干货 点击上方蓝字关注我 我们一起学编程 欢迎小伙伴们分享 转载 私信 赞赏 今天来看一道 外企德科 华为精英研发项目 的一道笔试编程题 求满足条件的最长字串的长度 题目描述
  • 一次 Young GC 的优化实践

    这个 GC 案例比较有意思 排查问题有点像侦探断案 先分析各种可能性 再按照获得的一个个证据 去排除各种可能性 然后定位原因 最终解决问题 问题 某同学在微信上问我 有没有办法排查 YoungGC 效率低的问题 听到这话 我也是不知从何说起
  • Linux网络编程:Socket套接字编程(Server服务器 Client客户端)

    文章目录 一 定义和流程分析 1 定义 2 流程分析 3 网络字节序 二 相关函数 IP地址转换函数inet pton inet ntop 本地字节序 网络字节序 socket函数 创建一个套接字 bind函数 给socket绑定一个服务器
  • 引领AI数据标注行业,景联文科技提供高质量图像和文本标注服务

    近年来 我国的数据要素市场呈现出高速增长的趋势 根据国家工信安全中心的统计数据 截至2022年 我国数据要素市场规模已达到815亿元 同比增长49 51 数据要素作为数字经济时代的关键要素 是构建新发展格局的重要支撑 其重要性日益凸显 党中
  • Android开发学习之路-基本事件的使用

    1 事件的响应方法 setOnClickListener view OnClickListener l setOnFocusChangeListener view OnFocusChangeListener l setOnLongClick
  • [从零开始学习FPGA编程-37]:进阶篇 - 基本时序电路-有限状态机实现(Verilog)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 第1章 状态机概述 1 1 UML描述状态机 1 2 数字电路描述状态机
  • VScode的代码截图插件CodeSnap

    CodeSnap 在 VS Code 中为您的代码截取漂亮的屏幕截图 插件名 CodeSnap 官方地址 CodeSnap Visual Studio Marketplace 特征 快速保存代码的屏幕截图 将屏幕截图复制到剪贴板 显示行号
  • user-select不可被用户选中

    目录 背景 字段属性 注意 案例 背景 项目中有这种奇葩需求 就是特定字段不让用户复制 不可被选中状态 这种应用场景最多的就是考试系统啥的吧 不让考生复制题目搜题 真恶心 字段属性 注意 浏览器实现之间的区别之一是继承 案例
  • 我用Python做副业,月赚1W+:千万别让死工资拖垮了自己。。

    被压垮的打工人 你还好吗 房贷车贷 上老下小 日常开销节省不了 但你的收入有多少 所以不敢生病 甚至不敢回家 就为了每个月那么点死工资 还得天天加班 然而忙忙忙 却变成了 穷忙族 成为了职场废人 其实很多人都想改变现状 想学点什么的 但就是
  • Proxy error: Could not proxy request错误解决

    vue 项目 错误原因 跨域 解决办法 package json文件中的scripts调试添加 start node index js server nodemon index js ignore client 这篇文章解释的很清楚http
  • 二叉排序树转化成双向链表

    题目描述 输入一棵二叉搜索树 将该二叉搜索树转换成一个排序的双向链表 要求不能创建任何新的结点 只能调整树中结点指针的指向 输入 输入可能包含多个测试样例 对于每个测试案例 输入的第一行为一个数n 0
  • 浅谈Spring-AOP

    HI 大家好 我是Lee 这篇文章我们主要说一下关于Spring AOP 什么是AOP 什么叫做面向切面编程 为什么要使用AOP 接下来让我们往下看 什么是AOP AOP为Aspect Oriented Programming的缩写 意思为
  • Lua基础之语法

    目录 1 输出2 注释3 控制语句4 赋值语句5 运算符6 关键字7 变量类型8 其他 原文地址http blog csdn net dingkun520wy article details 49930543 1 输出 print Hell
  • ubuntu16.04 开启ssh服务

    安装 sudo apt get install openssh server 启动 sudo service ssh start 查询服务启动状态 sudo ps e grep ssh 或者 sudo service ssh status
  • R绘图的文本大小,字体字号,字样,图形边界设置及坐标轴

    用于指定文本大小的参数 cex 表示相对于默认大小缩放倍数的数值 默认大小为1 1 5表示放大为默认值的1 5倍 0 5表示做小为默认大小的0 5倍 cex axis 坐标轴刻度文字的缩放倍数 类似cex cex lab 坐标轴标签 名称
  • markdown插入音频和视频

    优酷视频 width 560 height 315 src http player youku com embed XMzk1NTkwODkzNg allowfullscreen gt 爱奇艺视频 音乐1 border 0 width 33
  • 3DMAX 卸载工具,完美彻底卸载清除干净3dmax各种残留注册表和文件

    一些同学安装3dmax出错了 也有时候想重新安装3dmax的时候会出现这种本电脑windows系统已安装3dmax 你要是不留意直接安装 只会安装3dmax的附件 3dmax是不会安装上的 这种原因呢就是大家在之前卸载3dmax时没有吧3d
  • 中国联通卡上在苹果5上显示无服务器,苹果iPhone8显示无服务怎么办

    2019 10 11阅读 85 您可以通过以下方式下载壁纸 1 使用浏览器下载符合手机屏幕分辨率的壁纸保存至手机相册 2 使用第三方软件下载主题壁纸并安装 3 通过电脑下载喜欢的壁纸图片 通过数据线传输到手机中 2019 10 11阅读 9
  • Spring Boot 单体应用一键升级成 Spring Cloud Alibaba

    背景 随着 Apache Dubbo Nacos 以及 Spring Cloud 等服务框架的流行 越来越多的企业开始采用微服务架构来构建其应用程序 微服务架构使企业能够将其应用程序拆分成多个小型服务 这些服务可以独立部署和扩展 这种架构模
  • 操作系统 --- 进程/线程 同步

    操作系统 进程 线程 同步 资源竞争 race condition 临界区 critical section 解决临界区问题 实现进程同步 进程同步的三个原则 互斥 mutual exclusive 有限等待 bounded waiting