📄 os_unix.c
字号:
/*** 2004 May 22**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.************************************************************************************ This file contains code that is specific to Unix systems.**** $Id: os_unix.c,v 1.198 2008/08/22 18:41:37 aswift Exp $*/#include "sqliteInt.h"#if SQLITE_OS_UNIX /* This file is used on unix only *//*** If SQLITE_ENABLE_LOCKING_STYLE is defined, then several different ** locking implementations are provided:**** * POSIX locking (the default),** * No locking,** * Dot-file locking,** * flock() locking,** * AFP locking (OSX only).*//* #define SQLITE_ENABLE_LOCKING_STYLE 0 *//*** These #defines should enable >2GB file support on Posix if the** underlying operating system supports it. If the OS lacks** large file support, these should be no-ops.**** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch** on the compiler command line. This is necessary if you are compiling** on a recent machine (ex: RedHat 7.2) but you want your code to work** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2** without this option, LFS is enable. But LFS does not exist in the kernel** in RedHat 6.0, so the code won't work. Hence, for maximum binary** portability you should omit LFS.*/#ifndef SQLITE_DISABLE_LFS# define _LARGE_FILE 1# ifndef _FILE_OFFSET_BITS# define _FILE_OFFSET_BITS 64# endif# define _LARGEFILE_SOURCE 1#endif/*** standard include files.*/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <time.h>#include <sys/time.h>#include <errno.h>#ifdef SQLITE_ENABLE_LOCKING_STYLE#include <sys/ioctl.h>#include <sys/param.h>#include <sys/mount.h>#endif /* SQLITE_ENABLE_LOCKING_STYLE *//*** If we are to be thread-safe, include the pthreads header and define** the SQLITE_UNIX_THREADS macro.*/#if SQLITE_THREADSAFE# include <pthread.h># define SQLITE_UNIX_THREADS 1#endif/*** Default permissions when creating a new file*/#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644#endif/*** Maximum supported path-length.*/#define MAX_PATHNAME 512/*** The unixFile structure is subclass of sqlite3_file specific for the unix** protability layer.*/typedef struct unixFile unixFile;struct unixFile { sqlite3_io_methods const *pMethod; /* Always the first entry */#ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that ** it is larger than the struct CrashFile defined in test6.c. */ char aPadding[32];#endif struct openCnt *pOpen; /* Info about all open fd's on this inode */ struct lockInfo *pLock; /* Info about locks on this inode */#ifdef SQLITE_ENABLE_LOCKING_STYLE void *lockingContext; /* Locking style specific state */#endif int h; /* The file descriptor */ unsigned char locktype; /* The type of lock held on this fd */ int dirfd; /* File descriptor for the directory */#if SQLITE_THREADSAFE pthread_t tid; /* The thread that "owns" this unixFile */#endif int lastErrno; /* The unix errno from the last I/O error */};/*** Include code that is common to all os_*.c files*/#include "os_common.h"/*** Define various macros that are missing from some systems.*/#ifndef O_LARGEFILE# define O_LARGEFILE 0#endif#ifdef SQLITE_DISABLE_LFS# undef O_LARGEFILE# define O_LARGEFILE 0#endif#ifndef O_NOFOLLOW# define O_NOFOLLOW 0#endif#ifndef O_BINARY# define O_BINARY 0#endif/*** The DJGPP compiler environment looks mostly like Unix, but it** lacks the fcntl() system call. So redefine fcntl() to be something** that always succeeds. This means that locking does not occur under** DJGPP. But it is DOS - what did you expect?*/#ifdef __DJGPP__# define fcntl(A,B,C) 0#endif/*** The threadid macro resolves to the thread-id or to 0. Used for** testing and debugging only.*/#if SQLITE_THREADSAFE#define threadid pthread_self()#else#define threadid 0#endif/*** Set or check the unixFile.tid field. This field is set when an unixFile** is first opened. All subsequent uses of the unixFile verify that the** same thread is operating on the unixFile. Some operating systems do** not allow locks to be overridden by other threads and that restriction** means that sqlite3* database handles cannot be moved from one thread** to another. This logic makes sure a user does not try to do that** by mistake.**** Version 3.3.1 (2006-01-15): unixFile can be moved from one thread to** another as long as we are running on a system that supports threads** overriding each others locks (which now the most common behavior)** or if no locks are held. But the unixFile.pLock field needs to be** recomputed because its key includes the thread-id. See the ** transferOwnership() function below for additional information*/#if SQLITE_THREADSAFE# define SET_THREADID(X) (X)->tid = pthread_self()# define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \ !pthread_equal((X)->tid, pthread_self()))#else# define SET_THREADID(X)# define CHECK_THREADID(X) 0#endif/*** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996)** section 6.5.2.2 lines 483 through 490 specify that when a process** sets or clears a lock, that operation overrides any prior locks set** by the same process. It does not explicitly say so, but this implies** that it overrides locks set by the same process using a different** file descriptor. Consider this test case:** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);**** Suppose ./file1 and ./file2 are really the same file (because** one is a hard or symbolic link to the other) then if you set** an exclusive lock on fd1, then try to get an exclusive lock** on fd2, it works. I would have expected the second lock to** fail since there was already a lock on the file due to fd1.** But not so. Since both locks came from the same process, the** second overrides the first, even though they were on different** file descriptors opened on different file names.**** Bummer. If you ask me, this is broken. Badly broken. It means** that we cannot use POSIX locks to synchronize file access among** competing threads of the same process. POSIX locks will work fine** to synchronize access for threads in separate processes, but not** threads within the same process.**** To work around the problem, SQLite has to manage file locks internally** on its own. Whenever a new database is opened, we have to find the** specific inode of the database file (the inode is determined by the** st_dev and st_ino fields of the stat structure that fstat() fills in)** and check for locks already existing on that inode. When locks are** created or removed, we have to look at our own internal record of the** locks to see if another thread has previously set a lock on that same** inode.**** The sqlite3_file structure for POSIX is no longer just an integer file** descriptor. It is now a structure that holds the integer file** descriptor and a pointer to a structure that describes the internal** locks on the corresponding inode. There is one locking structure** per inode, so if the same inode is opened twice, both unixFile structures** point to the same locking structure. The locking structure keeps** a reference count (so we will know when to delete it) and a "cnt"** field that tells us its internal lock status. cnt==0 means the** file is unlocked. cnt==-1 means the file has an exclusive lock.** cnt>0 means there are cnt shared locks on the file.**** Any attempt to lock or unlock a file first checks the locking** structure. The fcntl() system call is only invoked to set a ** POSIX lock if the internal lock structure transitions between** a locked and an unlocked state.**** 2004-Jan-11:** More recent discoveries about POSIX advisory locks. (The more** I discover, the more I realize the a POSIX advisory locks are** an abomination.)**** If you close a file descriptor that points to a file that has locks,** all locks on that file that are owned by the current process are** released. To work around this problem, each unixFile structure contains** a pointer to an openCnt structure. There is one openCnt structure** per open inode, which means that multiple unixFile can point to a single** openCnt. When an attempt is made to close an unixFile, if there are** other unixFile open on the same inode that are holding locks, the call** to close() the file descriptor is deferred until all of the locks clear.** The openCnt structure keeps a list of file descriptors that need to** be closed and that list is walked (and cleared) when the last lock** clears.**** First, under Linux threads, because each thread has a separate** process ID, lock operations in one thread do not override locks** to the same file in other threads. Linux threads behave like** separate processes in this respect. But, if you close a file** descriptor in linux threads, all locks are cleared, even locks** on other threads and even though the other threads have different** process IDs. Linux threads is inconsistent in this respect.** (I'm beginning to think that linux threads is an abomination too.)** The consequence of this all is that the hash table for the lockInfo** structure has to include the process id as part of its key because** locks in different threads are treated as distinct. But the ** openCnt structure should not include the process id in its** key because close() clears lock on all threads, not just the current** thread. Were it not for this goofiness in linux threads, we could** combine the lockInfo and openCnt structures into a single structure.**** 2004-Jun-28:** On some versions of linux, threads can override each others locks.** On others not. Sometimes you can change the behavior on the same** system by setting the LD_ASSUME_KERNEL environment variable. The** POSIX standard is silent as to which behavior is correct, as far** as I can tell, so other versions of unix might show the same** inconsistency. There is no little doubt in my mind that posix** advisory locks and linux threads are profoundly broken.**** To work around the inconsistencies, we have to test at runtime ** whether or not threads can override each others locks. This test** is run once, the first time any lock is attempted. A static ** variable is set to record the results of this test for future** use.*//*** An instance of the following structure serves as the key used** to locate a particular lockInfo structure given its inode.**** If threads cannot override each others locks, then we set the** lockKey.tid field to the thread ID. If threads can override** each others locks then tid is always set to zero. tid is omitted** if we compile without threading support.*/struct lockKey { dev_t dev; /* Device number */ ino_t ino; /* Inode number */#if SQLITE_THREADSAFE pthread_t tid; /* Thread ID or zero if threads can override each other */#endif};/*** An instance of the following structure is allocated for each open** inode on each thread with a different process ID. (Threads have** different process IDs on linux, but not on most other unixes.)**** A single inode can have multiple file descriptors, so each unixFile** structure contains a pointer to an instance of this object and this** object keeps a count of the number of unixFile pointing to it.*/struct lockInfo { struct lockKey key; /* The lookup key */ int cnt; /* Number of SHARED locks held */ int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ int nRef; /* Number of pointers to this structure */ struct lockInfo *pNext, *pPrev; /* List of all lockInfo objects */};/*** An instance of the following structure serves as the key used** to locate a particular openCnt structure given its inode. This** is the same as the lockKey except that the thread ID is omitted.*/struct openKey { dev_t dev; /* Device number */ ino_t ino; /* Inode number */};/*** An instance of the following structure is allocated for each open** inode. This structure keeps track of the number of locks on that** inode. If a close is attempted against an inode that is holding** locks, the close is deferred until all locks clear by adding the** file descriptor to be closed to the pending list.*/struct openCnt { struct openKey key; /* The lookup key */ int nRef; /* Number of pointers to this structure */ int nLock; /* Number of outstanding locks */ int nPending; /* Number of pending close() operations */ int *aPending; /* Malloced space holding fd's awaiting a close() */ struct openCnt *pNext, *pPrev; /* List of all openCnt objects */};/*** List of all lockInfo and openCnt objects. This used to be a hash** table. But the number of objects is rarely more than a dozen and** never exceeds a few thousand. And lookup is not on a critical** path oo a simple linked list will suffice.*/static struct lockInfo *lockList = 0;static struct openCnt *openList = 0;/*** The locking styles are associated with the different file locking** capabilities supported by different file systems. **** POSIX locking style fully supports shared and exclusive byte-range locks ** AFP locking only supports exclusive byte-range locks** FLOCK only supports a single file-global exclusive lock** DOTLOCK isn't a true locking style, it refers to the use of a special** file named the same as the database file with a '.lock' extension, this** can be used on file systems that do not offer any reliable file locking** NO locking means that no locking will be attempted, this is only used for** read-only file systems currently** UNSUPPORTED means that no locking will be attempted, this is only used for** file systems that are known to be unsupported*/#define LOCKING_STYLE_POSIX 1#define LOCKING_STYLE_NONE 2#define LOCKING_STYLE_DOTFILE 3#define LOCKING_STYLE_FLOCK 4#define LOCKING_STYLE_AFP 5/*** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK*/#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))/*** Helper functions to obtain and relinquish the global mutex.*/static void enterMutex(){ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));}static void leaveMutex(){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));}#if SQLITE_THREADSAFE/*** This variable records whether or not threads can override each others** locks.**** 0: No. Threads cannot override each others locks.** 1: Yes. Threads can override each others locks.** -1: We don't know yet.**** On some systems, we know at compile-time if threads can override each** others locks. On those systems, the SQLITE_THREAD_OVERRIDE_LOCK macro** will be set appropriately. On other systems, we have to check at** runtime. On these latter systems, SQLTIE_THREAD_OVERRIDE_LOCK is** undefined.**** This variable normally has file scope only. But during testing, we make** it a global so that the test code can change its value in order to verify** that the right stuff happens in either case.*/#ifndef SQLITE_THREAD_OVERRIDE_LOCK# define SQLITE_THREAD_OVERRIDE_LOCK -1#endif#ifdef SQLITE_TESTint threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK;#elsestatic int threadsOverrideEachOthersLocks = SQLITE_THREAD_OVERRIDE_LOCK;#endif/*** This structure holds information passed into individual test** threads by the testThreadLockingBehavior() routine.*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -