内存管理
从segmentation fault (core dumped)问题出发:
原因(目前我遇到的):
一个是内存访问越界;
一个是访问空指针。
malloc calloc realloc的区别:
void *malloc(size_t size)
void *calloc(size_t number_of_elements, size_t element_size);
void *realloc(void *existing_memory, size_t new_size);
malloc 和 calloc都是返回一段空间的首地址, 里面的值都被初始化为0;
realloc一般是对calloc和malloc内存进行扩充,一般(如果可以)将会在原来的地址后续添加,否则将在新地址上新辟空间,将原地址的数据拷贝过去。
一般的使用如下:
my_ptr = malloc(BLOCK_SIZE);
...
tmp_ptr = my_ptr;
tmp_ptr = realloc(tmp_ptr, BLOCK_SIZE*2);
if (tmp_ptr != NULL) {
my_ptr = tmp_ptr;
}
这里考虑的地方有俩处:
1)realloc 可能返回 NULL
2)realloc成功的话,会自动判断realloc返回的指针是否跟先前的一致,不一致会自动free掉(来自 realloc函数心得 )
文件锁定
创建文件锁:
文件锁的原理,就是创建文件(建立锁),如果文件已存在(已经有进程创建文件锁) 则返回错误信息。
文件锁的创建, 是通过系统调用open(“lock_file”,O_RDWR|O_CREAT|O_EXCL, 0444)创建的。其中,O_CREAT|O_EXCL 表示如果文件未存在则创建,否则返回错误信息(17),open失败(返回-1).其中0444,表示创建文件的权限rwx(这里应该是-r–r–r–)
这里是例子程序:
#include
#include
#include
#include
#include
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());
printf("Open failed with error %d\n", errno);
sleep(3);
} else {
//这里是临界区
//***********************
printf("%d - I have exclusive access\n", getpid());
sleep(1);
//***********************
(void)close(file_desc);
//此处"删除"文件, 理解为释放锁
(void)unlink(lock_file);
sleep(2);
}
}
exit(EXIT_SUCCESS);
}
其中 unlink 的作用是“删除“文件, 这里准确的来说 是删除(硬)链接。linux删除一个文件,是看其硬链接个数, 如果硬链接个数为0, 则该文件被标记为可覆盖(其实文件内容仍然存在,直至新的文件写入覆盖之,毕竟真正的删除是很费事的(每个字节全部替换成0xcc?))
区域锁定
概要:锁定一个文件的某个区域
方式有fcntl(最常用) lockf。注意 由于俩者使用不同的底层实现,所以必须坚决的使用其中一种
函数调用为int fcntl(int fildes, int command, struct flock *flock_structure);
flock 结构包括:
1)short l_type
2)short l_whence
3)off_t l_start
4)off_t l_len
5)pid_t l_pid
其中l_type取值有F_RDLCK, F_WRLCK, F_UNLCK. 分别为共享锁,独占锁,解锁。(read允许有多个(共享),write只能有一个(独占),并且read时不允许有writer,write时不允许有reader。读者-写者问题)
//读者-写者问题代码
//writer
do{
wait(writeLock);
...
//write here
...
signal(writeLock);
} while(TRUE);
//reader
do {
wait(readLock);
readcount++;
if (readcount == 1)
wait(writeLock);
signal(readLock);
...
//read here
...
wait(readLock);
readcount--;
if (readcount == 0)
signal(writeLock);
signal(readLock);
} while(TRUE);
其中readLock是保证readcount多线程的安全,而总的来看,reader也是 wait(writeLock) ... signal(writeLock).
回到fcntl(fildes, command, flock_structure)
其中command 有 F_GETLK F_SETLK F_SETLKW.
F_GETLK:
尝试获取锁的信息,一般会返回不为-1的值,而且一般会修改flock_structure,l_pid此时的值是当前获得锁的进程id号,此处flock需要使用的值是type,whence,start,len,pid
F_SETLK:
功能为加锁,如果加锁失败(如对已独占区域加锁,对共享区域加独占锁)函数返回-1.(l_pid 与get不同 此时并不会修改)
F_SETLKW:
功能与上文一致,不同的是,无法得到锁时,将等待。。。只有在获得锁或者接受到信号才返回。
这三种锁,将会随着相关文件描述符fildes的关闭而自动清除。
代码:
//来自 Linux程序设计
lock3.c
#include
#include
#include
#include
const char *test_file = "/tmp/test_lock";
int main()
{
int file_desc;
int byte_count;
char *byte_to_write = "A";
struct flock region_1;
struct flock region_2;
int res;
file_desc = open(test_file, O_RDWR | O_CREAT, 0666);
if (!file_desc) {
fprintf(stderr, "Unable to open %s for read/write\n", test_file);
exit(EXIT_FAILURE);
}
for (byte_count = 0; byte_count < 100; byte_count++) {
(void)write(file_desc, byte_to_write, 1);
}
region_1.l_type = F_RDLCK;
region_1.l_whence = SEEK_SET;
region_1.l_start = 10;
region_1.l_len = 20;
region_2.l_type = F_WRLCK;
region_2.l_whence = SEEK_SET;
region_2.l_start = 40;
region_2.l_len = 10;
//it is ok.
//region_2.l_start = 10;
//region_2.l_len = 20;
printf("Process %d locking file\n", getpid());
res = fcntl(file_desc, F_SETLK, ®ion_1);
if (res == -1) fprintf(stderr, "Failed to lock region 1\n");
res = fcntl(file_desc, F_SETLK, ®ion_2);
if (res == -1) fprintf(stderr, "Failed to lock region 2\n");
sleep(60);
printf("Process %d closing file\n", getpid());
close(file_desc);
exit(EXIT_SUCCESS);
}
lock4.c
#include
#include
#include
#include
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;
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) {
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);
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");
}
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);
res= fcntl(file_desc, F_GETLK, ®ion_to_test);
if (res == -1) {
fprintf(stderr, "F_GETLK failed\n");
//show_lock_info(®ion_to_test);
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");
}
}
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, ", to_show->l_start);
printf("l_len %d, ", to_show->l_len);
printf("l_pid %d\n", to_show->l_pid);
}
先执行lock3 再执行 lock4
./lock3 &
./lock4
不建议 ./lock3 & ./lock4
此处并非严格顺序执行(并行),不能保证lock3执行到sleep时lock4才执行。
建议试试:
1)对lock3 两个锁(RD WR)的区域设为同一值,看看运行结果。(估计锁的粒度 是进程/线程?)
2)将lock4 fcntl中的GETLK 改成 SETLK, 看看运行结果。(fcntl将返回-1, 而且如果此时查看flock的l_pid,发现并无修改)
注意:
1)此处的锁都是建议锁,也就是实际上还是可以对被锁文件进行读写的。(可以尝试运行lock3, 再使用gedit打开lock_file,并修改)
建议锁的作用是,至少在你访问前能得知当前是否有进程也在同时使用他。
2)最后提及一下死锁,想一下这种情况:
process A:
lock(file1);
...
sleep(10);
lock(file2);
//do someting to file2
unlock(file2);
...
unlock(file1);
process B:
lock(file2);
...
sleep(10);
lock(file1);
//do someting to file1
unlock(file1);
...
unlock(file2);
如果同时执行,对于A来说只有获取文件2,执行结束后才释放文件1.而B只有在获取文件1,执行结束后才释放A想要的文件2. 双方都拥有对方想要的元素,但是都不想退让,僵持在那里。这就是死锁。
dbm数据库
轻量级数据库,通过索引dir+文件内容pag 读写数据。
基本操作跟mysql差不多,唯一得说的是Ubuntu下,如何使用dbm:
1, 安装gdbm:
$ sudo apt-get install libgdbm-dev
2, 在c文件中使用: 头文件: #include