目录结构

1 底层系统调用
2 标准IO库
3 格式化输入输出
4 带缓冲的IO和不带缓冲的IO
5 目录操作
6 文件和目录的维护
7 proc文件系统
8 锁文件
9 文件锁

想必在此之前,你已经听说过这么一句话,在linux中,一切(几乎一切)都是文件,设置硬件设备也被映射为文件,Linux中比较重要的设备文件有3个:

/dev/console:系统控制台
/dev/tty:终端控制,由系统自动运行的进程和脚本没有控制终端,所以它们不能打开/dev/tty
/dev/null:空设备,在cp命令里把它用作复制空文件的源文件,例如:
cp /dev/null empty_file

一 底层系统调用

文件操作的底层系统调用常用的有以下几个:

open 打开文件或设备
read 从打开的文件或者设备里面读数据
write 向文件或设备里写数据
close 关闭文件或设备
lseek 设置读写指针
fstat/stat/lstat 获取文件相关信息
mmap/msync 内存映射

当一个进程(process)开始运行时,一般会有3个已经打开的文件描述符:

0 标准输入
1 标准输出
2 标准错误

open

int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);

— 参数解释 —

  • path:文件路径和文件名
  • oflags:
O_RDONLY:以只读方式打开
O_WRONLY:以只写方式打开
O_RDWR:以读写方式打开
可选的组合模式:
O_APPEND:把写入的数据追加在文件的末尾
O_TRUNC:把文件的长度设置为0,丢弃已有的内容
O_CREAT:如果需要,则创建文件,比较常用
O_EXCL:与O_CREAT一起使用,使用这个模式可以方式两个进程同时创建出一个文件,如果文件已经存在,则创建失败。
  • mode:
    如果你使用O_CREAT标志的open来创建文件时,你必须使用三个参数的open函数。
    第三个参数是文件的读/写/执行权限,一般是以0开头的三个八进制数,每个八进制数都是由1/2/4中的一个或几个进行相加得到的。
    这三个八进制中,第一个表述属主的权限,第二个表示组的权限,第三个表示其他用户的权限。
0 允许任何权限
1 禁止执行权限
2 禁止写权限
4 禁止执行权限
  • 返回值:
    open调用成功时会返回一个新的文件描述符(非负整数),在失败时返回-1。
    值得注意的是,返回的新的文件描述符总是未使用描述符的最小值,这个特征非常有用,后面的文章会提到。

read

size_t read(int fildes,void *buf,size_t nbytes);

—参数解释—

  • filedes: 文件描述符
  • buf: 存放数据的缓冲区
  • nbytes: 读取的字节数
  • 返回值:返回实际读取的字节数,这可能会小于请求的字节数。如果返回的是0,表示已经到达文件尾,未读入任何数据,如果返回的是-1,表示read调用出现了错误。

write

size_t write(int fildes,const void *buf,size_t nbytes);

—参数解释—

  • filedes: 文件描述符
  • buf: 要写入的缓冲区
  • nbytes: 准备写入的字节数,写入的是缓冲区的前nbytes个字节
  • 返回值:返回实际写入的字节数,可能会小于nbytes,如果返回0,表示未写入任何数据,返回-1,表示发生错误。

close

int close(int fildes);
  • 终止文件描述符与其对应的文件之间的关系
  • 释放文件描述符并使其能够重新使用
  • 成功返回0,失败返回-1

lseek

lseek用于对文件描述符fildes的读写指针进行设置。

off_t lseek(int fileds,off_t offset,int whence);

—参数解释—

  • off_t offset:off_t是一个整数类型,offset表示偏移值
  • whence
SEEK_SET offset是一个绝对位置
SEEK_CUR offset是一个相对于当前位置的相对位置
SEEK_END offset是一个相对于文件尾的相对位置
  • 返回值
    返回从文件头到文件指针的字节偏移值,失败时返回-1.

fstat/stat/lstat

int fstat(int fildes,struct stat *buf);
int stat(const char *path,struct stat *buf);
int lstat(const char *path,struct stat *buf);
  • fstat 通过文件描述符返回相关的文件状态信息
  • stat/lstat 是通过文件名查到文件的状态信息
  • stat/lstat的区别:当查到的文件名是一个符号链接时,lstat返回的是该符号链接本身的信息,stat返回的是该链接指向的文件的信息。

