linux系统编程(二)--文件操作

1.0    文件描述符

        每个进程启动后会自动打开三个文件描述符 0、1、2   

         分别对应于宏 STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO    

2.0   标准IO的缓冲类型

       缓冲类型可以分为:无缓冲,行缓冲,与全缓冲

        标准输出是"行缓冲",即遇到换行符或进程结束才会真正执行 IO 操作

         标准出错中"非缓冲"的,即立即进行 IO 操作

         如果是磁盘文件,标准 IO 会在其缓冲区填满后才真正进行 IO 操作

//获取缓冲区的大小
void get_buf(FILE *fd)
{
    printf("buf star:%p\n",fd->_IO_buf_base);
    printf("buf end:%p\n",fd->_IO_buf_end);
    printf("%d\n",(int )(fd->_IO_buf_end - fd->_IO_buf_base));
}

	//让缓冲区在buf上分配
	 if(setvbuf(fp,buf,_IOFBF,1314) == 0)
	{
		printf("setvbuf OK\n");
	}	

3.0 打开和关闭文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

打开文件,返回文件描述符,失败返回 -1:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

flags:
1. 必须包含以下三个之一:
O_RDONLY, O_WRONLY, O_RDWR

2. 还可以包含以下 0 到 n 个创建标志(按位或):
O_CLOEXEC,O_CREAT,O_DIRECTORY,O_EXCL,O_NOCTTY,O_NOFOLLOW,O_TMPFILE

3. 以及其它文件状态标志(按位或)

O_CREAT|O_EXCL 如果文件已存在,则返回失败
O_TRUNC 截短文件长度为 0




创建文件,返回文件描述符,失败返回 -1:
int creat(const char *pathname, mode_t mode);

等价于 open() 函数的 O_CREAT|O_WRONLY|O_TRUNC 标志

以上两个函数的 mode 可能使用宏或 5 位八进制数来表示权限


#include <unistd.h>

关闭文件
int close(int fd);

  4.0 文件读写

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

读从文件描述符 fd 代表的文件读取最多 count 个字节数据到 buf 中
如果 count 超了 SSIZE_MAX,则结果未定义

返回值 <= count,-1 表示出错,0 表示文件尾,其它的表示成功
成功并不代表读取到了 count 个字节

ssize_t write(int fd, const void *buf, size_t count);

将 buf 中的最多 count 个字节数据写入 fd 文件中
返回值 <= count,-1 表示出错,0 表示什么也没有写,其它表示成功
成功并不代表写入了 count 个字节

	// 复制源文件到目的文件
	while (1)
	{
		cnt = read(srcfd, buf, sizeof buf);
		if (-1 == cnt)
		{
			perror("读文件失败");
			break;
		}
		else if (0 == cnt)
		{
			printf("复制完毕\n");
			break;
		}
		  
		// 要写入的 cnt 个字节不一定能一次写成功
		// 可以加入代码实现必须写完 cnt 个字节才进行下一轮复制
     
		/*if (-1 == write(destfd, buf, cnt))
		{	
			perror("写文件失败");
			break;
		}*/

        char *p = buf;
        int num ;

        while(1)
        {
            num = write(destfd, p , cnt);
        
            if(-1 == num)
            {
			    perror("写文件失败");
			    break;
            }
            else if(num < cnt)
            {
                cnt = cnt - num;
                p = p + num;
            }
            else
            {
                 break;
            }
         }

	}


5.0 文件读写效率

       缓冲区越大,效率越高。

6.0   lseek 

#include <sys/types.h>
#include <unistd.h>

移动 fd 文件的位置指针到基于 whence 偏移 offset 个字节
off_t lseek(int fd, off_t offset, int whence);

whence 取值:
SEEK_SET  文件头
SEEK_CUR  当前位置
SEEK_END  文件尾

7.0  link硬链接

     

#include <unistd.h>

为已存在的 oldpath 文件创建硬链接 newpath
如果 newpath 已存在,则不会被覆盖
成功返回 0,失败返回 -1
int link(const char *oldpath, const char *newpath);

8.0  symlink符号链接

#include <unistd.h>

为已存在的文件/目录 target 创建符号链接 linkpath
符号链接可以指向已存在的文件,也可以指向不存在的文件
如果 linkpath 已存在则不会被覆盖
成功返回 0,失败返回 -1
int symlink(const char *target, const char *linkpath);

9.0 unlink删除文件

#include <unistd.h>

删除 pathname 文件,并减少其硬链接数目,不能删除目录
如果是最后一个硬链接,则会从文件系统删除此文件
此时如果有任何进程要打开此文件,则直到进程关闭了打开的文件
才会真正删除它
如果删除的是符号链接文件,则删除符号链接文件本身
如果删除套接字、FIFO 或设备,则名字会被删除,但拥有它的进程
可以继续使用它
成功返回 0,失败返回 -1
int unlink(const char *pathname);

10  remove删除文件或目录

#include <stdio.h>

