chmod
int chmod(const char *path, mode_t mode)
将文件read的文件权限变成所有者有读写权限,其他用户只有读的权限。这和shell指令中的chmod有相同的功效。
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h> /* for chmod */
#include <sys/types.h> /* for chmod */
int main(){
system("touch read; ls -l |grep read");
chmod("./read",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
system("ls -l |grep read");
return 0;
}
umask
mode_t umask(mode_t mask)
先不讨论掩码,
创建文件默认最大权限为666 (-rw-rw-rw-),默认创建的文件没有可执行权限x位。
创建目录默认最大权限777(-rwx-rwx-rwx),默认创建的目录属主是有x权限。
linux系统每一个新建的文件都被赋予某种默认的权限,这由权限掩码设置函数umask决定。umask()h函数还将返回上一个mask
umask()会将系统umask值设成参数mask&0777后的值,然后将先前的umask值返回.
$ cat file.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(){
mode_t mask = 0755;
mode_t oldmod = umask(mask);
printf("old mask is 0%o\n",oldmod);
printf("new mask is 0%o\n",umask(mask));
return 0;
}
$ ./file
old mask is 02
new mask is 0755
$ cat file1.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(){
system("touch f1; ls f1 -l");
mode_t mask = 0123;
umask(mask);
system("touch f2; ls f2 -l");
return 0;
}
$ ./file1
-rw-rw-r--. 1 edemon edemon 0 Nov 20 23:06 f1
-rw-r--r--. 1 edemon edemon 0 Nov 20 23:06 f2
分析: 文件默认的权限是val1= 0666,默认的mask是val2 = 02,两者进行运算:
0666&~0002 = 0666&0775 = 0664
第二次,mask是0123
0666&~0123 = 0666&0654 = 0644
stat
int stat(const char *file_name, struct stat *buf)
stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中
关于结构体 stat:
[edemon@CentOS include]$ sed -n '39,94p' bits/stat.h
struct stat
{
__dev_t st_dev; /* Device. */
unsigned short int __pad1;
#ifndef __USE_FILE_OFFSET64
__ino_t st_ino; /* File serial number. */
#else
__ino_t __st_ino; /* 32bit file serial number. */
#endif
__mode_t st_mode; /* File mode. */
__nlink_t st_nlink; /* Link count. */
__uid_t st_uid; /* User ID of the file's owner. */
__gid_t st_gid; /* Group ID of the file's group.*/
__dev_t st_rdev; /* Device number, if device. */
unsigned short int __pad2;
#ifndef __USE_FILE_OFFSET64
__off_t st_size; /* Size of file, in bytes. */
#else
__off64_t st_size; /* Size of file, in bytes. */
#endif
__blksize_t st_blksize; /* Optimal block size for I/O. */
#ifndef __USE_FILE_OFFSET64
__blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */
#else
__blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */
#endif
#if defined __USE_MISC || defined __USE_XOPEN2K8
/* Nanosecond resolution timestamps are stored in a format
equivalent to 'struct timespec'. This is the type used
whenever possible but the Unix namespace rules do not allow the
identifier 'timespec' to appear in the <sys/stat.h> header.
Therefore we have to handle the use of this header in strictly
standard-compliant sources special. */
struct timespec st_atim; /* Time of last access. */
struct timespec st_mtim; /* Time of last modification. */
struct timespec st_ctim; /* Time of last status change. */
# define st_atime st_atim.tv_sec /* Backward compatibility. */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
__time_t st_atime; /* Time of last access. */
unsigned long int st_atimensec; /* Nscecs of last access. */
__time_t st_mtime; /* Time of last modification. */
unsigned long int st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
unsigned long int st_ctimensec; /* Nsecs of last status change. */
#endif
#ifndef __USE_FILE_OFFSET64
unsigned long int __unused4;
unsigned long int __unused5;
#else
__ino64_t st_ino; /* File serial number. */
#endif
};
例子应用:
[edemon@CentOS workspace]$ cat stat.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main(){
struct stat buff;
int ret = stat("/etc/passwd",&buff);
printf("/etc/passwd file's size is %d, last update time is %s",buff.st_size,ctime(&buff.st_mtime));
return 0;
}
[edemon@CentOS workspace]$ ./stat
/etc/passwd file's size is 1619, last update time is Sun Nov 20 18:45:26 2016
查看一个文件的文件类型,相关的宏:
$ sed -n '131,140p' /usr/include/sys/stat.h
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR)
#define S_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
#ifdef __S_IFIFO
# define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)
#endif
#ifdef __S_IFLNK
# define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)
#endif
不知道怎么用这些宏,明明将sys/stat.h已经include了,结果还说不认识。
基于文件描述符的文件操作:带缓存的文件I/O操作和不带缓存的文件I/O操作
不带缓存的文件I/O操作在嵌入式程序设计,socket程序设计,多路I/O操作程序设计方面应用广泛。
不带缓存的文件I/O操作
function | effect |
---|---|
creat | 创建文件 |
open | 打开或创建文件 |
close | 关闭文件 |
read | 读文件 |
write | 写文件 |
lseek | 移动文件的读写位置 |
flock | 锁定文件或解除锁定 |
fcntl | 对文件描述符的各种操作 |
创建文件,文件所有者有读写可执行的权限:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(void) {
int fd = creat("creat.txt",S_IRUSR|S_IWUSR|S_IXUSR);
system("ls creat.txt -l");
return EXIT_SUCCESS;
}
打开一个文件,如果不存在则创建他,存在的话内容清空。
open("open.txt",O_CREAT|O_TRUNC,0755)
使用write和read实现copy功能,将文件/etc/sysconfig/network拷贝到./cp.txt
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
int fd1 = open("/etc/sysconfig/network",O_RDONLY);
if(fd1 < 0){
perror("network open ");
exit(1);
}
void *buff = malloc(100);
int ret = read(fd1,buff,100);
if(ret < 0){
perror("read network ");
exit(1);
}
int fd2 = open("cp.txt",O_WRONLY|O_TRUNC|O_CREAT,0644);
if(fd2 == -1){
perror("cp.txt open ");
exit(1);
}
ret = write(fd2,buff,ret);
if(ret < 0){
perror("write ");
exit(1);
}
if(buff){
free(buff);
buff = 0x0;
}
close(fd1);
close(fd2);
system("cat cp.txt");
return 0;
}
文件上锁,可以避免共享资源的使用冲突,这又分为强制性锁和建议性锁,分别对应者fcntl和flock。一般情况下系统使用强制性锁。
fcntl
fcntl的相关用法:http://blog.youkuaiyun.com/thearcticocean/article/details/52614395
带缓存的文件I/O操作
带缓存的文件I/O操作是基于输入/输出(I/O)流机制的文件操作(头文件stdio.h),是标准的I/O操作,它比不带缓存的I/O操作的程序更加方移植,符合ANSI C标准。
读文件:现将数据读入内存缓存区,缓存区装满了再读入变量。
写文件:现将数据写入内存缓存区,缓存区装满了再写入变量。
缓存区越大,操作外存的次数越少,执行的效率也就越高。不同版本的系统缓存区是不一样的。
带缓存的I/O函数主要有:
fopen(), fclose(), fgetc(), fputc(), fgets(), fputs(), fread(), fwrite(), fseek(), ftell(), rewind().
fopen
FILE *fopen(const char *path, const char *mode)
mode: 字符串”r”, “r+”,”w”, “w+”, “a”, “a+” 。 加号主要是多了读或者写的权限。
fgetc
int fgetc(FILE * stream);
fgetc()从参数stream所指的文件中读取一个字符。若读到文件尾而无数据时便返回EOF
fputc
int fputc(int c, FILE *stream)
int c 是由字符的ASCII码。
fgets
char *fgets(char *s, int size, FILE *stream);
fputs
int fputs(const char *s, FILE *stream)
若成功则返回写出的字符个数,返回EOF则表示有错误发生.
fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
读取的字符数是size*nmemb, fread()会返回实际读取到的nmemb数目,如果此
值比参数nmemb 来得小,则代表可能读到了文件尾或有错误发生,这时必须用feof()或ferror()来判断。
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fseek
int fseek(FILE *stream, long offset, int whence);
whence有这些取值:SEEK_SET(文件开头),SEEK_CUR(当前位置),SEEK_END(文件末尾)
正常情况下,函数执行后文件流的读写位置是whence + offset,offset可以是负数。
ftell
long ftell(FILE *stream);
用于获取文件流目前的读写位置。
rewind
void rewind(FILE *stream);
将文件流的读写位置移至文件的开头。等价于 fseek(FILE *stream, 0, SEEK_SET).
应用:使用f系列的带缓存I/O函数将file1.txt的内容和file2.txt的内容交叉写入file3.txt中
应该是下面的效果:
file1.txt (Human Nature - Eternal Flame):
Close your eyes, give me your hand, darling
Do you feel my heart beating
Do you understand
Do you feel the same
Am I only dreaming
Is this burning an eternal flame
file2.txt (美丽的神话):
梦中人熟悉的脸孔
你是我守侯的温柔
就算泪水淹没天地
我不会放手
每一刻孤独的承受
只因我曾许下承诺
file3.txt:
Close your eyes, give me your hand, darling
梦中人熟悉的脸孔
Do you feel my heart beating
你是我守侯的温柔
Do you understand
就算泪水淹没天地
Do you feel the same
我不会放手
Am I only dreaming
每一刻孤独的承受
Is this burning an eternal flame
只因我曾许下承诺
经测试,SEEK_SET is 0, SEEK_CUR is 1, SEEK_END is 2
它和ftell()的返回值是完全不同的。
下面是实现的例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main(){
FILE *fp1 = fopen("file1.txt","r");
if(fp1 == NULL) {
perror("file1.txt fopen ");
exit(1);
}
FILE *fp2 = fopen("file2.txt","r");
if(fp2 == NULL) {
perror("file2.txt fopen ");
exit(1);
}
FILE *fp3 = fopen("file3.txt","w");
if(fp3 == NULL) {
perror("file3.txt fopen ");
exit(1);
}
char *buff = (char *)malloc(50);
char tag1 = 1, tag2 = 0;
fseek(fp1,0,SEEK_SET); /* 移动至文件的开头 */
fseek(fp2,0,SEEK_SET);
rewind(fp3);
struct stat st1,st2,st3;
stat("file1.txt",&st1); /* 获取文件的信息 */
stat("file2.txt",&st2);
while(ftell(fp1) != st1.st_size || ftell(fp2) != st2.st_size){
if(ftell(fp1) != st1.st_size && tag1){
if(fgets(buff,50,fp1)){
if(EOF == fputs(buff,fp3)){
perror("fputs ");
exit(1);
}
}
tag1 = 0;
tag2 = 1;
}
if(ftell(fp2) != st2.st_size && tag2){
if(fgets(buff,50,fp2)){
if(EOF == fputs(buff,fp3)){
perror("fputs ");
exit(1);
}
}
tag2 = 0;
tag1 = 1;
}
}
if(buff){
free(buff);
buff = 0x0;
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
/*
$ cat file3.txt
Close your eyes, give me your hand, darling
梦中人熟悉的脸孔
Do you feel my heart beating
你是我守侯的温柔
Do you understand
就算泪水淹没天地
Do you feel the same
我不会放手
Am I only dreaming
每一刻孤独的承受
Is this burning an eternal flame
只因我曾许下承诺
*/
目录文件的操作
有关目录文件操作的函数:mkdir, opendir, closedir, readdir, scandir等
mkdir
int mkdir(const char *path, mode_t mode)
失败返回-1,成功返回0
opendir
DIR *opendir(const char *name)
opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,失败返回NULL。
closedir
int closedir(DIR *dir)
失败返回-1,成功返回0
readdir
struct dirent *readdir(DIR *dir);
readdir()返回参数dir目录流的下个目录进入点
有关结构体dirent的信息:
$ sed -n '23,35p' /usr/include/bits/dirent.h
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino;
__off_t d_off;
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256]; /* We must not include limits.h! */
};
d_ino 目录进入点的inode
d_off 目录文件开头至此目录进入点的位移
d_reclen d_name的长度,不包含NULL字符
d_type d_name 所指的文件类型
d_name 文件名
关于d_type:
sed -n '96,118p' /usr/include/dirent.h
#ifdef __USE_BSD
/* File types for `d_type'. */
enum
{
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
scandir
$ man scandir
#include <dirent.h>
int scandir(const char *dirp, struct dirent ***namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);
The function versionsort() is a GNU extension, available since glibc 2.1.
Since glibc 2.1, alphasort() calls strcoll(3); earlier it used strcmp(3).
RETURN VALUE
The scandir() function returns the number of directory entries selected or -1 if an error occurs.
例子:
查看/root下所有的目录信息。
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
void check_type(struct dirent *pdir){
switch (pdir->d_type){
case DT_UNKNOWN:
printf("it's file type is unknown\n");
break;
case DT_FIFO:
printf("it's file type is fifo\n");
break;
case DT_CHR:
printf("it's file type is char\n");
break;
case DT_DIR:
printf("it's file type is dir\n");
break;
case DT_REG:
printf("it's file type is regular\n");
break;
case DT_LNK:
printf("it's file type is link\n");
break;
case DT_SOCK:
printf("it's file type is socket\n");
break;
case DT_WHT:
printf("it's file type is whiteout\n");
break;
default:
printf("????\n");
break;
}
}
int main(){
DIR *dir = opendir("/root");
if(dir == NULL){
perror("opendir ");
exit(1);
}
struct dirent *pdir = NULL;
while((pdir = readdir(dir))){
printf("dir name: %s,\t it's inode: %d,\t inode off: %d\t ",pdir->d_name,pdir->d_ino,pdir->d_off);
check_type(pdir);
}
if(-1 == closedir(dir)){
perror("closedir ");
exit(1);
}
return 0;
}
output:
dir name: Music, it's inode: 685577, inode off: 1848726371 it's file type is dir
dir name: .tcshrc, it's inode: 652812, inode off: 1893546464 it's file type is regular
dir name: Public, it's inode: 685575, inode off: 1922090525 it's file type is dir
dir name: install.log, it's inode: 652803, inode off: 2076570234 it's file type is regular
查看根目录/下所有的子目录并按照字母顺序输出
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main(){
struct dirent **namelist = NULL;
int n, i;
if((n=scandir("/",&namelist,0,alphasort)) == -1){
perror("scandir ");
exit(1);
}
for(i=0; i<n; i++){
printf("%s\t",namelist[i]->d_name);
free(namelist[i]);
}
puts("");
free(namelist);
return 0;
}
/*
./a.out
. .. .autofsck .dbus .pulse .pulse-cookie bin boot dev etc home
lib lost+found medimisc mnt net opt proc root sbin selinux srv
sys tmp usr var
$ ls -a /
. .autofsck boot dev home lost+found misc net proc .pulse-cookie sbin srv tmp var
.. bin .dbus etc lib media mnt opt .pulse root selinux sys usr
*/
链接文件
syslink
软连接函数
int symlink(const char *oldpath, const char *newpath);
symlink() creates a symbolic link named newpath which contains the string oldpath.
创建3个软连接,一个指向正常的文件,一个指向自己,一个指向不存在的文件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
if(symlink("read","read_link") == -1){
perror("read symlink ");
exit(1);
}
if(symlink("invalid","in_link") == -1){
perror("invalid symlink ");
exit(1);
}
if(symlink("self_link","self_link") == -1){
perror("self symlink ");
exit(1);
}
system("ls *link -l");
return 0;
}
/*
./a.out
lrwxrwxrwx. 1 edemon edemon 7 Nov 22 22:46 in_link -> invalid
lrwxrwxrwx. 1 edemon edemon 4 Nov 22 22:46 read_link -> read
lrwxrwxrwx. 1 edemon edemon 9 Nov 22 22:46 self_link -> self_link
*/
[edemon@CentOS symlink]$ file in_link
in_link: broken symbolic link to `invalid'
[edemon@CentOS symlink]$ file self_link
self_link: symbolic link in a loop
[edemon@CentOS symlink]$ file read_link
read_link: symbolic link to `read'
可以看出,软连接文件即使指向不存在的文件也能创建。不过这是空悬链接(断链)。
A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent one; the latter case is known as a dangling link.
link
硬连接函数
对于软连接文件来说,删除软连接文件源文件不受影响,删除源文件两者都被删除。
对于硬连接文件来说,删除硬链接文件源文件不受影响,删除源文件硬连接文件不受影响。
硬连接文件就好像是源文件的拷贝一样的。
但是,我们需要注意: 不能给目录创建硬连接,不能给不同文件系统的文件创建硬连接。
[edemon@CentOS symlink]$ ln -P ../symlink/ s_link
ln: `../symlink/': hard link not allowed for directory
关于link函数,系统调用版本的在线手册是这样解释的:
$ man 2 link
#include <unistd.h>
int link(const char *oldpath, const char *newpath);
DESCRIPTION
link() creates a new link (also known as a hard link) to an existing file.
If newpath exists it will not be overwritten.
This new name may be used exactly as the old one for any operation; both names refer to the same file (and so
have the same permissions and ownership) and it is impossible to tell which name was the "original".
创建一个空的文件,然后创建一个指向她的硬连接,接着向链接文件中写入数据,看看源文件会发生什么,删除源文件,再查看链接文件。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
system("touch read");
link("read","read_link");
int fd = open("read_link",O_WRONLY);
char *str = "hello world!\n";
int ret = write(fd,str,strlen(str));
if(ret == -1){
perror("write ");
exit(1);
}
system("cat read; rm read; ls read_link -l");
return 0;
}
/*
/a.out
hello world!
-rw-rw-r--. 1 edemon edemon 13 Nov 23 00:16 read_link
*/