mmap/msync

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

函数功能

  • mmap函数创建一段指向内存区域的指针,该内存区域与可以通过一个文件描述符访问的文件的内容相关联。
  • 简单的说,就是把二进制文件的内容放在内存里,读写文件就可以使用指针操作,方便对某个字节进行读写操作。

函数参数

  • addr : 分配的内存地址,一般设置为NULL,即由系统进行分配
  • len : 需要分配的长度
  • prot: 设置内存访问的权限,有PORT_READ读/PORT_WRITE写/PORE_EXEC执行,按位OR操作
  • fildes : 已经打开的文件描述符
  • off:内存访问的文件中数据的起始偏移地址
int msync(void *addr,size_t len,int flags);

函数功能:把映射的内存段写回到被映射的文件中

  • flasgs:执行修改的具体方式
MS_ASYNC 采用异步写方式
MS_SYNC 采用同步写方式
MS_INVALIDATE 从文件中读回数据

二 标准IO库

IO标准库提供的读写函数主要有以下几个

fopen 打开文件
fread 读取文件数据
fwrite 向文件写数据
flose 关闭文件流
fflush 写出缓冲区中所有的数据
fseek 移动读写指针
fgetc/getc/getchar 从文件流中获取一个字节
fputc/putc/putchar 输出一个字节到文件流中
fgets/gets 从文件流中获取字符串

标注IO库stdio为底层I/O系统调用提供了一个通用的接口,还提供了许多复杂的函数用于格式化输出和扫面输入,它还负责满足设备的缓冲需求。
在标准IO库中,与文件描述符对应的是流(stream),它是指向结构FILE的指针。
同样,在启动程序时,由3个流时自动打开的,分别是

stdin 标准输入
stdout 标准输出
stderr 标准错误

fopen

FILE *fopen(const char *filename,const char *mode);

— 参数解释 —

  • filename:指定的文件
  • moed:指定的打开方式
"r" / "rb" :只读
"w" / "wb" : 只写,把文件长度截短为零
"a" / "ab" : 只写,新内容追加在文件尾
"r+"/"rb+"/"r+b" : 以更新方式打开,读/写
"w+"/"wb+"/"w+b" : 以更新方式打开,并把文件长度截短为0
"a+"/"ab+"/"a+b" : 以更新方式打开,新内容追加在文件尾
  • 返回值:
    成功返回一个非空的FILE * 指针
    失败返回NULL

fread

size_t fread(void *ptr,size_t size,size_t nitems,FILE *stream);

—参数解释—

  • ptr : 数据存放的缓冲区
  • size: 每次读取的长度
  • nitems: 读取的次数
  • stream:文件流
  • 返回值:放到缓冲区里面的数据块的个数,而不是字节数

fwrite

size_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);

—参数解释—

  • ptr : 数据存放的缓冲区
  • size: 每次写入的长度
  • nitems: 写入的次数
  • stream:文件流
  • 返回值:写入的个数

flose

int fclose(FILE *stream);
  • flose关闭文件流stream,使所有尚未写出的数据都写出,因为stdio会对数据进行缓冲,所以使用fclose是非常重要的。

fflush

int fflush(FILE *stream);
  • 把文件流里面所有未写出的数据立刻写出,可以用这个函数来确保在试图读入一个用户响应之前,先向终端送出一个交互提示符。
  • flose()隐含执行了一次fflush函数

fseek

int fseek(FILE *stream,long int offset,int whence);

offset和whence的含义与lseek一样,不一样的时fseek返回的是一个整数,0表示成功,-1表示失败。

fgetc/getc/getchar

int fgetc(FILE stream);
int getc(FILE *stream);
int getchar();
  • fgetc从文件流里面取出下一个字节并把它作为字符返回,当到达文件尾部或者发生错误时返回EOF.
  • getc与fgetc的作用一样,但是它有可能被声明成一个宏(被ISO C声明为一个宏),所以它不可以作为函数指针,宏会需要更多的内存空间,但是有更高的执行效率。
  • getchar的作用相当于getc(stdin),从标准里读取下一个字符