删除 pathname 文件或目录
它调用 unlink 删除文件,调用 rmdir 删除目录
删除目录时,目录必须为空
成功返回 0,失败返回 -1
int remove(const char *pathname);

11 mkdir创建目录

#include <sys/stat.h>
#include <sys/types.h>

创建 pathname 目录,设置模式为 mode
模式=(mode & ~umask & 0777)
成功返回 0,失败返回 -1

int mkdir(const char *pathname, mode_t mode);

int main(int argc, char **argv)
{
	char *pathname;

	if (2 != argc)
	{
		printf("用法:%s <目录名>\n", argv[0]);
		return -1;
	}
	pathname = argv[1];

	// 创建目录
	if (-1 == mkdir(pathname, 0755))
	{
		perror("创建目录失败");
		return -1;
	}

	return 0;
}

12  rmdir删除目录

#include <unistd.h>

删除 pathname 目录,目录必须为空
成功返回 0,失败返回 -1
int rmdir(const char *pathname);

13 chdir切换目录

#include <unistd.h>

切换工作目录到 path
成功返回 0,失败返回 -1
int chdir(const char *path);
int fchdir(int fd);

获取当前工作目录的绝对路径,保存到大小为 szie 的 buf 中
glibc 库的 getcwd 在 buf 为 NULL 时,会使用 malloc 动态
分配内存用于存放路径,此时调用者必须使用 free 来释放内存
char *getcwd(char *buf, size_t size);

获取工作目录的绝对路径,它不会动态分配内存来存放路径,
用户必须提供不小于 PATH_MAX 大小的内存用于存放路径
char *getwd(char *buf);

获取工作目录的绝对路径,使用 malloc 分配内存来存放路径,
调用者必须使用 free 来释放分配的内存
char *get_current_dir_name(void);

以上函数,成功返回指向路径的字符串指针,失败返回 NULL

注意:
    进程用 chdir 函数切换目录,只在当前进程下有效,当
	退出进程回到父 shell,不会影响父 shell 的工作目录


int main(int argc, char **argv)
{
	char *path, pathbuf[256] = "";

	if (2 != argc)
	{
		printf("用法:%s <目录>\n", argv[0]);
		return -1;
	}
	path = argv[1];

	// 切换目录
	if (-1 == chdir(path))
	{
		perror("切换目录失败");
		return -1;
	}

	// 获取当前工作目录
	if (NULL == getcwd(pathbuf, sizeof pathbuf))
	{
		perror("获取当前工作目录失败");
		return -1;
	}

	printf("已切换到目录:%s\n", pathbuf);

	return 0;
}

14  扫描目录

#include <sys/types.h>
#include <dirent.h>

打开目录 name 或 fd
成功返回目录流指针,指针指向目录流的第一个入口;
失败返回 NULL
DIR *opendir(const char *name);
DIR *fdopendir(int fd);

#include <dirent.h>

读目录 dirp
成功返回目录流的下一个入口(即目录项);
失败或到达目录流尾返回 NULL。到达流尾时不会修改 errno 的值
注意,返回值有可能被后续的调用所覆盖,因此此函数是“不可重入的”
struct dirent *readdir(DIR *dirp);
 
以下是“可重入”版本
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

目录流入口结构体
struct dirent {
	ino_t          d_ino;       /* inode number */
	off_t          d_off;       /* not an offset; see NOTES */
	unsigned short d_reclen;    /* length of this record */
	unsigned char  d_type;      /* type of file; not supported
								   by all filesystem types */
	char           d_name[256]; /* filename */
};
struct dirent
{
   long d_ino; /* inode number 索引节点号 */
   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
   unsigned short d_reclen; /* length of this d_name 文件名长 */
   unsigned char d_type; /* the type of d_name 文件类型 */
   char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}


#include <sys/types.h>
#include <dirent.h>

关闭目录流 dirp
成功返回 0,失败返回 -1
int closedir(DIR *dirp);

15  文件状态

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

获取文件 pathname/fd 的状态,保存到 buf 中
如果 pathname 是链接文件,则返回的是它所指向的文件的状态
int stat(const char *pathname, struct stat *buf);

int fstat(int fd, struct stat *buf);

以下函数类似于 stat 函数,但当 pathname 是一个链接文件时,
返回的是链接文件本身的状态,而不是它所指向的文件的状态
int lstat(const char *pathname, struct stat *buf);

以上函数成功返回 0,失败返回 -1


状态结构体:
struct stat {
	dev_t     st_dev;         /* ID of device containing file */
	ino_t     st_ino;         /* inode number */
	mode_t    st_mode;        /* protection */
	nlink_t   st_nlink;       /* number of hard links */
	uid_t     st_uid;         /* user ID of owner */
	gid_t     st_gid;         /* group ID of owner */
	dev_t     st_rdev;        /* device ID (if special file) */
	off_t     st_size;        /* total size, in bytes */
	blksize_t st_blksize;     /* blocksize for filesystem I/O */
	blkcnt_t  st_blocks;      /* number of 512B blocks allocated */

