æ•°æ®ç®¡ç†2(Beginning Linux Programming 笔记11)
p297 文件é”定
有两ç§ç±»åž‹çš„æ–‡ä»¶é”定,最常è§çš„æ˜¯éžå†…æ ¸å®žçŽ°çš„åŠå‘Šå¼é”定(advisory locking),其方法是所有进程éµå¾ªä¸€ä¸ªçº¦å®šï¼Œä¹Ÿå°±æ˜¯ä¸‹é¢è¯´çš„æ–‡ä»¶é”。å¦å¤–ä¸€ç§æ˜¯å¼ºåˆ¶é”(mandatory locking)ï¼Œå®ƒæ˜¯ç”±å†…æ ¸å¼ºåˆ¶å®žæ–½çš„ï¼Œå½“ä¸€ä¸ªé”è¢«åˆ›å»ºæ—¶ï¼Œå…¶ä»–è¿›ç¨‹å¯¹è¢«é”æ–‡ä»¶è¿›è¡Œè¯»å†™æ“作时会被挂起,椅ååŽåˆ°é”å®šé‡Šæ”¾ï¼Œå› ä¸ºé”定在 read å’Œ write 上,会é™ä½Žç³»ç»Ÿè°ƒç”¨çš„æ€§èƒ½ã€‚
文件é”定对多用户多任务æ“作系统是éžå¸¸é‡è¦çš„,进程之间ç»å¸¸éœ€è¦é€šè¿‡æ–‡ä»¶æ¥å…±äº«æ•°æ®ã€‚linux 下的文件é”ä¸ä»…ä»…å¯ä»¥é€šè¿‡åˆ›å»ºé”定文件(lock file)çš„åŽŸåæ“ä½œæ¥ä¿è¯æ“作唯一性,还能针对文件的æŸä¸€éƒ¨åˆ†åšæŽ’ä»–æ€§è®¿é—®çš„é”æ“作。有一ç§è¯´æ³•,分别称这俩ç§é”为文件é”和记录é”(record lock)。
如果程åºéœ€è¦æŽ’他性的访问æŸä¸ªèµ„æºï¼Œå¯ä»¥é€šè¿‡å…ˆåˆ›å»ºä¸€ä¸ªçº¦å®šçš„æ–‡ä»¶é”的方å¼ï¼Œçº¦å®šå…¶ä»–访问该资æºçš„程åºéƒ½å…ˆæ£€æŸ¥è¯¥æ–‡ä»¶æ˜¯å¦å˜åœ¨ã€‚处ç†å®ŒæŸä¸ªèµ„æºåŽï¼ŒæŠŠè¯¥é”文件 unlink ,以便其他进程访问该资æºã€‚linux 系统一般把文件é”创建在 /var/spool 下。使用 open åˆ›å»ºæ–‡ä»¶é”æ—¶ï¼Œéœ€è¦ä½¿ç”¨ O_CREAT å’Œ O_EXCL æ ‡è¯†ï¼Œè¿™æ ·æ‰èƒ½ä»¥åŽŸåæ“ä½œçš„æ–¹å¼ä¿è¯åˆ›å»ºæœ‰æ•ˆé”,下é¢çš„程åºä¸ï¼Œåªåˆ›å»ºäº†é”,没有释放é”:
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int file_desc;
int save_errno;
file_desc = open("/tmp/LCK.test", O_RDWR | O_CREAT | O_EXCL, 0444);
if (file_desc == -1) {
save_errno = errno;
printf("Open failed with error %d\n", save_errno);
}
else {
printf("Open succeeded\n");
}
exit(EXIT_SUCCESS);
}
linux 下第二次执行上é¢çš„程åºï¼Œä¼šè¿”回错误ç 为 17 ï¼Œæ˜¯å› ä¸ºæ–‡ä»¶å˜åœ¨çš„错误ç EEXIST 的值被定义为 17 ï¼Œè¿™ä¹Ÿæ£æ˜¯ç”±äºŽæŒ‡å®šäº† O_CREAT | O_EXCL 。一ç§å¸¸è§åˆ›å»ºæ–‡ä»¶é”的方法是把å‘èµ·é”的进程的 id 写到文件ä¸ï¼Œè¿™æ ·å¯ä»¥é€šè¿‡æ£€æŸ¥è¿›ç¨‹ id 是å¦å˜åœ¨ï¼Œé˜²æ¢é”被创建以åŽï¼Œåœ¨åˆ 除文件é”之å‰ç¨‹åºæ„外终æ¢å¯¼è‡´æ–‡ä»¶ç一直å˜åœ¨ã€‚
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
const char *lock_file = "/tmp/LCK.test2";
int main() {
int file_desc;
int tries = 10;
while (tries--) {
file_desc = open(lock_file, O_RDWR | O_CREAT | O_EXCL, 0444);
if (file_desc == -1) {
printf("%d - Lock already present\n", getpid());
sleep(3);
}
else {
/* critical region */
printf("%d - I have exclusive access\n", getpid());
sleep(1);
(void)close(file_desc);
(void)unlink(lock_file);
/* non-critical region */
sleep(2);
}
} /* while */
exit(EXIT_SUCCESS);
}
p301 局部é”(locking regions)
PS: 这里很多笔记都æ¥è‡ª<
就是别的书上说的记录é”。需è¦è®¿é—®ä¸€ä¸ªå¤§çš„共享文件时,文件é”就显得局é™äº†ã€‚linux å…许é”定文件的æŸäº›éƒ¨åˆ†(regions),其他进程å¯ä»¥è®¿é—®æ–‡ä»¶çš„其他没有é”定的部分。记录é”å¯ä»¥é”定文件的任æ„部分,比如进程 A å¯ä»¥é”定一个文件从 50 å—节 200 å—节的部分,å¦ä¸€ä¸ªè¿›ç¨‹ B 则å¯ä»¥é”定 300 å—节到 350 å—节的部分,这两个é”之间并ä¸å†²çªã€‚记录é”çš„å¦å¤–ä¸€ä¸ªä¼˜ç‚¹æ˜¯è¢«å†…æ ¸ä¿å˜ï¼Œè€Œä¸æ˜¯æ–‡ä»¶ç³»ç»Ÿå®žçŽ°çš„ã€‚å½“ä¸€ä¸ªè¿›ç¨‹ç»ˆæ¢åŽï¼Œå®ƒæ‰€ä¿å˜çš„æ‰€æœ‰é”都会被自动释放。这ç§é”也是åŠå‘Šå¼çš„。linux 有æè®°å½•é”的强制å¼å˜ä½“ï¼Œä½†æ˜¯ä½¿ç”¨ä¸æ˜¯å¾ˆæ–¹ä¾¿ã€‚
è®°å½•é”æœ‰ä¸¤ç§ç±»åž‹ï¼Œè¯»é”和写é”,读é”实际上是共享é”ï¼Œå› ä¸ºå¤šä¸ªè¿›ç¨‹å¯ä»¥åŒæ—¶å¯¹ä¸€ä¸ªåŒºåŸŸä¿æŒè¯»é”。但是对一个记录åªèƒ½æœ‰ä¸€ä¸ªè¿›ç¨‹ä¿æŒå†™é”,写é”必须的胡斥的。进程在需è¦å†™æ–‡ä»¶æ—¶ï¼Œå¿…须得到一个写é”,写é”å˜åœ¨æ—¶ï¼Œè¿™ä¸ªè®°å½•应该也应该没有读é”å˜åœ¨ã€‚ä¿è¯è¯»å†™ä¸å†²çªã€‚
è‡³å°‘æœ‰ä¸¤ç§æ–¹æ³•能实现局部é”: fcntl å’Œ lockf ,下é¢ä¸»è¦çœ‹ç”¨ fcntl æ¥å®žçŽ°çš„æ–¹å¼ï¼Œæ³¨æ„两者虽然比较相似,但是ä¸èƒ½åŒæ—¶å¥æ•ˆï¼Œå¯¹åŒä¸€ä¸ªæ–‡ä»¶ï¼Œä¸èƒ½æ··åˆä½¿ç”¨ä¸¤ç§é”定方法。
int fcntl(int fildes, int command, struct flock *flock_structure);
ç¬¬ä¸€ä¸ªå‚æ•°ä¸ºéœ€è¦æ“作的文件 fd , ç¬¬äºŒä¸ªå‚æ•°ä¸ºéœ€è¦æ‰§è¡Œä»»åŠ¡æˆ–è€…è¯´å‘½ä»¤ï¼š F_GETLK, F_SETLK, F_SETLKW åŽé¢åˆ†åˆ«è¯¦ç»†ä»‹ç»ã€‚ç¬¬ä¸‰ä¸ªå‚æ•°ä¸º 一个文件结构体 flock ,包å«ä»¥ä¸‹æˆå‘˜:
- l_type é”的类型,å¯ä»¥æ˜¯ F_RDLCK 设置为读é”(共享é”)ï¼›F_WRLCK 设置为写é”(互斥é”)ï¼›F_UNLCK åˆ é™¤é”
- l_start l_whence é…åˆä½¿ç”¨æŒ‡å®šé”定区域, å¯ä»¥æ˜¯ SEEK_SET, SEEK_CUR, SEEK_END ,具体 lseek 函数
- l_len 指定é”定长度
- l_pid åªæœ‰å½“查询é”的时候æ‰ä¼šä½¿ç”¨ï¼Œå®ƒè¢«è®¾ç½®ä¸ºæ‹¥æœ‰è¢«æŸ¥è¯¢é”的进程的 pid
command å–值:
- F_SETLK 设置 flock 结构æè¿°çš„é”ï¼Œå¦‚æžœå› ä¸ºä¸Žå¦å¤–一个进程的é”冲çªè€Œä½¿å¾—锿— 法创建,则返回 EAGAIN 。l_type 为 F_UNLCK åˆ™åˆ é™¤é”
- F_SETLKW å’Œ F_SETLK 类似,但是一直阻塞é”直到é”被创建创建æˆåŠŸï¼Œå¦‚æžœåœ¨è¿›ç¨‹è¢«é˜»å¡žæ—¶æœ‰ä¿¡å·å‘生, fcntl 调用返回 EAGAIN
- F_GETLK 检查所æè¿°çš„锿˜¯å¦è¢«åˆ›å»ºæˆåŠŸï¼Œå¦‚æžœå·²ç»åˆ›å»ºï¼Œflock 结构除了 l_type 以外ä¸ä¼šæ”¹å˜: l_type è¢«æ”¹æˆ F_UNLCK (还ä¸å¤§æ˜Žç™½);如果创建é”失败,l_pid è¢«è®¾ç½®ä¸ºä¿æŒè¿™ä¸ªé”的进程 pid 。
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
const char *test_file = "/tmp/test_lock";
#define SIZE_TO_TRY 5
void show_lock_info(struct flock *to_show);
int main() {
int file_desc;
int res;
struct flock region_to_test;
int start_byte;
/* open a file descriptor */
file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, "Unable to open %s for read/write", test_file);
exit(EXIT_FAILURE);
}
for (start_byte = 0; start_byte < 99; start_byte += SIZE_TO_TRY) {
/* set up the region we wish to test */
region_to_test.l_type = F_WRLCK;
region_to_test.l_whence = SEEK_SET;
region_to_test.l_start = start_byte;
region_to_test.l_len = SIZE_TO_TRY;
region_to_test.l_pid = -1;
printf("Testing F_WRLCK on region from %d to %d\n",
start_byte, start_byte + SIZE_TO_TRY);
/* now test the lock on the file */
res = fcntl(file_desc, F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr, "F_GETLK failed\n");
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid != -1) {
printf("Lock would fail. F_GETLK returned:\n");
show_lock_info(®ion_to_test);
}
else {
printf("F_WRLCK - Lock would succeed\n");
}
/* now repeat the test with a shared (read) lock */
/* set up the region we wish to test */
region_to_test.l_type = F_RDLCK;
region_to_test.l_whence = SEEK_SET;
region_to_test.l_start = start_byte;
region_to_test.l_len = SIZE_TO_TRY;
region_to_test.l_pid = -1;
printf("Testing F_RDLCK on region from %d to %d\n",
start_byte, start_byte + SIZE_TO_TRY);
/* now test the lock on the file */
res = fcntl(file_desc, F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr, "F_GETLK failed\n");
exit(EXIT_FAILURE);
}
if (region_to_test.l_pid != -1) {
printf("Lock would fail. F_GETLK returned:\n");
show_lock_info(®ion_to_test);
}
else {
printf("F_RDLCK - Lock would succeed\n");
}
} /* for */
close(file_desc);
exit(EXIT_SUCCESS);
}
void show_lock_info(struct flock *to_show) {
printf("\tl_type %d, ", to_show->l_type);
printf("l_whence %d, ", to_show->l_whence);
printf("l_start %d, ", (int)to_show->l_start);
printf("l_len %d, ", (int)to_show->l_len);
printf("l_pid %d\n", to_show->l_pid);
}
书ä¸è¿˜è®²è¿°äº† 竞争é”以åŠå¦å¤–一ç§è®°å½•é”实现 lockf ,暂ä¸ç»†çœ‹ã€‚
p314 æ•°æ®åº“
大多数 unix 系的æ“作系统都支æŒä¸€ç§é«˜æ•ˆçš„æ•°æ®åº“管ç†ç³»ç»Ÿ dbm 。其实有些人怀疑它是å¦å¯ä»¥ä½œä¸ºä¸€ç§æ•°æ®åº“ï¼Œå› ä¸ºå®ƒåªå…·å¤‡å¾ˆç®€å•的数æ®åº“功能,å¯ä»¥è¯´å°±æ˜¯ä¸€ä¸ªæ–‡ä»¶ç´¢å¼•å˜å‚¨ç³»ç»Ÿã€‚RPM 打包系统就使用 dbm ä¿å˜å·²ç»å®‰è£…包的信æ¯çš„å˜å‚¨ä»‹è´¨ã€‚å¼€æºçš„ Open LDAP ä»¥åŠ apache , sendmail 也使用 dbm ä¿å˜æ•°æ®ã€‚相比 mysql ç‰æ•°æ®åº“系统, dbm éžå¸¸è½»é‡ï¼Œä¸éœ€è¦å®‰è£…独立的数æ®åº“ server 。dbm éžå¸¸é€‚åˆè¯»å¾ˆè¯»å¤šä½†æ˜¯å†™å¾ˆå°‘的数æ®ã€‚linux 系统一般带 dbm çš„ GNU 版,也就是 gdbm ã€‚ä¸‹é¢æ˜¯ä¸ªæ“作实例:
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <ndbm.h>
/* On some systems you need to replace the above with
#include <gdbm-ndbm.h>
*/
#include <string.h>
#define TEST_DB_FILE "/tmp/dbm1_test"
#define ITEMS_USED 3
/* A struct to use to test dbm */
struct test_data {
char misc_chars[15];
int any_integer;
char more_chars[21];
};
int main() {
struct test_data items_to_store[ITEMS_USED];
struct test_data item_retrieved;
char key_to_use[20];
int i, result;
datum key_datum;
datum data_datum;
DBM *dbm_ptr;
dbm_ptr = dbm_open(TEST_DB_FILE, O_RDWR | O_CREAT, 0666);
if (!dbm_ptr) {
fprintf(stderr, "Failed to open database\n");
exit(EXIT_FAILURE);
}
/* put some data in the structures */
memset(items_to_store, '\0', sizeof(items_to_store));
strcpy(items_to_store[0].misc_chars, "First!");
items_to_store[0].any_integer = 47;
strcpy(items_to_store[0].more_chars, "foo");
strcpy(items_to_store[1].misc_chars, "bar");
items_to_store[1].any_integer = 13;
strcpy(items_to_store[1].more_chars, "unlucky?");
strcpy(items_to_store[2].misc_chars, "Third");
items_to_store[2].any_integer = 3;
strcpy(items_to_store[2].more_chars, "baz");
for (i = 0; i < ITEMS_USED; i++) {
/* build a key to use */
sprintf(key_to_use, "%c%c%d",
items_to_store[i].misc_chars[0],
items_to_store[i].more_chars[0],
items_to_store[i].any_integer);
/* build the key datum strcture */
key_datum.dptr = (void *)key_to_use;
key_datum.dsize = strlen(key_to_use);
data_datum.dptr = (void *)&items_to_store[i];
data_datum.dsize = sizeof(struct test_data);
result = dbm_store(dbm_ptr, key_datum, data_datum, DBM_REPLACE);
if (result != 0) {
fprintf(stderr, "dbm_store failed on key %s\n", key_to_use);
exit(2);
}
} /* for */
/* now try and retrieve some data */
sprintf(key_to_use, "bu%d", 13); /* this is the key for the second item */
key_datum.dptr = key_to_use;
key_datum.dsize = strlen(key_to_use);
data_datum = dbm_fetch(dbm_ptr, key_datum);
if (data_datum.dptr) {
printf("Data retrieved\n");
memcpy(&item_retrieved, data_datum.dptr, data_datum.dsize);
printf("Retrieved item - %s %d %s\n",
item_retrieved.misc_chars,
item_retrieved.any_integer,
item_retrieved.more_chars);
}
else {
printf("No data found for key %s\n", key_to_use);
}
dbm_close(dbm_ptr);
exit(EXIT_SUCCESS);
}
dbm ä»¥åŠ mysql æ›´å…·ä½“è¯¦ç»†ä»‹ç»æš‚ä¸çœ‹ã€‚åŽé¢å‡ ç« ä¹Ÿä¸æ˜¯æˆ‘关注的点,暂时忽略ä¸çœ‹ã€‚直接跳到 第åä¸€ç« è¿›ç¨‹å’Œä¿¡å·ã€‚