fputc/putc/putchar

int fputc(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);

类似于fgetc/fget/getchar之间的关系
注意,putchar和getchar都是把字符当作int类型而不是char类型来使用的,这就允许文件尾(EOF)标识取值-1

fgets/gets

char *fgets(char *s,int n,FILE *stream);
char *gets(char *s);
  • fgets把读到的字符写到S所指向的字符串里,直到遇到换行符或者文件尾,也会把换行符接受到字符串里,并加上结尾字符\0,一次调用最多传输n-1个字符,因为最后一个字符必须是\0
    成功时,返回指向s的指针,失败时,返回一个空指针
  • gets类似于fgets,只不过它是从标准输入读取数据并丢弃换行符,并在结尾加上null字符

三 格式化输入输出

1 标准输出函数

printf/fprintf/sprintf

int printf(const char *format,...);
int sprintf(char *s,const char *format,...);
int fprintf(FILE *stream,const char *format,...);
  • printf 把自己的输出送到标准输出
  • fprintf 把自己的输出送到一个指定的文件流##
  • sprintf 把自己的输出和一个结尾空字符写到字符串s里面

scanf/fscanf/sscanf

int scanf(const char *format,...);
int fscanf(FILE *stream,const char *format,..);
int sscanf(const char *s,const char *format,...);
  • scanf 读入标准输入的值保存到对应的变量里去,这些变量的类型必须正确并且精确匹配格式字符串,斗则,内存数据会遭到破坏,使程序崩溃
    使用%c控制符从输入中读取一个自读,它不会跳过起始的空白字符
    使用%s控制符来扫描字符串,它会跳过起始的空白字符
    返回值是成功读取的数据项个数
    例:
给定下面的输入行:
hello, 1234, 5.678, X, string to the end of the line
下面的scanf会正确读取4个数据项:
char s[256];
int n;
float f;
char c;
scanf("Hello,%d,%g, %c, %[^\n]",&n,&f,&c,s);
/*
 * 1 注意%c前面有空格,是为了匹配字符X之前的空格
 * 2 %[^\n]: 表示遇到\n停止读取字符串
 */
  • fscanf 从文件流读取数据到对应项中
  • sscanf 从字符串读取数据到对应项中

四 带缓冲的IO与不带缓冲的IO

底层系统调用提供的IO读写操作是不带缓存的IO
标准库stdio提供的IO读写操作是带缓存的IO

看下面两个函数

size_t write(int fildes,const void *buf,size_t nbytes);
size_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);

在数据读入buff或者是ptr之前,都会先写到内核所设置的缓冲存储器中。如果存储器未满,则并不将其排入到输出队列,直到缓存写满或者内核需要重新使用此缓存时才将其排入输出队列,再进行实际的IO操作,这种技术叫延迟写

标准IO在系统调用上多加了一个缓冲区,也因此引入了流的概念,FILE实际上包含了管理流所需要的所有信息:

  • 实际的IO文件描述符
  • 指向流缓存的指针
  • 缓存长度
  • 当前在缓存中的字节数

标准IO提供三种缓存模式:

  • 全缓存,即填满IO缓存后才执行IO操作
  • 行缓存,即输入输出遇到新行符或者缓存满时,才执行真正的IO操作,stdin,stdout通常是行缓存,当stdout被重定向到一个具体的文件时,那么它是全缓存的
  • 无缓存,相当于read write,stderr时不带任何缓存的

带缓存的IO虽然数据复制了两次,但是无需考虑缓存以及最佳IO长度的选择,因此它不比read/write慢多少

如何直观的看到标准IO库的缓存效果,看下面的例子:

int main()
{
	char buff[1024];
	printf("This Line Should be Cached...");
	sleep(3);    //这时候在终端上是看不到任何输出
	printf("\nThis Line Should be Cached Again");  //这时候可以看到第一个printf的输出,因为被换行符刷新了
	sleep(3);  
	printf("This Line Should Not be Cached Again\n"); //这时候可以看到第二个和第三个printf的输出,因为被结尾的\n刷新
	//fgets(buff,20,stdin);		// buff中带'\n'
	gets(buff);					// buff中不带'\n'
	printf("%s ",buff);
}

