精华内容
下载资源
问答
  • Linux线程同步

    2019-04-28 08:59:02
    线程本地存储可以避免线程访问共享数据,但是...Linux提供的线程同步机制主要有互斥锁和条件变量。其它形式的线程同步机制用得并不多。 首先我们看一下互斥锁。所谓的互斥就是线程之间互相排斥,获得资源的线程排...

           线程本地存储可以避免线程访问共享数据,但是线程之间的大部分数据始终还是共享的。在涉及到对共享数据进行读写操作时,就必须使用同步机制,否则就会造成线程们哄抢共享数据的结果,这会把你的数据弄的七零八落理不清头绪。

    Linux提供的线程同步机制主要有互斥锁和条件变量。其它形式的线程同步机制用得并不多。

           首先我们看一下互斥锁。所谓的互斥就是线程之间互相排斥,获得资源的线程排斥其它没有获得资源的线程。Linux使用互斥锁来实现这种机制。

           既然叫锁,就有加锁和解锁的概念。当线程获得了加锁的资格,那么它将独享这个锁,其它线程一旦试图去碰触这个锁就立即被系统“拍晕”。当加锁的线程解开并放弃了这个锁之后,那些被“拍晕”的线程会被系统唤醒,然后继续去争抢这个锁。至于谁能抢到,只有天知道。但是总有一个能抢到。于是其它来凑热闹的线程又被系统给“拍晕”了……如此反复。感觉线程的“头”很痛:)

           从互斥锁的这种行为看,线程加锁和解锁之间的代码相当于一个独木桥,同意时刻只有一个线程能执行。从全局上看,在这个地方,所有并行运行的线程都变成了排队运行了。比较专业的叫法是同步执行,这段代码区域叫临界区。同步执行就破坏了线程并行性的初衷了,临界区越大破坏得越厉害。所以在实际应用中,应该尽量避免有临界区出现。实在不行,临界区也要尽量的小。如果连缩小临界区都做不到,那还使用多线程干嘛?

    互斥锁在Linux中的名字是mutex。这个似乎优点眼熟。对,在前面介绍NPTL的时候提起过,但是那个叫futex,是系统底层机制。对于提供给用户使用的则是这个mutex。Linux初始化和销毁互斥锁的接口是pthread_mutex_init()和pthead_mutex_destroy(),对于加锁和解锁则有pthread_mutex_lock()、pthread_mutex_trylock()和pthread_mutex_unlock()。这些接口的完整定义如下:

    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); 

    从这些定义中可以看到,互斥锁也是有属性的。只不过这个属性在绝大多数情况下都不需要改动,所以使用默认的属性就行。方法就是给它传递NULL。

    phtread_mutex_trylock()比较特别,用它试图加锁的线程永远都不会被系统“拍晕”,只是通过返回EBUSY来告诉程序员这个锁已经有人用了。至于是否继续“强闯”临界区,则由程序员决定。系统提供这个接口的目的可不是让线程“强闯”临界区的。它的根本目的还是为了提高并行性,留着这个线程去干点其它有意义的事情。当然,如果很幸运恰巧这个时候还没有人拥有这把锁,那么自然也会取得临界区的使用权。

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <pthread.h>
    pthread_mutex_t g_mutex;
    int g_lock_var = 0;
    void* thread1( void *arg )
    {
        int i, ret;
        time_t end_time;
        end_time = time(NULL) + 10;
        while( time(NULL) < end_time ) {
            ret = pthread_mutex_trylock( &g_mutex );
            if( EBUSY == ret ) {
                printf( "thread1: the varible is locked by thread2.\n" );
            } else {
                printf( "thread1: lock the variable!\n" );
                ++g_lock_var;
                pthread_mutex_unlock( &g_mutex );
            }
            sleep(1);
        }
        return NULL;
    }
    void* thread2( void *arg )
    {
        int i;
        time_t end_time;
        end_time = time(NULL) + 10;
        while( time(NULL) < end_time ) {
            pthread_mutex_lock( &g_mutex );
            printf( "thread2: lock the variable!\n" );
            ++g_lock_var;
            sleep(1);
            pthread_mutex_unlock( &g_mutex );
        }
        return NULL;
    }
    int main( int argc, char *argv[] )
    {
        int i;
        pthread_t pth1,pth2;
        pthread_mutex_init( &g_mutex, NULL );
        pthread_create( &pth1, NULL, thread1, NULL );
        pthread_create( &pth2, NULL, thread2, NULL );
        pthread_join( pth1, NULL );
        pthread_join( pth2, NULL );
        pthread_mutex_destroy( &g_mutex );
        printf( "g_lock_var = %d\n", g_lock_var );
        return 0;                            
    }

    使用条件变量

    从代码中会发现,等待“事件”发生的接口都需要传递一个互斥锁给它。而实际上这个互斥锁还要在调用它们之前加锁,调用之后解锁。不单如此,在调用操作“事件”发生的接口之前也要加锁,调用之后解锁。这就面临一个问题,按照这种方式,等于“发生事件”和“等待事件”是互为临界区的。也就是说,如果“事件”还没有发生,那么有线程要等待这个“事件”就会阻止“事件”的发生。更干脆一点,就是这个“生产者”和“消费者”是在来回的走独木桥。但是实际的情况是,“消费者”在缓冲区满的时候会得到这个“事件”的“通知”,然后将字符逐个打印出来,并清理缓冲区。直到缓冲区的所有字符都被打印出来之后,“生产者”才开始继续工作。

    为什么会有这样的结果呢?这就要说明一下pthread_cond_wait()接口对互斥锁做什么。答案是:解锁。pthread_cond_wait()首先会解锁互斥锁,然后进入等待。这个时候“生产者”就能够进入临界区,然后在条件满足的时候向“消费者”发出信号。当pthead_cond_wait()获得“通知”之后,它还要对互斥锁加锁,这样可以防止“生产者”继续工作而“撑坏”缓冲区。另外,“生产者”在缓冲区不满的情况下才能工作的这个限定条件是很有必要的。因为在pthread_cond_wait()获得通知之后,在没有对互斥锁加锁之前,“生产者”可能已经重新进入临界区了,这样“消费者”又被堵住了。也就是因为条件变量这种工作性质,导致它必须与互斥锁联合使用。

    此外,利用条件变量和互斥锁,可以模拟出很多其它类型的线程同步机制,比如:event、semaphore等。

    展开全文
  • Linux内核同步

    2020-09-25 17:03:08
    如果多个执行线程同时访问和操作数据,就有可能发生各线程之间互相覆盖共享数据的情况,造成被访问数据处于不一致状态,因此共享资源需要防止并发访问。 临界区与竞争条件 临界区就是访问和操作共享数据的代码段,...

    如果多个执行线程同时访问和操作数据,就有可能发生各线程之间互相覆盖共享数据的情况,造成被访问数据处于不一致状态,因此共享资源需要防止并发访问。

    临界区与竞争条件

    临界区就是访问和操作共享数据的代码段,如果两个执行线程有可能处于同一个临界区中同时执行,那么这种情况就称为竞争条件。

    简单来讲,当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,这种情况就是竞争条件。

    例如下面的例子就出现了竞争条件:

    假设两个进程P1和P2共享了变量a。在某一执行时刻,P1更新a为1,在几乎同一时刻,P2更新a为2。因此两个任务竞争地写变量a。在这个例子中,竞争的“ 失败者”(最后更新的进程)决定了变量a的最终值。

    同步即避免并发和防止竞争条件。

    内核使用的各种同步技术

    有以下技术:
    在这里插入图片描述

    每CPU变量

    最简单也是最重要的同步技术包括把内核变量声明为每CPU变量。

    每CPU变量主要是数据结构的数组,系统的每个CPU对应数组的一个元素。

    一个CPU不应该访问其它CPU对应的数组元素,但可以随意读写自己的元素而不用担心出现竞争条件,因为它是唯一有资格这么做的CPU。

    按我的理解,这项同步技术就是将可能被竞争访问的内核变量声明为每CPU变量,然后每CPU变量只能被一个CPU访问,从而避免出现竞争情况。

    但是要注意:在单处理器和多处理器系统中,内核抢占都可能使每CPU变量产生竞争条件,因此总的原则是内核控制路径应该在禁用抢占的情况下访问每CPU变量


    原子操作

    有不少汇编指令是"读-修改-写"的类型的,也就是说这种指令要访问内存两次,一次读来获取旧的值,一次写来写入新的值。如果有两个或两个以上CPU同时发起了这种类型的操作,最终的结构就可能是错误的(每个CPU都读到了旧的值,然后做修改再写,这样最后的写会取胜,如果是两次加1的话,这种情形下,最终只会加一次1)。

    最简单的避免这种问题的方式是在芯片级保证这种操作是原子的。 任何一个这样的操作都必须以单个指令执行(即以单条指令完成对内存的修改),中间不能中断,且避免其它的CPU访问同一存储器单元。


    优化屏障和内存屏障

    编译器编译源代码时,会将源代码进行优化,将源代码的指令进行重排序,以适合于CPU的并行执行。

    然而,内核同步必须避免指令重新排序,优化屏障(Optimization barrier)避免编译器的重排序优化操作,保证编译程序时在优化屏障之前的指令不会在优化屏障之后执行。

    也可通过内存屏障强制内存访问次序。内存屏障像一堵墙,所有在设置内存屏障之前发起的内存访问,必须先于在设置屏障之后发起的内存访问之前完成,确保内存访问按程序的顺序完成。

    自旋锁

    加锁是广泛应用的同步技术。当内核控制路径必须访问共享数据结构或进入临界区时,就需要为自己获取一把“锁”。由锁机制保护的资源类似于闲置房间内的资源,当某人进入房间时,就把门锁上。当内核控制路径释放了锁之后,门就打开,另一个内核控制路径就可以进入房间。

    在这里插入图片描述

    自旋锁是在多处理器环境中工作的一种特殊锁。如果内核控制路径发现自旋锁开着,就获取锁并继续自己的执行。相反,如果发现锁被另一个CPU上的内核控制路径锁着,就在周围旋转,反复执行一条紧凑的循环指令,直到锁被释放

    自旋锁的循环指令表示“忙等”,即无事可做,也在CPU上保持运行。

    一般来说,自旋锁保护的每个临界区都是禁止内核抢占的。在单处理器系统上,这种锁本身起不到锁的作用,此时自旋锁原语仅仅是禁止或启用内核抢占。另外需要注意的是在自旋锁忙等期间,内核抢占还是有效的,因此等待自旋锁被释放的任务可能被更高优先级的任务所替代。

    读写自旋锁

    读写自旋锁的引入是为了增加内核的并发能力。只要没有内核控制路径对数据结构进行修改,读写自旋锁就允许多个内核控制路径同时读同一个数据结构。如果一个内核控制路径想要对这个结构进行写操作,那么它必须获取读写锁的写锁,写锁授权独占这个资源

    读写自旋锁核心在于防写不防读,多个读操作可以同时进行,但写操作只能独占资源。

    在这里插入图片描述

    顺序锁

    顺序锁与读写自旋锁非常相似,只是它为写者赋予了较高优先级,即使正在读的时候也允许写者继续运行

    这种策略的好处是写者永远不会等待(除非另外一个写者正在写),缺点是有些时候不得不反复多次读相同的数据直到它获得有效的副本。

    每个读者都必须在读数据前后两次读顺序计数器,并检查两次读到的值是否相同,如果不相同说明新的写者已经开始写并且增加了顺序计数器,暗示刚才读到的数据是无效的。

    注意:当读者进入临界区时,不必禁用内核抢占;另一方面,写者获取自旋锁,所以它进入临界区时自动禁用内核抢占。

    信号量

    和自旋锁一样,信号量也是保护临界资源的一种有用方法。信号量只有当得到信号量时,进程或者线程才能够进入临界区,执行临界代码(down等函数后面的代码块)。

    信号量与自旋锁的最大不同点在于,当一个进程试图去获取一个已经锁定的信号量时,该进程不会像自旋锁一样在自旋忙等待,而是会将自身加入一个等待队列中去睡眠,这时处理器能重获自由,去执行其他代码。直到其他进程释放信号量后,处于等待队列中的进程才会被唤醒。当进程唤醒之后,就立刻重新从睡眠的地方开始执行,又一次试图获得信号量,当获得信号量后,程序继续执行。

    所以,从信号量的原理上来说,没有获得信号量的函数可能睡眠。这就要求只有能够睡眠的进程才能够使用信号量,不能睡眠的进程不能使用信号量。例如中断处理程序中,由于中断需要立刻完成,所以不能睡眠,也就是说在中断处理程序中不能使用信号量

    有以下结论

    • 因为等待时会睡眠,所以信号量适用于锁被长时间持有的情况
    • 相反,锁被短时间持有的情况就不适宜使用信号量了,因为睡眠、维护等待队列、唤醒的开销可能比较大。
    • 因为等待时会睡眠,所以只能在进程上下文中获取信号量锁,不能在中断上下文中使用。
    • 在占用信号量的同时不能占用自旋锁,因为自旋锁是不允许睡眠的。

    计数信号量和二值信号量:

    信号量同时允许任意数量的锁持有者,具体数量可在声明信号量时指定。

    通常情况下,信号量一个时刻只允许有一个锁持有者,这时计数为1,这样的信号量称为二值信号量(或者称为互斥信号量)。

    另一方面也允许初始化计数为大于1的值,这种情况称为计数信号量,允许多个线程同时访问临界区,内核中使用它的机会不多。

    在使用信号量的时候,基本上用到的都是互斥信号量(二值信号量)。


    读写信号量

    与读写自旋锁类似,防写不防读,只对写者互斥,不对读者。并发持有读锁的读者数量不限,只有唯一的写者可以获得写锁。


    互斥锁

    互斥锁(也叫互斥体)就是互斥信号量,即一个时刻只允许有一个锁持有者的信号量。

    内核中优先使用互斥锁,一般不用普通的计数信号量(支持多个持有者)。

    读-拷贝-更新(RCU)

    RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用。RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数据的时候不对链表进行耗时的加锁操作。这样在同一时间可以有多个线程同时读取该链表,并且允许一个线程对链表进行修改(修改的时候,需要加锁)。RCU适用于需要频繁的读取数据,而相应修改数据并不多的情景,例如在文件系统中,经常需要查找定位目录,而对目录的修改相对来说并不多,这就是RCU发挥作用的最佳场景。

    读(Read):读者不需要获得任何锁就可访问RCU保护的临界区;

    拷贝(Copy):写者在访问临界区时,写者“自己”将先拷贝一个临界区副本,然后对副本进行修改;

    更新(Update):RCU机制将在在适当时机使用一个回调函数把指向原来临界区的指针重新指向新的被修改的临界区,锁机制中的垃圾收集器负责回调函数的调用。(时机:所有引用该共享临界区的CPU都退出对临界区的操作。即没有CPU再去操作这段被RCU保护的临界区后,这段临界区即可回收了,此时回调函数即被调用)

    在RCU的实现过程中,我们主要解决以下问题:

    1,在读取过程中,另外一个线程删除了一个节点。删除线程可以把这个节点从链表中移除,但它不能直接销毁这个节点,必须等到所有的读取线程读取完成以后,才进行销毁操作。RCU中把这个过程称为宽限期(Grace period)。

    2,在读取过程中,另外一个线程插入了一个新节点,而读线程读到了这个节点,那么需要保证读到的这个节点是完整的。这里涉及到了发布-订阅机制(Publish-Subscribe Mechanism)。

    3, 保证读取链表的完整性。新增或者删除一个节点,不至于导致遍历一个链表从中间断开。但是RCU并不保证一定能读到新增的节点或者不读到要被删除的节点。


    死锁

    什么是死锁

    所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。如下图所示:
    在这里插入图片描述

    产生死锁的原因

    可归结为如下两点:

    a. 竞争资源

    b. 进程间推进顺序非法

    • 例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁

    死锁产生的4个必要条件?

    1. 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
    2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
    4. 环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。

    进程死锁和线程死锁

    进程和线程都可以产生死锁,只要满足上述的四个必要条件。

    详见https://www.zhihu.com/question/263380756

    预防死锁

    预防死锁:

    • 资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
    • 只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
    • 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
    • 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

    1、以确定的顺序获得锁

    如果必须获取多个锁,那么在设计的时候需要充分考虑不同线程之前获得锁的顺序。按照上面的例子,两个线程获得锁的时序图如下:

    在这里插入图片描述

    如果此时把获得锁的时序改成:
    在这里插入图片描述


    那么死锁就永远不会发生。 针对两个特定的锁,开发者可以尝试按照锁对象的hashCode值大小的顺序,分别获得两个锁,这样锁总是会以特定相同的顺序获得锁,那么死锁也不会发生。问题变得更加复杂一些,如果此时有多个线程,都在竞争不同的锁,简单按照锁对象的hashCode进行排序(单纯按照hashCode顺序排序会出现“环路等待”),可能就无法满足要求了,这个时候开发者可以使用银行家算法,所有的锁都按照特定的顺序获取,同样可以防止死锁的发生,该算法在这里就不再赘述了,有兴趣的可以自行了解一下。

    2、超时放弃

    当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。 还是按照之前的例子,时序图如下:

    在这里插入图片描述

    避免死锁

    • 预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
    • 银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个能使所有进程都可顺利推进完的资源分配序列,即不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程直到同意该请求后系统状态仍然是安全的。

    检测死锁

    1. 首先为每个进程和每个资源指定一个唯一的号码;
    2. 然后建立资源分配表和进程等待表。

    解除死锁

    当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:

    • 剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
    • 撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。

    具体的三种方法:

    1.利用抢占恢复

    在某些情况下,可能会临时将某个资源从它的当前所有者那里转移到另一个进程。许多情况下,尤其是对运行在大型主机上的批处理操作系统来说,需要人工进行干预。

    比如,要将激光打印机从它的持有进程那里拿走,管理员可以收集已打印好的文档并将其堆积在一旁。然后,该进程被挂起(标记为不可运行)。接着,打印机被分配给另一个进程。当那个进程结束后,堆在一旁的文档再被重新放回原处,原进程可重新继续工作。

    在不通知原进程的情况下,将某一资源从一个进程强行取走给另一个进程使用,接着又送回,这种做法是否可行主要取决于该资源本身的特性。用这种方法恢复通常比较困难或者说不太可能。若选择挂起某个进程,则在很大程度上取决于哪一个进程拥有比较容易收回的资源。

    2.利用回滚恢复

    如果系统设计人员以及主机操作员了解到死锁有可能发生,他们就可以周期性地对进程进行检查点检查(checkpointed)。进程检查点检查就是将进程的状态写入一个文件以备以后重启。该检查点中不仅包括存储映像,还包括了资源状态,即哪些资源分配给了该进程。为了使这一过程更有效,新的检查点不应覆盖原有的文件,而应写到新文件中。这样,当进程执行时,将会有一系列的检查点文件被累积起来。

    假设,检测到进程1与进程2构成死锁.进程1需要的资源A此时被进程2占用.我们准备通过重新分配资源满足进程1,从而消除死锁.此时,我们需要检查进程2的检查点,将其恢复到某一检查点文件上, 在该检查点上,进程2尚未占用资源A. 通过对进程2进程复位,其在检查点后的所有工作将被清除.然后我们可以把资源A分配给进程1.如果复位后的进程2试图重新获得对该资源的控制,它必须等到该资源可用时为止, 至少需要在进程1执行完毕并释放该资源后.

    3.通过杀死进程恢复

    最直接也是最简单的解决死锁的方法是杀死一个或若干个进程。一种方法是杀掉环中的一个进程。如果走运的话,其他进程将可以继续。如果这样做行不通的话,就需要继续杀死别的进程直到打破死锁环。

    另一种方法是选一个环外的进程作为牺牲品以释放该进程的资源。在使用这种方法时,选择一个要被杀死的进程要特别小心,它应该正好持有环中某些进程所需的资源。比如,一个进程可能持有一台绘图仪而需要一台打印机,而另一个进程可能持有一台打印机而需要一台绘图仪,因而这两个进程是死锁的。第三个进程可能持有另一台同样的打印机和另一台同样的绘图仪而且正在运行着。杀死第三个进程将释放这些资源,从而打破前两个进程的死锁。

    有可能的话,最好杀死可以从头开始重新运行而且不会带来副作用的进程。比如,编译进程可以被重复运行,由于它只需要读入一个源文件和产生一个目标文件。如果将它中途杀死,它的第一次运行不会影响到第二次运行。

    另一方面,更新数据库的进程在第二次运行时并非总是安全的。如果一个进程将数据库的某个记录加1,那么运行它一次,将它杀死后,再次执行,就会对该记录加2,这显然是错误的。

    参考资料

    展开全文
  • Ubuntu Linux下用rsync进行数据备份和同步配制 参考的github工程 cwRsync提示password file must be owned by root when running as root的解决方法 有两台计算机均是ubuntu系统,但是其上有一些文件想要互相拷贝...

    参考

    Ubuntu Linux下用rsync进行数据备份和同步配制
    参考的github工程
    cwRsync提示password file must be owned by root when running as root的解决方法

    有两台计算机均是ubuntu系统,但是其上有一些文件想要互相拷贝,虽然有其他替代方案,但是作为一个练习,学习rsync的使用还是很好的。并且在实际应用中,可能也需要用到rsync来同步文件,比如可以用此作为ftp中重要文件的备份策略。
    这里仅尝试ubuntu系统下安装和测试可用性,并没有尝试windows系统。
    以下是根据网上的资料执行后确认可行的方案,备份并且标注一些可能的问题。

    背景

    • 服务器的数据是非常重要的,一旦发生磁盘损坏就麻烦了。作为一个管理人员,对数据进行实时地同步备份那是很有必要的。
    • 假如我的服务器A的ip为192.168.68.56, 我的备份客户机B的ip为192.168.68.61

    服务器端的配制:

    打开rsync作为服务器的开关

    修改文件/etc/default/rsync的内容其中一行如下
    RSYNC_ENABLE=true

    新建配制文件/etc/rsyncd.conf 内容如下

    #[globale]
    strict modes = yes
    #rsync default port
    port = 873
    logfile = /var/log/rsyncd.log
    pidfile = /var/run/rsyncd.pid
    max connections = 4
    auth users = backup, user
    secrets file = /etc/rsyncd.scrt
    
    #[modules] each path responding to a module
    [appbackup]
    path = /home/aborn/backup
    #hosts allow=9.4.122.24
    
    [databackup]
    path = /home/data

    说明:
    a) auth users 配制一定要和/etc/rsyncd.scrt里的用户名保持一致,但不一定是系统里的用户名,名字随便取,这里配置了两个用户,可以只配置一个,按需要配置即可

    b) 要备份的每个路径为一个module,这里有两个路径分别对应module为[appbackup]和[databackup]
    我使用的时候仅仅配置了一个module,具体module数量按照个人需求配置即可

    c) hosts allow指明允许的地址,可以指定唯一的ip,允许访问同步服务器

    d) 其余配置按需修改

    新建密码文件/etc/rsyncd.scrt 对应内容如下:

    backup:configurebackup@#$^&*()google
    user:passwordpassword

    这里有两个用户backup和user,冒号对应为密码,注意该文件属性为600 (其他用户没用读写执行权限)
    权限命令:

    sudo chmod 600 rsyncd.scrt

    如果用户不是root想修改为root:

    chown root  rsyncd.scrt

    开启备份服务

     sudo /etc/init.d/rsync start

    开户后可用

    netstat -tupln

    查看873端口有没有打开,若打开则成功

    客户端配制

    切换工作目录

    假如当前工作目录为~/backup

    创建密码文件

    在当前工作目录创建密码文件rsyncd.scrt,内容和服务器端保持一样,属性也为600

    创建配置文件client.conf

    在当前工作目录创建配制文件client.conf,内容如下:

    BACKUPPATH="/home/aborn/backup/";
    SERVERIP="192.168.68.56"
    MODULE="appbackup databackup"
    #OPTIONS="-vazu --progress --delete"
    OPTIONS="-vazu --progress"

    说明:
    其中BACKUPPATH为客户端数据存放路径
    SERVERIP为服务器A机器的IP地址
    MODULE为对应于服务端的/etc/rsyncd.conf下的module,多个module以空格分开

    创建运行备份脚本 rsyncclient.sh

    其内容如下

    #!/bin/bash
    ##################################################################
    # NAME
    #    rsyncclient.sh  ---- running in client machine, which
    #                      is used to backup data in client machine
    #
    # USAGE 
    #    ./rsyncclient.sh
    #
    # AUTHOR
    #  Aborn Jiang (aborn.jiang@gmail.com)
    #
    # NOTE    
    #  pls configure the file client.conf and rsyncd.scrt
    # 
    ##################################################################
    ABSPATH=$(dirname $0)
    source ${ABSPATH}/client.conf
    
    function get-user-pwd()
    {
    # obtain usrname and password
        iUSR=$(cat ${ABSPATH}/rsyncd.scrt|tr -d ' ' |grep  -v "^$" | \
            grep -v "^#"|head -n 1|awk -F : '{print $1}')
        iPWD=$(cat ${ABSPATH}/rsyncd.scrt|tr -d ' ' |grep  -v "^$" | \
            grep -v "^#"|head -n 1|awk -F : '{print $2}')
        if [ -z ${iUSR} ] || [ -z ${iPWD} ];then
            echo "iUSR=$iUSR  iPWD=$iPWD" 
            echo "rsyncd.scrt format illegal, please check!";
            exit -1;
        fi
    
    # produce password file
        echo "$iPWD" > ${ABSPATH}/.pass
        chmod 600 ${ABSPATH}/.pass
        [ ! -d $BACKUPPATH ] && mkdir -p ${BACKUPPATH}
    }
    
    function backup-module()
    {
    # print key information
        iModule=$1
        echo
        echo "---------------------------------------------------"
        echo "---- backup module ${iModule}@${SERVERIP} begin "
        echo "---- TIME=`date`----"
        echo "ABSPATH=${ABSPATH}"  
        echo "BACKUPPATH=${BACKUPPATH}"
        echo "iUSR=$iUSR  iPWD=$iPWD" 
        echo "OPTIONS=${OPTIONS}"
        iModuleBackpath=${BACKUPPATH}/${iModule};
        [ ! -d ${iModuleBackpath} ] && mkdir -p ${iModuleBackpath}
    
    # begin backup
        rsync  ${OPTIONS}  ${iUSR}@${SERVERIP}::${iModule}  ${iModuleBackpath} \
            --password-file=${ABSPATH}/.pass
        if [ $? != 0 ];then
            echo "---- backup module ${iModule}@${SERVERIP} failed."
        else
            echo "---- backup module ${iModule}@${SERVERIP} succuess. "
        fi
        echo "---- TIME=`date`----"
        echo "---------------------------------------------------"
        echo 
    }
    
    function __main__()
    {
        get-user-pwd
        for md in $MODULE
        do
            backup-module $md
        done
    }
    
    __main__

    如果有异常

    1. 如果提示连接超时,请检查服务端防火墙配置
      开启873端口
    sudo ufw allow 873

    或者允许来自192.168.68.61的所有请求

    sudo ufw allow from 192.168.68.61

    查看自己的防火墙配置

    sudo ufw status

    删除某个防火墙策略

    sudo ufw delete allow 873
    1. 如果提示密码用户不对,请修改.pass/rsyncd.scrt文件的权限和用户
      权限命令:
    sudo chmod 600 rsyncd.scrt

    如果用户不是root想修改为root:

    chown root  rsyncd.scrt

    .pass应该是自动生成的文件,执行的时候发现它所属的用户不是root,改成root后执行同步成功。

    展开全文
  • Linux下指定数据库数据配置主主同步的实例,有需要的朋友可以参考下一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例...

    Linux下指定数据库数据配置主主同步的实例,有需要的朋友可以参考下

    一、 概念:① 数据库同步  (主从同步 --- 主数据库写的同时 往从服务器写数据)

    ② 数据库同步  (主主同步 --- 两台数据库服务器互相写数据)

    二、 举例

    主主数据库同步服务器配置

    数据库服务器(A) 主数据库   IP:192.168.1.134

    数据库服务器(B) 主数据库   IP:192.168.1.138

    两台服务器同步的用户名为: bravedu    密码: brave123

    一、主数据库操作设置(A):

    ① 创建同步用户名   允许连接的 用户IP地址  (非本机IP)

    grant replication slave on *.* to 'bravedu'@'192.168.1.%' identified by 'brave123';

    flush privileges;

    ② 更改mysql配置文件

    [mysqld]

    server-id = 1

    log-bin=/www/mysql/binlog/binlog (路径要根据自己的安装设置)

    binlog-do-db = dbname (要同步的数据库名)

    binlog-ignore-db=mysql

    #相对应主从数据库同步不同的地方

    log-slave-updates

    sync_binlog=1

    auto_increment_offset=1

    auto_increment_increment=2

    replicate-do-db = dbname

    replicate-ignore-db = mysql,information_schema

    重启mysql服务器

    ③ 查看主数据库同步状态  IP: ***.134

    mysql>flush tables with read lock;

    mysql>show master status\G

    *************************** 1. row ***************************

    File: mysql-bin.000001    (这里注意 设置从服务器的时候要用)

    Position: 106             (这里注意设置从服务器的时候要用)

    Binlog_Do_DB: dbname

    Binlog_Ignore_DB: mysql

    1 row in set (0.00 sec)

    mysql>unlock tables;

    *****主服务器到目前位置设置完毕*******

    二、从数据库操作设置(B):

    ① 创建同步用户名

    grant replication slave on *.* to 'bravedu'@'192.168.1.%' identified by 'brave123';

    flush privileges;

    ② 更改mysql配置文件

    [mysqld]

    server-id = 2

    log-bin=/www/mysql/binlog/binlog (路径要根据自己的安装设置)

    binlog-do-db = dbname (要同步的数据库名)

    binlog-ignore-db= mysql,information_schema

    #相对于主从同步不同的地方

    binlog-do-db = dbname

    binlog-ignore-db=mysql

    log-slave-updates

    sync_binlog=1

    auto_increment_offset=2

    auto_increment_increment=2

    重启mysql服务器

    查看主数据库同步状态  IP: ***.138

    mysql>flush tables with read lock;

    mysql>show master status\G

    *************************** 1. row ***************************

    File: mysql-bin.000005    (这里注意 设置从服务器的时候要用)

    Position: 106             (这里注意设置从服务器的时候要用)

    Binlog_Do_DB: dbname

    Binlog_Ignore_DB: mysql

    1 row in set (0.00 sec)

    mysql>unlock tables;

    ③  指定主主数据库服务器同步指令

    注:IP为主服务器的IP,用户名,密码,log_file,log_post 服务器互相统一

    可能这块操作 需要先  解除锁表、停止数据库状态、在运行后 在启动状态

    mysql > stop  slave;

    #设置192.168.1.138数据库服务器配置 那么host 配置文件信息 就是 134的信息

    mysql > change master to master_host='192.168.1.134', master_user='bravedu', master_password='brave123', master_log_file='mysql-bin.000005', master_log_pos=106;

    #设置192.168.1.134 数据库服务器配置 那么host 等配置文件信息 就是 134的信息

    mysql > change master to master_host='192.168.1.138', master_user='bravedu', master_password='brave123', master_log_file='mysql-bin.000001', master_log_pos=106;

    mysql > start slave;

    mysql > unlock tables;

    ④ 查看主数据库同步状态  会出来很多信息 但是主要看这两个状态就行了 如果都是 yes 就可以了

    mysql>show slave status\G;

    Slave_IO_Running: Yes

    Slave_SQL_Running: Yes

    至此,主主数据库同步成功配置完成。

    展开全文
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:192.168.1....
  • 一、 概念: ① 数据库同步 (主从同步 — 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 — 两台数据库服务器互相数据) 二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:...
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:192.168.1....
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:192.168.1....
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:192.168.1....
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例主主数据库同步服务器配置数据库服务器(A) 主数据库 IP:192.168.1....
  • 线程同步和生活中的同步概念是有差异的,日常中同步最多的是表示同时的意思,在程序中“同” 指的应该是协同、协助、互相配合。 主旨在协同步调,按预定的先后次序运行。同步其实是协同,按预定的先后次序运行。线程...
  • 官网开源地址 ... 官网配置手册 ... 注意:Lsyncd存在数据被替换风险,操作前请做好...Lsyncd 是一个简单高效的文件同步工具,通过lua语言封装了 inotify 和 rsync 工具,采用了 Linux 内核(2.6.13 及以后)里的 inoti...
  • 通过lsyncd 设置两个linux(centOS7)服务器之间的目录文件互相实时同步 官网开源地址 https://github.com/axkibe/lsyncd 官网配置手册 https://axkibe.github.io/lsyncd/ 注意:Lsyncd存在数据被替换风险,操作前请...
  • 两台(多台)linux服务器,互相联通,最好可以通过内网访问。这里以两台服务器内网访问为例,A服务器ip:192.168.0.100,B服务器ip:192.168.0.101 需要同步的文件夹,例如:A服务器:/www/A/upload B服务器/...
  • 数据同步到其它服务器这里使用Linux同步文件工具rsync来进行文件的同步 rsync rsync是类unix系统下的数据镜像备份工具——remote sync。一款快速增量备份工具 Remote Sync,远程同步 支持本地复制,或者与其他...
  • 学习实现两台机器实现主主热备(数据库互相同步) 说明: ​ 使用数据库:MySQL-5.1.73 ​ 操作系统:CenTOS6.5 64位 ​ 两台机器注意操作系统和mysql版本都要一致 环境 节点A:192.168.1.10 节点B:192....
  • linux线程与进程同步锁机制 同步概念 所谓同步,即同时起步,协调一致。 而,编程中、通信中所说的同步与生活中大家印象中的同步概念略有差异。“同”字应是指协同、协助、互相配合。主旨在协同步调,按预定的先后...
  • 同步”的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信号间等等都需要同步机制。 因此,所有“多个控制流,共同操作一个共享资源”的情况,都需要同步数据混乱...
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例数据库服务器(A) 主数据库 IP:192.168.1.134数据库服务器(B) 主...
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例数据库服务器(A) 主数据库 IP:192.168.1.134数据库服务器(B) 主...
  • 一、 概念: ① 数据库同步 (主从同步 — 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 — 两台数据库服务器互相数据) 二、 举例数据库服务器(A) 主数据库 IP:192.168.1.134数据库服务器...
  • 而在编写代码中,通信中所说的同步与生活中大家说的同步有些差异,“同”表示协同,协助,互相配合。主旨在于协同步调,按预定的先后次序运行。 二.线程同步 线程同步说白就是 多个线程共同协调的 操作一个或者...
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例数据库服务器(A) 主数据库 IP:192.168.1.134数据库服务器(B) 主...
  • 如果有多个执行线程(指任何正在执行的代码实例,比如一个在内核执行的进程,一个中断处理程序,或一个内核线程)同时访问和操作共享的数据,就有可能造成进程之间互相覆盖共享数据,造成被访问数据处于不一致的情况。...
  • 一、 概念:① 数据库同步 (主从同步 --- 主数据库写的同时 往从服务器写数据)② 数据库同步 (主主同步 --- 两台数据库服务器互相数据)二、 举例数据库服务器(A) 主数据库 IP:192.168.1.134数据库服务器(B) 主...
  • 帧封装,传输,同步 差错校验 地址 MAC地址 媒体访问控制地址 结构48位2进制数字12位16进制数字 帧封装 目标MAC//源MAC//数据//CRC 交换机工作原理 学习/广播/转发/更新 组建局域网 1 安装cisco cpt软件 2 部署...
  • 同步对象是内存中的变量,可以按照与访问数据完全相同的方式对其进行访问。不同进程 中的线程可以通过放在由线程控制的共享内存中的同步对象互相通信。尽管不同进程中的 线程通常互不可见,但这些线程仍可以互相...

空空如也

空空如也

1 2 3 4 5
收藏数 87
精华内容 34
关键字:

linux互相同步数据

linux 订阅