一、Linux系统文件和文件系统
1.Linux文件类型
普通文件 - || 目录文件 d || 符号链接文件 l || 字符设备文件 c || 块设备文件 b || 管道文件 p || socket文件 s
权限的10个字符中第四位是‘s’的文件 setUid可执行文件 ||
第七位是‘s’的文件 setGid可执行文件 || 第四位和第七位都是‘l’的文件 setUid+setGid文件
文件的属性被保存在文件的索引结点inode(是一个特殊的数据块),属性信息包括【文件使用的设备号、索引节点号、文件访问权限和文件类型、文件的硬链接数、UID、GID、设备文件的设备号、文件大小、包含该文件的磁盘的块大小、文件占用的磁盘块的数、最后访问时间、最后修改时间、文件状态最后改变时间】
目录的内容主要由文件名和索引节点号组成,目录中每一对文件名和索引节点号称为一个“连接”(硬连接);删除一个文件时,实质是删除目录中与该文件对应的“连接”,同时将文件属性中的硬连接数减1。
查看文件的i结点
Command: ls -i
文件描述符多用于系统调用;
例1.1
设计一个程序,要求列出当前目录下的文件信息,以及系统“/dev/sda1”和“/dev/lp0”的文件信息;
#include <stdio.h>
#include <stdlib.h>
int main()
{
int newret;
printf("列出当前目录下的文件信息\n");
newret = system("ls -l");
/*system函数*/
printf("列出/dev/sda1下的文件信息\n");
newret = system("ls -l /dev/sda1");
printf("列出/dev/lp0下的文件信息\n");
newret = system("ls -l /dev/lp0");
return 0;
}
int system(char *cmdStr) //#include <stdlib.h>包含system函数库
①调用fork()来产生一个子进程,②启动shell,③调用“/bin/sh –c cmdStr”来执行参数cmdStr锁指定的命令,/bin/sh调用失败返回127,其他失败返回-1,成功返回0.
2.文件权限
例1.2
设计程序要求函数chmod[权限修改]把系统中‘/etc’目录下的passwd文件权限设置成文件所有者可读可写,其他用户为只读权限。
#inlude <sys/types.h>
#include <sys/stat.h> //预处理:包括chmod库
int main()
{
chmod("/etc/passwd",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
return 0;
}
chmod //#include <sys/stat.h>和#include <sys/types.h>
int chmod(const char *path,mode_t mode);依据mode权限来修改指定路径的文件的权限;成功返回0,失败返回-1;
对于每个新建文件都被赋予的默认权限,由系统的权限掩码设置函数umask决定;
例1.3
设计程序,要求设置系统文件与目录的权限掩码;
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
mode_t new_umask,old_umask;
new_umask=0666;
old_umask=umask(new_umask);
printf("系统原来的掩码是:%o\n",old_umask); //运行输出是123---感觉存在点问题后期验证!
printf("系统新的权限掩码是:%o\n",new_umask); //运行输出是666
system("touch liu1");
printf("创建文件liu1");
new_umask = 0444; //修改新权限
old_umask=umask(new_umask);
printf("系统原来的掩码是:%o\n",old_umask); //输出是666
printf("系统原来的掩码是:%o\n",new_umask); //输出是444
system("touch liu2");
printf("创建文件liu2");
system("ls liu1 liu2 -l"); //ls输出文件1、2的信息
chmod("liu2",S_IWUSR|S_IXOTH);
return 0;
}
//对于文件1,权限掩码设为0666,则真实的访问权限是000,有10位-;
//对于文件2,权限掩码设为0444,则真实的访问权限是0222,有--w--w--w-;
//运行此次程序后修改源程序的源码再编译运行,创建的文件的权限不会更改,因为touch只是修改时间标记,如果要验证新的权限,需要删除原来的文件;
mode_t umask(mode_t mask); //#include <sys/stat.h> #include <sys/types.h>
返回原先系统的umask值,建立文件时,该文件真正的权限是0666-mask,建立文件夹时候,文件夹的真正权限是0777-mask;
若用户创建一个文件,则文件的默认访问权限为-rw-rw-rw-【0666】,创建目录的默认权限 drwxrwxrwx 【0777】
默认的 mode值要与 umask【八进制】 的取反值按位与出来的值才是文件的权限的值;也就是默认值减去 umask 的值;
使用命令 umask 022 后,用户权限不变,群组权限减掉 2,也就是去掉写(w)权限,其他用户减 2,也就是去掉写权限(w) ,umask命令只是改变了当前会话的umask值,再打开终端时,umask的默认值仍然是 002 ,要想永久改变 umask 值,则可以修改文件 /etc/bashrc,在文件中添加一行 umask [想要修改的值]。
3.Linux文件的其他属性
stat结构体的信息 包括有设备ID st_dev、索引节点号 st_ino、文件的连接数 st_nlink、文件字节数 st_st_size等;
文件其他属性的获得使用 stat 函数、fstat 函数或 lstat 函数;
例1.4
设计程序,应用系统函数stat获取系统中/etc目录下的passwd文件的大小;
#include <unistd.h> //预处理,包含stat函数库
#include <sys/stat.h> //预处理,包含stat函数库
int main()
{
struct stat buf; //声明stat结构体 名buf
stat("\etc\passwd",&buf);
printf("大小是:%d\n",buf.st_size);
return 0;
}
int stat(const char *file_name,struct stat *buf);
//#include <sys/stat.h> #include <unistd.h>
将参数1指向的文件状态复制到参数buf所指向的buf所指的结构中;成功返回0,失败返回-1;
二、不带缓存的文件I/O操作
基于文件描述符的文件操作有:不带缓存的文件IO操作和带缓存的文件IO操作
前者包括系统调用或API的IO操作,由OS提供,主要包括以下函数:create、open、close、read、write、lseek、flock、fcntl等;
1.文件的创建
例2.1
要求在/root目录下创建一个名称是5-5file的文件,并将此文件的权限设置为所有者具有只读权限,最后显示此文件的信息;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //create的头文件
int main()
{
int fd;
fd=create("/root/5-5file",S_IRUSR);
system("ls /root/5-5file -l");
return 0;
}
int create(const char *path,mode_t mode)
//#include <sys/types.h> #include <sys/stat.h> #include <<fcntl.h>
成功返回内核最小可用的fd,失败返回-1;
2.文件的打开和关闭
例2.2
要求在‘/root’下以可读写的方式打开一个名为‘5-6file’的文件,若该文件不存在创建此文件,若存在,则将文件清空后关闭;
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> //open库
int main(){
int fd;
if((fd=open("/root/5-6file",O_CREATE|O_TRUNC|O_WRONLY,0600))<0)
{
perror("打开文件出错");
exit(1);
}
else
{
perror("打开/创建文件,文件描述符是:"%d\n",fd);
}
if(close(fd)<0)
{
perror("关闭文件出错");
exit(1);
}
system("ls /root/5-6file -l");
return 0;
}
int open(const chr* path,int flags);
int open(const chr* path,int flags,mode_t mode); //O_CREATE要加权限
//#include <fcntl.h> #include <sys/types.h> #include <sys/stat.h>
成功返回内核最小可用的fd,失败返回-1;
flags参数:O_RDONLY O_WRONLY O_RDWR O_APPEND[追加]
O_TRUNC [设置文件长度是0,舍弃现存的数据]
O_CREATE[使用mode设置访问权限]
O_EXCL[与O_CREATE一起使用,确保打开不存在的文件]
int close(int fd); #include <unistd.h>
顺利关闭返回0,失败返回-1;
3.文件的读写操作
例2.3
对常规文件的读或写不会阻塞,不管读多少字节,read一定会在有限的时间内会返回。但终端设备/网络读则不一定,若在终端输入的数据没有换行符,调用read读终端设备会阻塞,下面程序在运行过程中等待用户从终端设备读入,若没有读入则产生堵塞;
#include <unistd.h>
#include <stdlib.h>
int main()
{
char buf[80];
int n;
n = read(STDIN_FILENO,buf,80); //标准输入
if(n<0){
perror("read fail");
exit(1);
}
write(STDOUT_FILENO,buf,n); //标准输出
printf("\n");
return 0;
}
STDIN_FILENO标准输入符的文件描述符
STDOUT_FILENO标准输出符的文件描述符
例2.4
设计C程序,完成文件的复制工作,将/etc/passwd中的文件复制到目标文件中,目标文件从键盘输入;
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fdsrc,fddes,n;
int flags=O_CREATE|O_TRUNC|O_WRONLY; //实际是int类型
int z;
char buf[20],des[20];
printf("输入目标文件名:\n");
scanf("%s",des);
fdsrc = open("/etc/passwd",O_RDONLY); //以只读方式打开源文件
if(fdsrc<0){
perror("打开源文件失败");
exit(1);
}
fddes = open(des,flags,0644); //因为有O_CREATE所以要有访问权限-rw--w--w-
if(fddes < 0){
perror("目标文件没打开");
exit(1);
}
while((n=read(fdsrc,buf,20))>0){ //read和write均有返回值,返回读入的字节数
z=write(fddes,buf,n);
if(z<0){
perror("写文件出错");
}
close(fdsrc);
close(fddes);
printf("复制文件成功");
return 0;
}
size_t read(ind fd,void *buf,size_t count); #include <unistd.h>
错误返回-1;
size_t write(int fd,void *buf,size_t count); #include <unistd.h>
错误返回-1,正确返回实际读入的字节数
4.文件的非阻塞操作
例2.5
非阻塞IO--程序打开终端/dev/tty,在打开时设O_NONBLOCK标志,程序运行时每隔一定时间6秒等待用户从终端输入,共等待30秒。每次屏幕都有提示""。30秒后执行主程序并输出以下图形;
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "tomeout\n"
int main()
{
chat buf[10];
int fd,n,i,j;
fd=open("/dev/tty",O_RDONLY|O_NONBLOCK); //以只读和非阻塞(不等待)的方式打开终端
if(fd<0)
{
perror("打开终端失败");
exit(1);
}
for(i=0;i<5;i++)
{
n=read(fd,buf,10);
if(n>=0)
{
break;
}
if(errno!=EAGAIN) //EAGAIN和 EWOULDBLOCK等效!
{
perror("read /dev/tty");
exit(1);
}
sleep(6); //程序等待6秒
write(STDOUT_FILENO,buf,n);
}
if(i==5) //说明前边一直是n<0 一直++到5
{
write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY); //超时提示
}
else
{
write(STDOUT_FILENO,buf,n); //写到标准输出
for(i=0;i<5;i++)
{
for(j=0;j<=i;j++)
{
printf("%2c",'*');
}
printf("\n");
}
close(fd);
}
return 0;
}
5.文件的上锁 /*待补充*/
例2.6
int flock(int fd,int operation) #include <sys/file.h> 建议锁
LOCK_SH : 共享锁,多个进程可以同时对一个文件上锁
LOCK_EX : 互斥锁;
LOCK_UN : 解除文件锁定
LOCK_NB : 无法锁定时,不接受阻断,马上返回进程
int fcntl(int fd,int cmd); #include <unistd.h> #include <fcntl.h> 强制性锁
int fcntl(int fd,int cmd,long arg); //cmd表示操作类型
int fcntl(int fd,int cmd,struct flock *lock);
成功返回0,错误返回-1
struct flock结构体
struct flock
{
short l_type; //锁的类型
short 1_whence; //对l_start的解释
off_t l_start; //加锁开始的位置
off_t l_len; //加锁的长度
pid_t l_pid; //加锁进程的进程id
};
三、特殊文件的操作
1.目录文件
例3.1
设计一个程序,要求打印系统目录下‘/etc/rc.d’下的所有文件和子目录的名字
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h> //目录
#include <unistd.h>
int main()
{
DIR *dir;
struct dirent *ptr;
dir = opendir("/etc/rc.d");
printf("/etc/rc.d下的子目录有:\n");
while((ptr=readdir(dir))!=null)
{
printf("%s\n",ptr->d_name);
}
closedir(dir);
}
结构体DIR:
struct _dirstream
{
void *_fd;
char *_data;
int _entry_data;
char *_ptr;
int _entry_ptr;
size_t _allocation;
size_t _size;
_libc_lock_define;
};
typedef struct _dirstream DIR; //打开一个文件目录返回 opendir()
结构体dirent的定义
struct dirent
{
ino_t d_ino; //此目录进入点的inode的节点号
ff_t d_off; //目录文件开头至此目录进入点的位移
signed short it d_reclen; //不包含NULL字符
unsigned char d_type; //所指的文件类型
har d_name[256]; //文件名
}
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 };
例3.2
设计程序要求用递归的方法列出某一个目录下的全部文件和文件夹的大小以及创建日期,包括子文件和子文件夹; //待定
#include <stdio.h>
#include <time.h>
#include <linux/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
char *wday[] = {"日","一","二","三","四","五","六"};
void list(char *name,int suojin) //参数1是要打开的目录名
{
DIR *dirname;
struct dirent *content;
struct stat sb;
struct tm *ctime;
int i;
if((dirname=opendir(name))==null)
{
printf("该目录不存在\n");
}
chdir(name); //改换工作目录
while((content=readdir(dirname))!=null)
{
for(i=0;i<suojin;i++)
{
putchar("\t");
}
if(content->d_type==4) //4是目录
{
printf("目录\t");
}
else if(content->d_type==8) //8是普通文件
{
printf("文件\t");
}
else
{
printf("其他");
}
stat(content->d_name,&sb);
ctime=gmtime(&sb.st_time);
printf("%d年%d月%d日 星期%s %d:%d:%d\t",ctime->tm_year+1900,1+ctime->tm_mon,ctime->tm_mday,wday[ctime->tm_wday],ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
printf("%d\t",sb.st_size);
printf("%s\n",content->d_name); //列出文件/目录的信息
if(content->d_type==4&&strcmp(content->d_name,"..")&&strcmp(content->d_name,"."))
{
list(content->d_name,suojin+1);
}
closedir(dirname);
chdir(".."); //该层目录的文件列完,返回父目录
}
int main(int argc,char *argv[]){
char name[256];
printf("类型\t 最后修改时间\t\t\t大小\t文件名\n");
printf("**************************************");
if(argc==1)
{
printf("Enter directory name:");
scanf("%s",name);
list(name,0);
}
else
{
list(argv[1],0);
}
}