	/* Since Linux 2.6, the kernel supports nanosecond
	   precision for the following timestamp fields.
	   For the details before Linux 2.6, see NOTES. */

	struct timespec st_atim;  /* time of last access */
	struct timespec st_mtim;  /* time of last modification */
	struct timespec st_ctim;  /* time of last state.s change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

测试 st_mode 的宏:
S_ISREG(m)  is it a regular file?
S_ISDIR(m)  directory?
S_ISCHR(m)  character device?
S_ISBLK(m)  block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)
S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

测试文件权限的掩码(与 st_mode 按位与来判断):
S_ISUID     04000   set-user-ID bit
S_ISGID     02000   set-group-ID bit (see below)
S_ISVTX     01000   sticky bit (see below)

S_IRWXU     00700   owner has read, write, and execute permission
S_IRUSR     00400   owner has read permission
S_IWUSR     00200   owner has write permission
S_IXUSR     00100   owner has execute permission

S_IRWXG     00070   group has read, write, and execute permission
S_IRGRP     00040   group has read permission
S_IWGRP     00020   group has write permission
S_IXGRP     00010   group has execute permission

S_IRWXO     00007   others (not in group) have read,  write,  and
		execute permission
S_IROTH     00004   others have read permission
S_IWOTH     00002   others have write permission
S_IXOTH     00001   others have execute permission

16 chmod文件权限

/*

#include <sys/stat.h>

修改文件 pathname/fd 的模式为 mode
成功返回 0,失败返回 -1
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);

 */

int main(int argc, char **argv)
{
	char *pathname;
	mode_t mode;

	if (3 != argc)
	{
		printf("用法:%s <文件> <模式>\n", argv[0]);
		return -1;
	}
	pathname = argv[1];

	sscanf(argv[2], "%o", &mode);   //转为八进制

	if (-1 == chmod(pathname, mode))
	{
		perror("修改模式失败");
		return -1;
	}
	
	return 0;
}

17_文件属性控制


#include <unistd.h>
#include <fcntl.h>

对文件 fd 进行 cmd 操作
根据 cmd 情况传递不同类型的参数 arg
int fcntl(int fd, int cmd, ... /* arg */ );

返回值:
失败返回 -1
成功除以下 cmd 之外,返回 0:
F_DUPFD  The new descriptor.
F_GETFD  Value of file descriptor flags.
F_GETFL  Value of file status flags.
F_GETLEASE Type of lease held on file descriptor.
F_GETOWN Value of descriptor owner.
F_GETSIG Value  of  signal sent when read or write becomes possible, or
zero for traditional SIGIO behavior.
F_GETPIPE_SZ, F_SETPIPE_SZ The pipe capacity.
F_GET_SEALS A bit mask identifying the seals that have been  
set  for  the inode referred to by fd.

记录锁的命令有:F_SETLK, F_SETLKW, F_GETLK
使用的数据类型为:
struct flock {
	...
		short l_type;    /* Type of lock: F_RDLCK,
							F_WRLCK, F_UNLCK */
	short l_whence;  /* How to interpret l_start:
						SEEK_SET, SEEK_CUR, SEEK_END */
	off_t l_start;   /* Starting offset for lock */
	off_t l_len;     /* Number of bytes to lock */
	pid_t l_pid;     /* PID of process blocking our lock
						(set by F_GETLK and F_OFD_GETLK) */
	...
};
可以锁住文件的一部分:基于 l_whence 偏移 l_start 字节的 l_len 大小
锁住整个文件:l_whence = SEEK_SET, l_start = 0, l_len = 0

写锁:一个进程先加写锁成功,则其它进程无论读还是写锁都不能加
读锁:一个进程先加读锁成功,其它进程加可以读锁,但不能加写锁
int main(int argc, char **argv)
{
	char *pathname;
	int fd, i;
	struct flock lock;
	char *str;

	if (3 != argc)
	{
		printf("用法:%s <文件> <字符串>\n", argv[0]);
		return -1;
	}
	pathname = argv[1];
	str = argv[2];

	fd = open(pathname, O_RDWR, 0644);
	if (-1 == fd)
	{
		perror("打开文件失败");
		return -1;
	}

	// 获取文件记录锁
	if (-1 == fcntl(fd, F_GETLK, &lock))
	{
		perror("获取记录锁失败");
		goto _out;
	}

	printf("pid = %d\n", lock.l_pid);

	// 加记录锁才写入
	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	while (-1 == fcntl(fd, F_SETLK, &lock))
	{
		perror("加记录锁失败");
	}

	// 向文件写入数据
	for (i = 0; i < strlen(str); i++)
	{
		write(fd, str + i, 1);
		write(STDERR_FILENO, str + i, 1);
		usleep(200000);
	}
	putchar('\n');

	// 解记录锁
	lock.l_type = F_UNLCK;
	if (-1 == fcntl(fd, F_SETLK, &lock))
	{
		perror("解记录锁失败");
		goto _out;
	}

_out:
	// 关闭文件
	close(fd);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/h490516509/article/details/84993396
今日推荐