IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> unix文件系统管理 -> 正文阅读

[系统运维]unix文件系统管理

作者:recommend-item-box type_blog clearfix

动态内存----内存管理

承接上文:
标准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);//sbrk
	if(p==NULL){
		printf("%m\n");
		return ;
	}
	*p = 100;
	//printf("%d\n",*p);
	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++)
		//test();
		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(){
	//stdout  标准输出的FILE*  stdin stderr
	fprintf(stdout,"Hello world!\n");
	fwrite("Hello unix\n",1,12,stdout);//c
	write(1,"Hello unix\n",12);	//unix c系统调用的接口
	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;
	}
	//O_EXCL 排斥  结合O_CREAT使用 如果文件存在则打开失败
	//如果文件不存在,则创建文件并打开 3
	//如果O_CREAT  必须传第三个参数 即mode_t mode 权限
	int fd = open(argv[1],O_RDONLY|O_CREAT|O_EXCL,0777);//rwxrwxrwx
	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;
	}
	//如果文件不存在,则创建文件并打开 3
	//如果O_CREAT  必须传第三个参数 即 mode_t mode 权限
	//O_TRUNC 如果文件存在则清空
	int fd = open(argv[1],O_RDONLY|O_CREAT|O_TRUNC,0777);//rwxrwxrwx
	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|O_APPEND,0777);//rwxrwxrwx
	int fd = open(argv[1],O_WRONLY|O_CREAT,0777);//rwxrwxrwx
	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){//access  stat   fstat
		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;
}

//cp 
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';//x^'a'^'a'   ~x   

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+'a';
		ch = ~(ch-salt);
		lseek(fd,-1,SEEK_CUR);
		write(fd,&ch,1);
	}
	close(fd);
	return 0;
}


//code 1|2 file
int main(int argc,char *argv[]){
	if(argc < 3){
		printf("%s 1|2 file\n",argv[0]);//1加密  2解密
		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(){
	//打开a.txt文件
	int fd = open("a.txt",O_WRONLY|O_CREAT|O_APPEND,0644);
	if(fd == -1){
		printf("%m\n");	
	}
	dup2(fd,1);//close(stdout);  1 === fd  没必要标准输入 输出
	printf("hello world\n");//CGI
	
	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[]){
/*
	const char *fds = getenv("OPENFD");
	if(fds==NULL){
		printf("Not OPENFD environ list!\n");	
		return -1;
	}
*/
	if(argc < 2){
		return -1;	
	}
	//int fd = atoi(fds);
	int fd = atoi(argv[1]);//fd 代表的文件描述符没有打开 肯定出错
	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);
	//fcntl(fd,F_SETFD,st|FD_CLOEXEC);//fd 文件描述到了a.out就关闭了
	/*
	char *argv[] = {NULL};
	char envs[100] = {};
	sprintf(envs,"OPENFD=%d",fd);
	char *envp[] = {envs,NULL};
	*/
	char sfd[10] ={};
	sprintf(sfd,"%d",fd);
	char *argv[] = {"a.out",sfd,NULL};
	char *envp[] = {NULL};
	execve("a.out",argv,envp);//创建一个进行去执行a.out程序
	//argv main函数的参数列表 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;//自动填充该进程ID
}
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);//手动创建a.txt
	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  因为..相当于退回上一级
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-05 17:44:38  更:2021-08-05 17:45:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 3:48:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计