五 目录操作

opendir 打开目录流
readdir 扫描目录
telldir 返回目录当前位置
seekdir 设置目录流指针
closedir 关闭一个目录流

Linux中,与目录操作有关的头文件在diret.h中,它们使用一个名为DIR的结构体作为目录操作的基础,被称为目录流的指针指向这个结构体。

opendir

DIR *opendir(const char *name);
  • 打开并建立一个目录流,如果失败,则返回一个空指针

readdir

struct dirent *readdir(DIR *dirp);
  • readdir返回一个指针,指向目录流的下一个目录项的有关资料,如果发生错误或者到达目录为,则返回NULL
  • struct dirent 包含的内容:
ino_t d_ino: 文件的inode节点号
char d_name[]: 文件的名字

telldir

long int telldir(DIR *dirp);
  • 返回值记录一个目录流的当前位置

seekdir

void seekdir(DIR *dirp,long int loc);
  • 设置目录流dirp的目录项指针,loc的值用来设置指针位置,它应该通过telldir来获得

closedir

int closedir(DIR *dirp);
  • 关闭一个目录流并释放与之关联的资源,执行成功返回0,错误返回-1

六 文件和目录的维护

chmod 改变文件的访问权限
unlink/link/symlink 删除/建立文件链接
mkdir/rmdir 建立/删除目录
chdir/getcwd 获取当前目录

chmod

int chmod(const char *path,mode_t mode);
  • path参数指定的文件被修改为具有mode参数给出的权限,参数mode与open中的一样

unlink/link/symlink

int unlink(const char *path);
int link(const char *path1,const char *path2);
int symlink(const char *path1,const char *path2);
  • unlink,用来删除一个文件的目录项并减少它的连接数,成功返回0,失败返回-1,如果一个文件的链接数减少到0并且没有进程打开它,这个文件就会被删除
  • link 将创建一个指向已有文件path1的新连接,新目录由path2给出
  • symlink 以类似的方式创建符号链接

mkdir/rmdir

int mkdir(const char *path,mode_t mode);
int rmdir(const char *path);
  • mkdir 用于创建目录,权限由mode参数给出
  • rmdir 用于删除目录,只有在目录为空时才行

chdir/getcwd

int chdir(const char *path);
char *getcwd(char *buf,size_t size);
  • chdir 用于切换目录
  • getcwd 用于获取当前目录,把当前目录的名字写到buff里面,size参数给出了buff的长度

七 /proc文件系统

Linux提供了一个特殊的文件系统procfs,它通常以/proc目录的形式呈现

该目录中包含了许多驱动程序和内核信息,只要应用程序具有正确的访问权限,它们就可以通过读写这些文件来获得信息或者设置参数

  • 这是我阿里云服务器的/proc目录列表
1      143    23     430   54    6875  985          interrupts   modules        thread-self
10     15     24     431   55    6893  acpi         iomem        mounts         timer_list
11     16     274    446   56    6894  buddyinfo    ioports      mtrr           timer_stats
11293  163    27448  47    57    6911  bus          irq          net            tty
11296  165    27449  4700  573   7     cgroups      kallsyms     pagetypeinfo   uptime
12     166    27456  474   58    7081  cmdline      kcore        partitions     version
13     17     27457  479   59    786   consoles     keys         sched_debug    version_signature
133    18     27458  48    599   8     cpuinfo      key-users    schedstat      vmallocinfo
134    19     27507  49    60    81    crypto       kmsg         scsi           vmstat
135    197    28     5     61    811   devices      kpagecgroup  self           zoneinfo
137    2      29     50    62    82    diskstats    kpagecount   slabinfo
138    20     3      504   6539  874   dma          kpageflags   softirqs
139    20824  30     505   6559  882   driver       loadavg      stat
14     21     31     51    6581  9     execdomains  locks        swaps
140    218    394    52    6584  930   fb           mdstat       sys
141    22     411    5220  68    952   filesystems  meminfo      sysrq-trigger
142    22921  425    53    6857  953   fs           misc         sysvipc
  • 常用的信息
