动态内存----内存管理
承接上文:
标准C
malloc/calloc/realloc/free
底层实现双向链表 每一个动态内存块有控制信息块
第一个申请33页,即使释放所有的动态内存,最开始的33页也会保存
POSIX
sbrk/brk 增量内存
底层维护的是一个指针 指向动态内存的最后一个字节下一个内存的位置
Kernel
mmap/munmap 内存映射
把虚拟内存映射到物理内存上面
内存页(4K 4096个字节byte)
mmap
文件操作
系统调用
1、Unix/Linux大部分系统功能是通过系统调用实现的,如open/close。 2、Unix/Linux的系统调用已被封装成C函数的形式,但它们并不是标准C的一部分。 3、标准库函数大部分时间运行在用户态,但部分函数偶尔也会调用系统调用,进入内核态,如malloc/free。 4、程序员自己编写的代码也可以调用系统调用,与操作系统内核交互,进入内核态,如brk/sbrk/mmap/munmap。 5、系统调用在内核中实现,其外部接口定义在C库中,该接口的实现借助软中断进入内核。 ?time命令:测试运行时间 ?real : 总执行时间 ?user : 用户空间执行时间 ?sys : 内核空间执行时间 ?strace命令:跟踪系统调用 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
为什么要有用户态和内核态?
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态。
例如 time a.out 显示结果为: 运行结果 出现总共时间 用户时间 内核时间
#include <stdio.h>
#include <stdlib.h>
void test(void){
int *p = malloc(4);
if(p==NULL){
printf("%m\n");
return ;
}
*p = 100;
free(p);
}
int func(void){
FILE *fp = fopen("a.txt","w+");
if(fp == NULL){
printf("%m\n");
return -1;
}
int num = 1024;
fwrite(&num,sizeof(num),1,fp);
int a = 0;
rewind(fp);
fread(&a,sizeof(a),1,fp);
fclose(fp);
}
int main(){
int i;
for(i=0;i<10000;i++)
func();
return 0;
}
malloc时间占比,内核态时间占比很少,因为malloc事实上只进入了一次内存态,最开始sbrk一次申请了33页,free后再申请仍是一直使用那个地方 fopen因为每次循环都需要切换到内核态,大部分时间都在内核态和用户态直接的切换,切换比函数调用开销大
例 strace命令 跟踪系统调用 strace a.out 详细显示调用过程
1.open - 打开/创建文件 2.creat - 创建空文件 3.close - 关闭文件 4.read - 读取文件 5.write - 写入文件 6.lseek - 设置读写位置 7.fcntl - 修改文件属性 8.unlink - 删除硬链接 9.rmdir - 删除空目录 10.remove - 删除硬链接(unlink)或空目录(rmdir)
ioctl读写控制,ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
ls -l 查看详细
ln a.out b.out //创建一个硬链接
ln -s a.out c.out //创建软链接
第一位:
-:普通文件
d:文件夹
l:链接文件(软链接)
s:socket文件
c:字符设备
b:块设备
p:管道文件
rwx(都是权限):r是读,w写,x执行 - 是指没有该权限
第一组rw-: -属主权限(文件拥有者) 000 在二进制数值上是0~7,例如rw-就是6
第二组r--: 属组权限(文件拥有者同组用户) 000 上同
第三组r--: 其他用户权限 000 上同
有三组身份,每种身份都有读、写、执行三种权限需要分配
1就是文件的硬链接数,表示访问该文件的一种路径方式
只有当一个文件的硬连接数为0时,该文件才真正从磁盘文件中删除,否则删除一个文件只是把该文件的硬链接数-1
硬链接数相当于访问同一片数据的不同方法,删了一个其他的在就还能访问那片空间
与硬链接对应的叫软链接(相当于window的快捷方式)
软链接文件中存储目标文件的路径
有两种方式打开软链接文件:普通打开,打开目标文件
特殊打开,打开软链接文件本身 存储了一个路径 (大小很小)例如 d.out如果是个软链接就是5个字节
软链接的话,删了源文件,后面创建的软链接就无法访问了
文件描述符
整数,用于代指一个打开的文件,内核会记录
fprintf(stdout,"Hello world\n");
fscanf(stdin,"%d",&data);
printf();其实就是调用fprintf
1、非负的整数。
2、表示一个打开的文件。
3、由系统调用(open)返回,被内核空间(后续系统调用)引用。
4、内核缺省为每个进程打开三个文件描述符:
?stdin 0 - 标准输入
?stdout 1 - 标准输出
?stderr 2 - 标准出错
#include <stdio.h>
int main (void) {
int data;
fscanf (stdin, "%d", &data);
fprintf (stdout, "标准输出:%d\n", data);
fprintf (stderr, "标准错误:%d\n", data);
return 0;
}
在#include<unistd.h>中被定义为如下三个宏:
#define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2
调用的是unix提供的c语言的接口函数
#include <stdio.h>
#include <unistd.h>
int main(){
fprintf(stdout,"Hello world!\n");
fwrite("Hello unix\n",1,12,stdout);
write(1,"Hello unix\n",12);
return 0;
}
man open
这个open后面可以确定权限
O_RDONLY - 只读。\
|
O_WRONLY - 只写。 > 只选一个
|
O_RDWR - 读写。/
互斥的
O_CREAT - 创建,不存在即创建(已存在即直接打开,并保留原内容,除非...),有此位mode参数才有效。
O_EXCL - 排斥,已存在即失败。\
> 只选一个,
O_TRUNC - 清空,已存在即清空 /
配合O_CREAT使用(有O_WRONLY/O_RDWR)。
O_NOCTTY - 非控,若pathname指向控制终端,则不将该终端作为控制终端。
O_NONBLOCK - 非阻,若pathname指向FIFO/块/字符文件,则该文件的打开及后续操作均为非阻塞模式。
O_SYNC - 同步,write等待数据和属性,被物理地写入底层硬件后再返回。
O_DSYNC - 数同,write等待数据,被物理地写入底层硬件后再返回。
O_RSYNC - 读同,read等待对所访问区域的所有写操作,全部完成后再读取并返回。
O_ASYNC - 异步,当文件描述符可读/写时,向调用进程发送SIGIO信号。
open/creat所返回的一定是当前未被使用的,最小文件描述符。
一个进程可以同时打开的文件描述符个数,受limits.h中定义的OPEN_MAX宏的限制,
POSIX要求不低于16,传统Unix是63,现代Linux是256。 但是这些都是理论上,其实可以修改
#include <unistd.h>
int close (int fd // 文件描述符);
成功返回0,失败返回-1。
O_EXCL的使用:
#include <stdio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[]){
if(argc < 2){
printf("%s filename\n",argv[0]);
return -1;
}
int fd = open(argv[1],O_RDONLY|O_CREAT|O_EXCL,0777);
if(fd == -1){
printf("%s\n",strerror(errno));
return -1;
}
close(fd);
return 0;
}
O_TRUNC的使用:
#include <stdio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[]){
if(argc < 2){
printf("%s filename\n",argv[0]);
return -1;
}
int fd = open(argv[1],O_RDONLY|O_CREAT|O_TRUNC,0777);
if(fd == -1){
printf("%s\n",strerror(errno));
return -1;
}
close(fd);
return 0;
}
man umask
umask 113
需要减去umake(权限屏蔽字) 的权限
umask 0133 属主和属组一定没有x 其他用户没有wx权限
如果没有O_APPEND就是在最开始写,就是所谓的覆盖写
#include <stdio.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main(int argc,char *argv[]){
if(argc < 2){
printf("%s filename\n",argv[0]);
return -1;
}
int fd = open(argv[1],O_WRONLY|O_CREAT,0777);
if(fd == -1){
printf("%s\n",strerror(errno));
return -1;
}
write(fd,"thanks you",10);
close(fd);
return 0;
}
上面这个程序的运行结果就是: 当O_APPEND使a.txt中的内容为thanks youthanks you 当不加上O_APPEND,结果还是为thanks youthanks you 因为始终在开头写入 覆盖前面的那个thanks you
size_t: unsigned int,无符号整数
ssize_t: int,有符号整数
实现复制
#include <stdio.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int copy(const char *srcFileName,const char *destFileName){
int sfd = open(srcFileName,O_RDONLY);
if(sfd == -1){
printf("%s\n",strerror(errno));
return -1;
}
int dfd = open(destFileName,O_WRONLY|O_CREAT|O_EXCL,0644);
if(dfd == -1){
if(errno == EEXIST){
printf("%s 文件存在!\n",destFileName);
}else{
printf("%s\n",strerror(errno));
}
close(sfd);
return -1;
}
char ch;
while(read(sfd,&ch,1)>0){
write(dfd,&ch,1);
}
close(sfd);
close(dfd);
return 0;
}
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s srcfile destfile\n",argv[0]);
return -1;
}
copy(argv[1],argv[2]);
return 0;
}
文件属性 系统I/O与标准I/O
1、当系统调用函数被执行时,需要切换用户态和内核态,频繁调用会导致性能损失。
2、标准库做了必要的优化,内部维护一个缓冲区,只在满足特定条件时才将缓冲区与系统内核同步,
借此降低执行系统调用的频率,减少进程在用户态和内核态之间来回切换的次数,提高运行性能。
标准IO都有缓冲区,目的是为了减少用户态和内核态的切换频率,从而提高效率
fwrite()先写到缓冲区的,在满足一定条件后输出来文件中
所谓的动态内存的申请和释放本质是虚拟内存和物理内存映射关系
标准C语言对文件系统的使用其实是对系统调用函数的封装
标C的IO 底层调用 系统的IO
设置了缓冲区 相对于频繁地调用系统IO,提高了效率
lseek函数的使用:
加密 实现对一个文件的加密
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
char salt = 'a';
int encode(const char *fileName){
int fd = open(fileName,O_RDWR);
if(fd == -1){
perror("open:");
return -1;
}
char ch = '\0';
while(read(fd,&ch,1)>0){
ch = ~ch+salt;
lseek(fd,-1,SEEK_CUR);
write(fd,&ch,1);
}
close(fd);
return 0;
}
int decode(const char *fileName){
int fd = open(fileName,O_RDWR);
if(fd == -1){
perror("open:");
return -1;
}
char ch = '\0';
while(read(fd,&ch,1)>0){
ch = ~(ch-salt);
lseek(fd,-1,SEEK_CUR);
write(fd,&ch,1);
}
close(fd);
return 0;
}
int main(int argc,char *argv[]){
if(argc < 3){
printf("%s 1|2 file\n",argv[0]);
return -1;
}
if(1 ==atoi(argv[1])){
encode(argv[2]);
}else if(2 == atoi(argv[1])){
decode(argv[2]);
}else{
printf("%s 1 or 2 %s\n",argv[0],argv[2]);
}
return 0;
}
获取文件大小
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
long fsize(const char *file){
int fd = open(file,O_RDONLY);
if(fd == -1)
return -1;
long int size = lseek(fd,0,SEEK_END);
close(fd);
return size;
}
int main(int argc,char *argv[]){
if(argc < 2){
printf("%s file\n",argv[0]);
return -1;
}
long int size = fsize(argv[1]);
if(size == -1){
printf("%m\n");
}else{
printf("%s %ld\n",argv[1],size);
}
return 0;
}
dup1
dup复制文件描述符 找一个最小的未被使用的文件描述符
dup2
dup2 复制文件描述符为指定的文件描述符
如果指定的文件描述符已经打开了,它会先关闭再关联
进程由内核维护着进程文件描述符表,文件描述符 file pointer
dup/dup2复制的内容,文件标识符不同,但是指向的地方是同一个,
但是如果是进程里多次打开同一个文件,它指向的地方是不一样的,是另外开了一个一模一样的结构体
#include <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
int main(){
int fd = open("a.txt",O_WRONLY|O_CREAT|O_APPEND,0644);
if(fd == -1){
printf("%m\n");
}
dup2(fd,1);
printf("hello world\n");
close(fd);
return 0;
}
所以dup2用的比较多dup2(connfd,STDOUT_FILENO);//不在控制台输出,直接输出到想输出的地方
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
int main(){
int fd1 = open("a.txt",O_WRONLY|O_APPEND);
int fd2 = open("a.txt",O_WRONLY|O_APPEND);
int fd3 = dup(fd1);
lseek(fd1,0,SEEK_END);
printf("%ld\n",lseek(fd1,0,SEEK_CUR));
printf("%ld\n",lseek(fd2,0,SEEK_CUR));
printf("%ld\n",lseek(fd3,0,SEEK_CUR));
char str[100] = "Hello wrold";
write(fd1,str,strlen(str));
lseek(fd1,0,SEEK_END);
lseek(fd2,0,SEEK_END);
lseek(fd3,0,SEEK_END);
printf("%ld\n",lseek(fd1,0,SEEK_CUR));
printf("%ld\n",lseek(fd2,0,SEEK_CUR));
printf("%ld\n",lseek(fd3,0,SEEK_CUR));
return 0;
}
DNS 服务器
sever -> html ->cilent DNS服务器是计算机域名系统 (Domain Name System 或DomainName Service)的缩写,它是由域名解析器和域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器。其中域名必须对应一个IP地址,而IP地址不一定有域名。域名系统采用类似目录树的等级结构。域名服务器为客户机/服务器模式中的服务器方,它主要有两种形式:主服务器和转发服务器。将域名映射为IP地址的过程就称为"域名解析"。
sync/fsync/fdatasync
1、大多数磁盘I/O都通过缓冲进行,写入文件其实只是写入缓冲区,直到缓冲区满,才将其排入写队列。 2、延迟写降低了写操作的次数,提高了写操作的效率,但可能导致磁盘文件与缓冲区数据不同步。 3、sync/fsync/fdatasync用于强制磁盘文件与缓冲区同步。 4、sync将所有被修改过的缓冲区排入写队列即返回,不等待写磁盘操作完成。 5、fsync只针对一个文件,且直到写磁盘操作完成才返回。 6、fdatasync只同步文件数据,不同步文件属性。
execve 创建进程
//下面这个是承接,这个代码是没法出结果的
#include <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc,char *argv[]){
if(argc < 2){
return -1;
}
int fd = atoi(argv[1]);
char str[] = "Hello wrold hello unix";
printf("fd = %d\n",fd);
int ret = write(fd,str,strlen(str));
if(ret == -1){
printf("%s\n",strerror(errno));
return -1;
}
close(fd);
return 0;
}
fcntl
有了fcntl就可以设置标志关闭了b.out,execve可以通过在a.out执行,因为这个时候对a.out来说fd打开了
#include <stdio.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(){
int fd = open("b.txt",O_WRONLY|O_CREAT,0644);
if(fd == -1){
perror("open:");
return -1;
}
int st = 0;
st = fcntl(fd,F_GETFD);
char sfd[10] ={};
sprintf(sfd,"%d",fd);
char *argv[] = {"a.out",sfd,NULL};
char *envp[] = {NULL};
execve("a.out",argv,envp);
return 0;
}
为了效率着想,都是非阻塞的,不能死等,用户没输入就先执行其他的
int fcntl(fd,F_SETFL,O_APPEND|O_NONBLOCK)
这是后续项目中会经常碰到的题目
flags.c 打印文件状态
#include<stdio.h>
#include<fcntl.h>
void pflags(int flags){
printf("文件状态标志(%08x):",flags);
struct {
int flag;
const char *desc;
}flist[] = {
O_RDONLY, "O_RDONLY",
O_WRONLY, "O_WRONLY",
O_RDWR, "O_RDWR",
O_APPEND, "O_APPEND",
O_CREAT, "O_CREAT",
O_EXCL, "O_EXCL",
O_TRUNC, "O_TRUNC",
O_NOCTTY, "O_NOCTTY",
O_NONBLOCK, "O_NONBLOCK",
O_SYNC, "O_SYNC",
O_DSYNC, "O_DSYNC",
O_RSYNC, "O_RSYNC",
O_ASYNC, "O_ASYNC"
};
size_t i;
int first=1;
for(i=0;i<sizeof(flist) / sizeof(flist[0]);i++){
if(flags & flist[i].flag){
printf("%s%s",first?"" : "|",flist[i].desc);
first=0;
}
}
printf("\n");
}
int main(){
int fd=open("flags.txt",O_WRONLY|O_CREAT|O_TRUNC|O_ASYNC,0644);
if(fd==-1){
perror("open");
return -1;
}
int flags=fcntl(fd,F_GETFL);
if(flags==-1){
perror("fcntl");
return -1;
}
pflags(flags);
if(fcntl(fd,F_SETFL,O_RDWR|O_APPEND|O_NONBLOCK)==-1){
perror("fcntl");
return -1;
}
pflags(flags);
close(fd);
return 0;
}
文件锁
进程A对a.txt 写入hello world
进程B对a.txt写入thank you
如果有多个进程同时进行写的时候,程序会有问题
#include <fcntl.h>
int fcntl (int fd, int cmd, struct flock* lock);
struct flock { short int l_type; // 锁的类型:
// F_RDLCK/F_WRLCK/F_UNLCK
// (读锁/写锁/解锁) short int l_whence; // 偏移起点:
// SEEK_SET/SEEK_CUR/SEEK_END
// (文件头/当前位置/文件尾) off_t l_start; // 锁区偏移,从l_whence开始 off_t l_len; // 锁区长度,0表示锁到文件尾
pid_t l_pid; // 加锁进程,-1表示自动设置 };
文件锁cmd取值:
F_GETLK - 测试lock所表示的锁是否可加。
若可加则将lock.l_type置为F_UNLCK,
否则通过lock返回当前锁的信息。
F_SETLK - 设置锁定状态为lock.l_type,
成功返回0,失败返回-1。
若因其它进程持有锁而导致失败,
则errno为EACCES或EAGAIN。
F_SETLKW - 设置锁定状态为lock.l_type,
成功返回0,否则一直等待,
除非被信号打断返回-1。
?1) 既可以锁定整个文件,也可以锁定特定区域。 ?2) 读锁(共享锁)、写锁(独占锁/排它锁)、解锁。 ?3) 文件描述符被关闭(进程结束)时,自动解锁。 ?4) 劝谏锁(协议锁)、强制锁。 ?5) 文件锁仅在不同进程间起作用。 ?6)通过锁同步多个进程对同一个文件的读写访问。
大体分为: 写锁:独占锁、排它锁 当一个进程对一个文件上了写锁,其他进程不能再对该文件进行上锁 读锁:共享锁 允许多个进程对同一个文件进行上读锁,但是当一个进程对文件上了读锁之后,其他进程不能上写锁。 解锁
锁的意义在于多个进程对同一个文件进行读写过程中,一定要有先后顺序 文件锁只对多个进程有效,同一个进程可以多次上读写锁 文件锁针对两个及以上多个终端而言,一个终端关闭后,即所有该终端进行的锁操作都失效了
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void read_lock(struct flock *lock){
printf("输入锁的起始位置参考点(SEEK_SET 0,SEEK_CUR 1,SEEK_END 2):");
int whence = 0;
scanf("%d",&whence);
lock->l_whence = whence==0?SEEK_SET:(whence==1?SEEK_CUR:SEEK_END);
printf("输入距离参考位置偏移距离:");
off_t offset = 0;
scanf("%ld",&offset);
lock->l_start = offset;
printf("请输入锁的长度:");
off_t len = 0;
scanf("%ld",&len);
lock->l_len = len;
lock->l_pid = -1;
}
void write_lock(struct flock *lock){
if(lock->l_type == F_RDLCK){
printf("读锁 ");
}else{
printf("写锁 ");
}
int w = lock->l_whence;
printf("锁的参考位置:%s ",w==SEEK_SET?"SET":(w==SEEK_CUR?"CUR":"END"));
printf("偏移参考位置距离:%ld ",lock->l_start);
printf("锁的长度:%ld ",lock->l_len);
printf("上锁进程为:%d\n",lock->l_pid);
}
void menu(void){
printf("***文件锁功能测试***\n");
printf("** 1.上读锁 \n");
printf("** 2.测试是否可以上读锁 \n");
printf("** 3.上写锁 \n");
printf("** 4.测试是否可以上写锁 \n");
printf("** 5.解锁 \n");
printf("** 0.退出 \n");
printf(">>>");
}
void run(void){
int fd = open("a.txt",O_RDWR);
if(fd == -1){
printf("%m\n");
return;
}
printf("进程号:%d\n",getpid());
struct flock lock = {};
for(;;){
menu();
int opt = 0;
scanf("%d",&opt);
if(opt>=1 && opt<=4){
read_lock(&lock);
switch(opt){
case 1:
case 2:lock.l_type = F_RDLCK;break;
case 3:
case 4:lock.l_type = F_WRLCK;break;
}
int ret = 0;
if(opt==2||opt==4){
ret = fcntl(fd,F_GETLK,&lock);
if(ret == 0){
if(lock.l_type == F_UNLCK){
printf("可以上锁!\n");
}else{
printf("不可以上锁!\n");
write_lock(&lock);
}
}else{
printf("获得锁信息失败!\n");
}
}else{
printf("如果不能上锁,是否一直等待(0.等 1.立即返回):");
int w = 0;
scanf("%d",&w);
int cmd = w==0?F_SETLKW:F_SETLK;
ret = fcntl(fd,cmd,&lock);
if(ret == 0){
printf("上锁成功!\n");
}else{
printf("上锁失败!\n");
if(errno == EAGAIN || errno == EACCES){
printf("其他进程已上锁!\n");
}
}
}
}else if(opt==5){
lock.l_type = F_UNLCK;
printf("请输入解锁的信息:\n");
read_lock(&lock);
int ret = fcntl(fd,F_SETLK,&lock);
if(ret == 0){
printf("解锁成功!\n");
}else{
printf("解锁失败!\n");
}
}else if(opt == 0){
break;
}else{
printf("没有该操作!\n");
}
}
close(fd);
}
int main(){
run();
return 0;
}
stat/fstat/lstat获取文件属性
软链接:是个符号
stat获取到的是目标文件
lstat获得软链接本身
有关S_ISUID/S_ISGID/S_ISVTX的说明
?1) 具有S_ISUID/S_ISGID位的可执行文件 其有效用户ID/有效组ID, 并不取自由其父进程(比如登录shell)所决定的, 实际用户ID/实际组ID, 而是取自该可执行文件的属主ID/属组ID。 如:/usr/bin/passwd ?2) 具有S_ISUID位的目录, 其中的文件或目录除root外, 只有其属主可以删除。 ?3) 具有S_ISGID位的目录, 在该目录下所创建的文件,继承该目录的属组ID, 而非其创建者进程的有效组ID。 ?4) 具有S_ISVTX位的可执行文件, 在其首次执行并结束后, 其代码区将被连续地保存在磁盘交换区中, 而一般磁盘文件中的数据块是离散存放的。 因此,下次执行该程序可以获得较快的载入速度。 现代Unix系统大都采用快速文件系统,已不再需要这种技术。 ?5) 具有S_ISVTX位的目录, 只有对该目录具有写权限的用户, 在满足下列条件之一的情况下, 才能删除或更名该目录下的文件或目录: ?A. 拥有此文件; ?B. 拥有此目录; ?C. 是超级用户。 任何用户都可在该目录下创建文件,任何用户对该目录都享有读/写/执行权限。 但除root以外的任何用户在目录下,都只能删除或更名属于自己的文件。
stat结构体中有一个st_mode常使用,
S_ISDIR() - 是否目录
S_ISREG() - 是否普通文件
S_ISLNK() - 是否软链接
S_ISBLK() - 是否块设备
S_ISCHR() - 是否字符设备
S_ISSOCK() - 是否Unix域套接字
S_ISFIFO() - 是否有名管道
具有S_ISUID/S_ISGID位的可执行文件 权限x变成s
ISVTX x变t
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<time.h>
const char *mtos(mode_t m){
static char s[11];
if(S_ISDIR(m)) strcpy(s,"d");
else
if(S_ISLNK(m)) strcpy(s,"l");
else
if(S_ISBLK(m)) strcpy(s,"b");
else
if(S_ISCHR(m)) strcpy(s,"c");
else
if(S_ISSOCK(m)) strcpy(m)) strcpy(s,"s");
else
if(S_ISFIFO(m)) strcpy(s,"p");
else
strcpy(s,"-");
strcat(s,m & S_IRUSR ? "r" : "-");
strcat(s,m & S_IWUSR ? "w" :"-");
strcat(s,m & S_IXUSR ? "x" : "-"):
strcat (s, m & S_IRGRP ? "r" : "-");
strcat (s, m & S_IWGRP ? "w" : "-");
strcat (s, m & S_IXGRP ? "x" : "-");
strcat (s, m & S_IROTH ? "r" : "-");
strcat (s, m & S_IWOTH ? "w" : "-");
strcat (s, m & S_IXOTH ? "x" : "-");
if(m & S_ISUID)
s[3]=(s[3] == 'x' ? 's' : 'S');
if(m & S_ISGID)
s[6]=(s[6] == 'x' ? 's' : 'S');
if(m & S_ISVTX)
s[9]=(s[9] == 'x' ? 't' : 'T');
return s;
}
const char *ttos(time_t t){
static char s[20];
struct tm*lt=loacltime(&t);
sprintf(s,"%04d-%02d-%02d-%02d %02d:%02d:%02d");
lt=tm_year + 1900,lt->tm_mon + 1,lt->tm_maday;
lt=tm_hour,lt->tm_min,lt->tm_sec);
return s;
}
int main(int argc,char *argc[]){
if(argc<2)
goto usage;
struct stat st;
if(argc<3){
if(stat(argv[1].&st)==-1){
perror("stat");
return -1;
}
}else if(!strcmp(argv[2],"-l")){
if(lstat(argv[1],&st)==-1){
perror("lstat");
return -1;
}
}else
gota usage;
printf (" 设备ID:%u\n", st.st_dev);
printf (" i节点号:%u\n", st.st_ino);
printf (" 模式:%s\n", mtos (st.st_mode));
printf (" 硬链接数:%u\n", st.st_nlink);
printf (" 属主ID:%u\n", st.st_uid);
printf (" 属组ID:%u\n", st.st_gid);
printf (" 特殊设备ID:%u\n", st.st_rdev);
printf (" 总字节数:%u\n", st.st_size);
printf (" I/O块字节数:%u\n", st.st_blksize);
printf ("占用块(512字节)数:%u\n", st.st_blocks);
printf (" 最后访问时间:%s\n", ttos (st.st_atime));
printf (" 最后修改时间:%s\n", ttos (st.st_mtime));
printf (" 最后状态改变时间:%s\n", ttos (st.st_ctime));
return 0;
usage:
fprintf (stderr, "用法:%s <文件> [-l]\n", argv[0]);
return -1;
}
要形成的新的设计思路:
设置值 环境变量 处理方式 等级
设置新的值,并且返回旧的值
当事情处理完之后,要恢复原样
umask
mode_t umask(mode_t cmask);
为进程设置文件权限屏蔽字,并返回以前的值,此函数永远成功。
其实就是原始权限大小-umask设定的大小,得到目前的权限大小
chmod/fchmod
chmod给的是文件名
chmod 777 a.txt // 修改权限
chmod u+x a.txt
chmode u-rw a.txt
主u
组g
其他o
fchmod 修改文件权限 fchmod给的是文件标识符
chown/fchown
chown 修改属主和属组权限
fchown 修改属主和属组权限
lchown 修改属主和属组权限 针对软链接
truncate/ftruncate
truncate修改文件长度
ftruncate修改文件长度
link/unlink/remove/rename
创建多级目录-p
mkdir -p node/day01
rm 不能删除非空目录
rm -r 能删除
rm -i 询问是否删除
rm -f
link 创建硬链接
unlink 删除硬链接
remove 删除文件等同于unlink 删除目录等同rmdir(不能删除非空目录)
rename 修改文件名或者目录名
symlink/readlink
symlink 创建软链接
命令行实现:ln -s 目标文件 链接文件
readlink 读取软链接本身的内容(目标文件的路径)
open 读取到的是目标文件的内容
//对于软链接这个函数有错,因为软链接的大小只是名字的字节大小,而这个函数对于软链接算的是指向的那个文件
long filesize(const char *file){
int fd=open(file,O_RDONLY);
off_t size=lseek(fd,0,SEEK_END);
close(fd);
return size;
}//所以要自己注意区别
mkdir/rmdir
mkdir/rmdir 创建或者删除非空目录
chdir/fchdir/getcwd
chdir/fchdir 改变工作路径 工作目录是进程的属性,只影响调用进程本身。
getcwd 获取当前工作路径 相当于pwd
opendir
tree node
树形显示
#include<stdio.h>
#include<dirent.h>
#include<errno.h>
#include<limits.h>
#include<unistd.h>
#include<sys/stat.h>
#include<stdlib.h>
int tree(const char *path,int deep){
printf(“%*s”,deep,” ”);
printf(“%s\n”,path);
DIR *dir=opendir(path);
if(dir==NULL) return -1;
struct dirent *pd= NULL;
while((pd=readdir(dir))!=NULL){
if(pd->d_type==DT_DIR){
if(strcmp(pd->d_name,”.”_==0 || strcmp(pd->d_name,”..”)==0){
continue;
}
char npath[256]={};
strcpy(npath,path);
strcat(npath,”/”);
strcat(npath,pd->d_name);
tree(npath,deep+4);
}else{
printf(“%*s”,deep+4,” ”);
printf(“%s\n”,pd->d_name);
}
}
}
int main(int argc,char *argv[]){
if(argc<2){
printf(“%s path\n”,argv[0]);
return -1;
}
struct stat st={};
if(stat(argv[1],&st)==-1){
perror(“stat:”);
return -1;
}
if(S_ISDIR(st.st_mode){
tree(argv[1],0);
}else{
printf(“%s not dirent!\n”,argv[1]);
}
return 0;
}
有提到一个后面真实写项目会遇到的情况
#include<limits.h>
realpath(const char *path,char *resolved_path)
realpath是用来将参数path所指的相对路径转换成绝对路径,
然后存于参数resolved_path所指的字符串数组或指针中的一个函数。
其实就是实际情况中会遇到 ubuntu/test/../node/m
ubuntu/tt/../node/m
有这种..的情况是无法进行比较目录的,绝对路径其实是ubuntu/node/m 因为..相当于退回上一级
|