进程谈通讯,线程谈同步!

术语

进程、线程、共享锁、排他锁、记录锁、原子操作…

文件锁

参考

术语介绍

LINUX锁包括“建议性锁(advisory lock)”和“强制性锁mandatory lock”。

Linux 中还引入了两种强制锁的变种形式:共享模式强制锁(share-mode mandatory lock)和租借锁(lease)。

根据细粒度锁可以分为 文件锁记录锁

记录锁根据访问形式的不同可以分为读锁和写锁。

其中读锁允许多个进程同时进行操作,称为共享锁。

写锁的主要目的是隔离文件使所写内容不被其他进程的读写干扰,以保证数据的完整性,称为排他锁。

Linux 系统的文件记录锁默认情况下是建议性的!建议性锁要求每个上锁的文件的进程都要检查是否有锁存在,并且尊重已有的锁。

共享模式锁

Linux 中还引入了两种特殊的文件锁:共享模式强制锁和租借锁。这两种文件锁可以被看成是强制锁的两种变种形式。共享模式强制锁可以用于某些私有网络文件系统,如果某个文件被加上了共享模式强制锁,那么其他进程打开该文件的时候不能与该文件的共享模式强制锁所设置的访问模式相冲突。但是由于可移植性不好,因此并不建议使用这种锁。

租借锁

采用强制锁之后,如果一个进程对某个文件拥有写锁,只要它不释放这个锁,就会导致访问该文件的其他进程全部被阻塞或不断失败重试;即使该进程只拥有读锁,也会造成后续更新该文件的进程的阻塞。为了解决这个问题,Linux 中采用了一种新型的租借锁。

当进程尝试打开一个被租借锁保护的文件时,该进程会被阻塞,同时,在一定时间内拥有该文件租借锁的进程会收到一个信号。收到信号之后,拥有该文件租借锁的进程会首先更新文件,从而保证了文件内容的一致性,接着,该进程释放这个租借锁。如果拥有租借锁的进程在一定的时间间隔内没有完成工作,内核就会自动删除这个租借锁或者将该锁进行降级,从而允许被阻塞的进程继续工作。

系统默认的这段间隔时间是 45 秒钟,定义如下:

	`137 int lease_break_time = 45;` 这个参数可以通过修改 /proc/sys/fs/lease-break-time 进行调节(当然,/proc/sys/fs/leases-enable 必须为 1 才行)。

注意

  1. 在fork 产生子进程后,一般会调用 execve 函数族在子进程中执行新的程序。如果在调用 execve 之前,

子进程中某些打开的文件描述符已经持有了文件锁,那么在执行execve 时,如果没有设置 close-on-exec 

标志,那么在新的程序中,原本打开的文件描述符依然会保持打开,原本持有的文件锁还会继续持有。

  1. 调用 dup 后,两个描述符指向了相同的文件表项,而flock 的文件锁是加在了文件表项上,因而如果对

fd0 加锁那么 fd1 就会自动持有同一把锁,释放锁时,可以使用这两个描述符中的任意一个。

线程锁(同步)

线程同步的方式:互斥量、读写锁、条件变量、自旋锁、屏障

互斥量

Linux使用互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。 使用互斥锁(互斥)可以使线程按顺序执行。通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程。互斥锁还可以保护单线程代码。

当对互斥量进行加锁时,改互斥量已经上锁。那么就会导致该进程的阻塞,也就是说同一个线程对同一个互斥量进行第二次加锁就会导致死锁的情况出现。

如果不希望阻塞发生就要使用 pthread_mutex_trylock 进行加锁,如果没锁则会上锁,如果有锁则会直接返回 0 而不会阻塞。

读写锁

读写锁也成为共享互斥锁(shared-exclusice lock)。当读锁的时候就是共享锁,当写锁的时候就是互斥锁。
概念和文件的共享锁与排他锁基本一样。

条件变量

条件变量与互斥量一起使用时,可允许线程以无竞争的方式等待特殊条件。

自旋锁

自旋锁不是通过睡眠等待而是通过轮询等待,这样的话如果在短时间内获得锁,就可以省去调度的成本。
但是在用户层线程取消调度,那么持有自旋锁的线程还是会进入睡眠。

屏障

协调多个线程并行工作的同步机制,屏障允许每个线程等待,直到所有的合作线程都到达某一个点。

线程锁(同步)