cat /proc/cpuinfo    输出cpu信息
cat /proc/meminfo    输出内存使用情况
cat /proc/version    输出内核版本信息
... ...

八 锁文件

Linux提供了很多种方法来实现文件锁定,其中最简单的方法就是以原子操作的方式创建锁文件,所谓“原子操作”就是在创建锁文件时,这个过程不允许被打断。

这种方式却把它所创建的文件是唯一的,而且这个文件不可能被其他程序在同一时间创

  • 锁文件只是建议锁,而不是强制锁

    建议锁:进程在对某一个文件进行操作时,没有检测是否加锁或者直接向文件写入数据,内核是不会加以阻止的
    强制锁:OS内核的文件锁,应用程序对文件进行读写操作时,OS内核会检测文件是否加锁,如果加锁将导致操作失败

  • 锁文件的实现是通过open函数调用的O_EXCL标志来完成的

  • 示例代码
/*
 *  文件锁
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

int main()
{
        int file_desc;
        int save_error;

        file_desc = open("./LCK.test",O_RDWR | O_CREAT | O_EXCL,0444);
        if(file_desc == -1)
                {
                        save_error = errno;
                        printf("open failed with error %d \n",save_error);
                }
        else
                {
                        printf(" open succeeded \n");
                }
        exit(EXIT_SUCCESS);
}

通过O_EXCL标志以原子操作的方式创建了一个锁文件,但是需要注意以下两点:

  • 1 如果需要保证这个文件的名字是唯一的,那么所有的进程在创建文件时都需要加上O_EXCL,如果这个文件已经存在,则open()调用会返回错误
  • 2 如果其他进程不加O_EXCL标志,则可以直接读写这个文件,因为锁文件是建议锁而不是强制锁

九 文件锁

Linux至少提供两种系统调用,分别是fcntl系统调用和lockf系统调用,这些系统调用的好处是可以实现文件的区域锁定段锁定
fcntl和lockf使用不同的底层实现,因此两者不能混合使用
fcntl和lockf都是建议锁,而不是强制锁

fcntl系统调用

  • fcntl对一个打开的文件描述符进行操作,并能根据command参数完成不同的任务
int fcntl(int fildes,int command,struct flock *flock_structure);
  • command选项如下:
/* 获取锁信息,信息存储在 struct flock 结构体中 */
F_GETLK

/* 加锁或者解锁,加锁或者解锁的信息在 struct flock 结构体中  */
F_SETLK

/* 与SETLK类似,但在无法获取锁时,这个调用将等待直到 1 获取锁 2 收到一个信号 才会返回 */
F_SETLKW
  • struct flock的成员如下:
short l_type
short l_whence
off_t l_start
off_t l_len
pid_t l_pid
  • l_type
/*
 * 共享锁/读锁 
 * 许多不同的进程可以拥有文件同一区域的共享锁
 * 只要任一进程拥有共享锁,则没有进程可以获得该区域的独占锁
 * 简单的说,这把锁的作用是使文件不让进程上锁
 * 要想获取共享锁,文件必须以读或者读写的方式打开
 * 一般读文件的进程设置共享锁
 */
F_RDLCK

/*
 *解锁,清除锁
 */
F_UNLCK

/*
 * 独占锁/写锁 
 * 有且只有一个进程可以在文件的特定区域拥有一把独占锁
 * 只要有一个进程设置了独占锁,其他任何进程都不可以设置任何锁
 * 获取独占锁的前提是,文件必须要以写或者读写的方式打开,(否则没有获取独占锁,因为不需要修改文件)
 * 一般写文件的进程设置独占锁
 */
F_WRLCK
  • l_whence
/* 文件头 */
SEEK_SET
/* 当前位置 */
SEEK_CUR
/* 文件尾 */
SEEK_END
  • l_start / l_len
    l_whence定义了l_start的相对偏移值,l_start定义了该区域的第一个字节,l_len定义了改区域的字节数

  • l_pid
    l_pid记录 持有锁的进程

示例代码

1 在文件上加锁

