数据管理五个原则,数据管理五个维度 客户 渠道

  数据管理五个原则,数据管理五个维度 客户 渠道

  竞争锁

  现在我们已经了解了如何测试文件上已经存在的锁,下面我们来看一下当两个程序在文件的同一块区域竞争锁时会出现什么情况。我们将会使用我们的锁定3程序在文件的第一个位置进行加锁操作,然而一个新的程序在同样的位置尝试加锁。要完成这个例子,我们需要添加一些解锁操作。

  试验-竞争锁

  下面是程序lock5.c,试图在文件中已被加锁的区域进行加锁操作,而不是测试文件不同部分的锁状态。

  一在#包括以及声明之后,打开一个文件描述符:

  #包括unistd.h

  #包含标准库

  #包含标准视频

  #包含fcntl.h

  const char * test _ file="/tmp/test _ lock ";

  int main()

  {

  int file _ desc;

  结构群体区域锁定

  内部资源

  file_desc=open(test_file,O_RDWR O_CREAT,0666);

  如果(!文件_desc) {

  fprintf(stderr,"无法打开%s进行读/写/n ",test _ file);

  退出(退出_失败);

  }

  2程序的其余部分指定文件的不同区域,并且在其上尝试不同的锁操作:

  region _ to _ lock.l _ type=F _ RDLCK

  区域锁定。l _ where=SEEK _ SET

  region _ to _ lock.l _ start=10

  区域锁定。l _ len=5;

  printf("进程%d,正在尝试F_RDLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,(int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLK,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -无法锁定区域/n ",getpid());

  }否则{

  printf("进程%d -获得的锁区域/n ",getpid());

  }

  region _ to _ lock.l _ type=F _ UNLCK

  区域锁定。l _ where=SEEK _ SET

  region _ to _ lock.l _ start=10

  区域锁定。l _ len=5;

  printf("进程%d,正在尝试F _ UNLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,

  (int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLK,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -未能解锁区域/n ",getpid());

  }否则{

  printf("进程%d -解锁区域/n ",getpid());

  }

  region _ to _ lock.l _ type=F _ UNLCK

  区域锁定。l _ where=SEEK _ SET

  区域锁定。l _ start=0;

  region _ to _ lock.l _ len=50

  printf("进程%d,正在尝试F _ UNLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,

  (int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLK,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -未能解锁区域/n ",getpid());

  }否则{

  printf("进程%d -解锁区域/n ",getpid());

  }

  region _ to _ lock.l _ type=F _ WRLCK

  区域锁定。l _ where=SEEK _ SET

  region _ to _ lock.l _ start=16

  区域锁定。l _ len=5;

  printf("进程%d,正在尝试F_WRLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,

  (int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLK,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -无法锁定区域/n ",getpid());

  }否则{

  printf("进程%d -在区域/n上获得锁“,getpid());

  }

  region _ to _ lock.l _ type=F _ RDLCK

  区域锁定。l _ where=SEEK _ SET

  region _ to _ lock.l _ start=40

  region _ to _ lock.l _ len=10

  printf("进程%d,正在尝试F_RDLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,

  (int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLK,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -无法锁定区域/n ",getpid());

  }否则{

  printf("进程%d -在区域/n上获得锁“,getpid());

  }

  region _ to _ lock.l _ type=F _ WRLCK

  区域锁定。l _ where=SEEK _ SET

  region _ to _ lock.l _ start=16

  区域锁定。l _ len=5;

  printf("进程%d,尝试带等待的F_WRLCK,区域%d到%d/n ",getpid(),

  (int)region_to_lock.l_start,

  (int)(region_to_lock.l_start

  区域锁定。l _ len));

  res=fcntl(file_desc,F_SETLKW,region _ to _ lock);

  if (res==-1) {

  printf("进程%d -无法锁定区域/n ",getpid());

  }否则{

  printf("Process %d -在区域/n上获得锁",getpid());

  }

  printf("Process %d ending/n ",getpid());

  关闭(文件_ desc);

  退出(EXIT _ SUCCESS);

  }

  如果我们首先在后端运行lock3程序,然后立即运行这个新程序:

  $ ./lock3

  $ process 1845锁定文件

  $ ./锁定

  我们的程序输出结果如下:

  进程227锁定文件

  进程228,尝试F_RDLCK,区域10到15

  过程228 -获得区域锁定

  过程228,尝试F_UNLCK,区域10到15

  过程228 -解锁区域

  过程228,尝试F_UNLCK,区域0到50

  过程228 -解锁区域

  进程228,尝试F_WRLCK,区域16到21

  过程228 -无法锁定区域

  进程228,尝试F_RDLCK,区域40到50

  过程228 -无法锁定区域

  进程228,用等待尝试F_WRLCK,区域16到21

  进程227关闭文件

  过程228 -获得区域锁定

  过程228结束

  操作原理

  首先,程序试图使用一个共享锁来锁定文件中10到15字节的区域。该区域已经有一个共享锁,但是允许后续的共享锁,锁定操作成功。

  然后程序解锁这个区域的共享锁,这个操作也是成功的。然后程序尝试解锁文件的前50个字节,尽管这里没有锁设置。这个操作也是成功的,因为虽然程序在第一个位置没有锁,但是解锁请求的结果是程序没有在前50个字节上添加任何锁。

  接下来,程序试图使用一个排他锁来锁定16-21字节的区域。该区域已经有一个共享锁,因此这个新锁操作失败,因为无法创建独占锁。

  此后,程序试图在40-50字节的区域上添加一个共享锁。该区域中已经存在独占锁,因此锁定操作失败。

  最后,程序试图获得16-21字节区域的独占锁,但这次他使用F_SETLKW命令等待,直到获得锁。在程序的输出中会有一个很长的停顿,直到锁定这个区域的lock3程序关闭文件并释放所有获得的锁。lock5程序继续执行,并在退出前成功锁定了该区域。

  其他锁定命令

  还有第二种锁定文件的方法:lockf函数。这个函数也使用文件描述符进行操作。其功能原型如下:

  #包括unistd.h

  int lockf(int fildes,int function,off _ t size _ to _ lock);

  函数参数的值如下:

  解锁(_ u)

  F_LOCK:独占锁

  F_TLOCK:测试并添加独占锁。

  F_TEST:测试其他进程的锁。

  size_to_lock参数是要操作的字节数,从文件中的当前偏移量开始计算。

  Lockf的接口比fcntl接口更简单,主要是因为它的功能更少,灵活性更强。要使用这个函数,我们必须设置要锁定的区域的开头,然后用要锁定的字节数来调用它。

  类似于文件锁定的fcntl方法,所有的锁都只是建议锁;他们并没有真正停止读写文件。锁的检测是程序的责任。Linux中使用fcntl锁和lockf锁的效果没有定义,所以我们必须决定我们要使用哪种类型的锁,并坚持只使用一种锁。

  僵局

  如果不提及死锁的危险,那么关于锁的讨论是不完整的。假设两个程序想要更新同一个文件。它们都需要同时更新1到2个字节的区域。一个程序选择先更新字节2,然后更新字节1。程序B试图首先更新字节1,然后更新字节2。

  两个程序同时走。程序A锁定字节2,而程序B锁定字节1。一个程序试图锁定字节1。因为这已经被程序B锁定了,所以程序A等待。程序试图锁定字节2。因为这个已经被程序A锁定了,所以程序B也在等待。

  当任何一个程序处理不了的时候,就叫死锁。这是数据库程序中常见的问题,因为大量用户会同时访问相同的数据。大多数业务关系数据会检测死锁并自动打破它们;Linux内核不会。为了处理死锁,需要一些外部干预,比如终止其中一个程序。

  程序员必须意识到这种情况。当我们有多个程序等待锁时,我们必须非常小心以避免死锁。在我们的例子中很容易避免死锁:两个程序以相同的顺序锁定它们需要的字节,或者锁定一个更大的区域。

数据管理五个原则,数据管理五个维度 客户 渠道