/*
 *	文件锁 
 *  use fcntl
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

const char *test_file = "./tmp/test_lock.txt";

/*
The structure describing an advisory lock.  This is the type of the third 
argument to `fcntl' for the F_GETLK, F_SETLK, and F_SETLKW requests.  

struct flock
  {
    __off_t l_start;	 	Offset where the lock begins.  
    __off_t l_len;		    Size of the locked area; zero means until EOF.  
    __pid_t l_pid;			Process holding the lock.  
    short int l_type;		Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK.  
    short int l_whence;		Where `l_start' is relative to (like `lseek').  
  };
*/
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 file \n");
			exit(EXIT_FAILURE);
		}
	for(byte_count = 0;byte_count < 100;byte_count ++)
		{
			write(file_desc,byte_to_write,1);
		}
	/* 10-30 字节设置为区域 1 ,设置共享锁 */
	region_1.l_type = F_RDLCK;
	region_1.l_whence = SEEK_SET;
	region_1.l_start =  10;
	region_1.l_len = 20;

	/* 40-50 字节设置为区域 2 ,设置独占锁 */
	region_2.l_type = F_WRLCK;
	region_2.l_whence = SEEK_SET;
	region_2.l_start =  40;
	region_2.l_len = 10;

	/* 锁定文件 */
	printf(" process %d locking file \n ",getpid());
	res = fcntl(file_desc,F_SETLK,&region_1); /* F_SETLK : 区域加锁 */
	if(res == -1) fprintf(stderr,"failed to lock region 1\n");
	res = fcntl(file_desc,F_SETLK,&region_2); /* F_SETLK : 区域加锁 */
	if(res == -1) fprintf(stderr,"failed to lock region 2\n");

	sleep(60);

	printf("process %d close file \n",getpid());
	close(file_desc);
	exit(EXIT_SUCCESS);
}

2 在另一个进程中读出这些锁


/*
 *	测试文件中不同部分的文件锁
 *  use fcntl 
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

const char *test_file = "./tmp/test_lock.txt";
#define SIZE_TO_TRY 5

void show_lock_info(struct flock * lck);

int main()
{
	int file_desc;
	int res;
	struct flock region_to_test;
	int start_byte;

	file_desc = open(test_file,O_RDWR | O_CREAT,0X666);
	if(!file_desc)
		{	
			fprintf(stderr,"Unable to open file \n");
		}
	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);

			/*  		F_GETLK   : 获取锁信息 */
			res = fcntl(file_desc,F_GETLK,&region_to_test);
			if(res == -1)
				{
					fprintf(stderr,"lock fail,F_WRLCK retutn \n");
					exit(EXIT_FAILURE);
				}
			if(region_to_test.l_pid != -1)
				{
					printf("lock fail,F_WRLCK retutn \n");
					show_lock_info(&region_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);

			/*      F_GETLK : 获取锁信息 */
			res = fcntl(file_desc,F_GETLK,&region_to_test);
			if(res == -1)
				{
					fprintf(stderr,"lock fail,F_RDLCK retutn \n");
					exit(EXIT_FAILURE);
				}
			if(region_to_test.l_pid != -1)
				{
					printf("lock fail,F_RDLCK retutn \n");
					show_lock_info(&region_to_test);						
				}
			else
				{
					printf(" F_RDLCK - Lock would succeed \n");
				}

			
		}
		close(file_desc);
		exit(EXIT_SUCCESS);
	
}

void show_lock_info(struct flock * lck)
{
	printf(" \t1_type %d ", 	lck->l_type);
	printf(" \t1_whence %d ",  lck->l_whence);
	printf(" \t1_start %d ",   lck->l_start);
	printf(" \t1_len %d ",      lck->l_len);
	printf(" \t1_pid %d \n",      lck->l_pid);
}

lockf系统调用

int lockf(int fildes,int function,off_t size_to_lock);
  • function定义如下:
F_ULOCK:解锁
F_LOCK : 设置独占锁
F_TLOCK : 测试并设置独占锁
F_TEST : 测试其他进程设置的锁
  • size_to_lock

操作的字节数,从文件的当前偏移值开始计算

lockf的接口更加简单,但是功能和灵活性比fcntl差一些


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

C++脑图 上一篇
Linux应用程序调试方法 下一篇