文件系统
目标
类似ls
的实现,如myls
ls [选项] [文件名…]
/etc/passwd、/etc/group
目录与文件
获取文件属性
- stat 通过文件路径获取属性,面对符号链接文件时获取的是其所指向的目标文件的属性
- fstat 通过文件描述符获取属性
- lstat 面对符号链接文件时,获取的是符号链接文件的属性,而
stat
获取的是链接对象的属性
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static off_t flen(const char *fname){
struct stat statres;
if (stat(fname,&statres) < 0) {
perror("tata()");
exit(1);
}
return statres.st_size;
}
int main(int argc,char **argv)
{
if (argc < 2) {
fprintf(stderr,"Usage...\n");
exit(1);
}
long len = flen(argv[1]);
printf("st_size = %ld\n",len);
exit(0);
}
注意,在unix中文件大小size
只是一个属性,不一定代表文件真正的大小(与文件系统相关)
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char **argv)
{
if (argc < 2) {
fprintf(stderr,"Usage...\n");
exit(1);
}
int fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0600);
if (fd < 0) {
perror("open()");
exit(1);
}
long err = lseek(fd,5LL*1024LL*1024LL*1024LL-1LL,SEEK_SET);
if (err == -1) {
perror("lseek");
exit(1);
}
//偏移5G大小,计算时加上LL,转成long long型,防止类型溢出。
write(fd,"",1);
return 0;
}
// blkcnt_t st_blocks; /* number of 512B blocks allocated */
cp拷贝空文件,遇到'\0'不read,直接计数器记录下来,
文件访问权限
st_mode是一个16位的位图,用于表示文件类型,文件访问权限,及特殊权限位
- _ _ _ (7种文件类型) _ t(粘住位) _ g+s _ u+s _ _ _ user _ _ _ group _ _ _ other共15位用16位的位图表示
7种文件类型b c d - l s p
不错的-老色批
- b 块设备文件
- c 字符设备文件
- d 目录
- “-” 常规文件
- l 符号链接文件
- s socket文件
- p 匿名管道文件(不可见)
//文件类型
static int ftype(const char* fname) {
if (stat(fname,&statres) < 0) {
perror("rstat()");
exit(1);
}
if (S_ISREG(statres.st_mode)) {
return '-';
}else if (S_ISDIR(statres.st_mode)) {
return 'd';
}else if(S_ISSOCK(statres.st_mode)){
return 's';
}else{
return '?';
}
umask
Linux 中umask
的工作方式与chmod
命令类似,它也用于定义文件或目录的权限。它们之间的区别在于chmod
用于改变已有文件或目录的权限,而umask
用于定义新建文件或目录的默认权限。
0666 & ~umask
- 防止产生权限过松的文件
文件权限的更改与管理
-
chmod (命令)
'ugoa’控制哪些用户对该文件访问权限将被改变:文件的所有者(u),与文件所有者同组的用户(g),其他组的用户(o),所有用户(a).因此,a在这里等同于ugo.如果没有带参数,则缺省设置为a,运行效果相同,但是在umask中设
置的位将不会受影响.- chmod a+x ??x ??x ??x
- chmod u+x ??x ??? ???
- chmod g+x ??? ??x ???
- chmod o+x ??? ??? ??x
-
fchmod()
#include <sys/stat.h>
int chmod(const char *path,mode_t mode);
int fchmod(int fd,mode_t mode); //修改一个已经成功打开的文件
粘住位
t位
$ ls -l /
drwxrwxrwt 1 root root 3.6K 2月 8 17:58 tmp
sticky bit 粘住位
如果一个可执行文件的这一位被设置了,那么在该程序第一次执行结束时,其程序的正文部分(机器指令部分)的一个副本仍被保存在交换区。下次执行该程序时能较快的装入内存。原因:交换区占用连续磁盘空间,可视为连续文件,而一个程序的正文部分在交换区也是连续存放的,而在一般的unix文件系统中,文件的各数据块很可能是随机存放的。对于常用的应用程序,常常设置他们所在文件的粘住位。
现今的unix系统大多 虚拟存储系统以及 快速文件系统,所以不再需要这种技术。
现今系统扩展了粘住位的使用范围:
如果对一个目录设置了粘住位,只有对该目录具有 写权限的用户在满足下列条件之一的情况下,才能删除或更名该目录下的文件:
拥有此文件
拥有此目录
是超级用户
目录/tmp和/var/spool/uucppublic设置了粘住位,任何用户都可以在这两个目录中创建文件。任用户对该两个目录的权限通常是r,w,x但是用户不能删除或更名属于其他人的文件。
文件系统的实质
FAT ,UFS
文件系统: 文件或数据的存储和管理
硬链接 符号链接
硬链接是目录项的同义词,且建立硬链接有限制;不能给分区建立,不能给目录建立。符号链接优点:可跨分区,可以给目录建立。
- link (命令) 创建
硬链接
其实就是在目录项
中添加一条映射 - ln() => ln -在文件之间建立连接
- unlink() 删除一个文件的硬连接 但并不是删除文件 只有当一个文件的硬链接数为0 且没有进程占用该文件时一个文件才有被删除的可能(数据可被随意改写)
- remove
- rename
文件读写时间
-
utime()
int utime(const char *filename, const struct utimbuf *times);
可更改文件的最后读的时间和最后修改的时间
目录的创建和销毁
- mkdir ()
- rmdir()
更改当前工作路径
-
chdir => cd
-
fchdir
-
getcwd //get
current working directoryint fchdir(int fd);
int chdir(const char *path);
分析目录/读取目录内容
单独调用
- glob 解析模式/通配符
*,?
//glob解析路径
static void Glob(){
glob_t globres;
int err = glob(PAT,0,&errfunc,&globres);
if (err) {
printf("Error code = %d\n",err);
}
for (int i = 0;globres.gl_pathv[i]!= NULL;i++) {
fprintf(stdout,"%s\n",globres.gl_pathv[i]);
}
}
#include<stdio.h>
#include<stdlib.h>
#include <glob.h>
#define PAT "/etc/a*.conf"
//#define PAT "/etc/.*" //隐藏文件
#if 0
static int errfunc_(const char *errpath,int eerrno){
puts(errpath);
fprintf(stderr,"ERROR MSG:%s\n",strerror(eerror()));
return 0;
}
#endif
int main(int argc,char *argv[]){
int err,i;
glob_t golbres;
err=glob(PAT,0,NULL,&golbres);
if(err){
printf("ERROR code=%d\n",err);
exit(1);
}
for(i=0;i<golbres.gl_pathc;i++){
puts(golbres.gl_pathv[i]);
}
exit(0);
}
组合调用
- opendir()
- closedir()
- readdir()
- rewenddir()
- seekdir()
- telldir()
//组合解析路径
static void PathParse(char *Path) {
DIR *dp;
struct dirent *cur;
dp = opendir(Path);
if (dp == NULL) {
perror("opendir");
exit(1);
}
while((cur = readdir(dp)) != NULL) {
fprintf(stdout,"%s ",cur->d_name);
fprintf(stdout,"type:%d ",cur->d_type);
}
closedir(dp);
}
//getcwd()的使用
char pwd[1024];
getcwd(pwd,1024);
fprintf(stdout,"%s\n",pwd);
PathParse(pwd);
Linux du (英文全拼:disk usage)命令用于显示目录或文件的大小。
du 会显示指定的目录或文件所占用的磁盘空间。
stat、du和ls命令查询的文件大小不一致,是因为它们计算文件大小的方式不同。
stat命令返回的是文件的字节数(即文件大小)。
du命令返回的是文件实际在磁盘上占用的大小(即磁盘使用情况大小)。
ls命令返回的是文件大小的近似值,通常以千字节为单位(即文件大小除以1024)。
- 综合案例
mydu.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <glob.h>
#include <string.h>
#define PATHSIZE 1024
static int path_noloop(const char *path) {
char *pos;
pos = strrchr(path,'/');
if (pos == NULL) {
exit(1);
}
if (strcmp(pos+1,".") == 0||strcmp(pos+1,"..")== 0) {
return 0;
}
return 1;
}
static int64_t mydu(const char *path) {
static struct stat statres;
static char nextpath[PATHSIZE];
glob_t globres;
int64_t sum = 0;
//非目录
if (lstat(path,&statres) < 0) {
perror("lstat()");
exit(1);
}
if (!S_ISDIR(statres.st_mode)){
fprintf(stdout,"%ld\t%s\n",statres.st_blocks / 2,path);
return statres.st_blocks;
}
//目录
//拼接路径
strncpy(nextpath,path,PATHSIZE);
strncat(nextpath,"/*",PATHSIZE);
if (glob(nextpath,0,NULL,&globres) < 0) {
fprintf(stderr,"glob()");
exit(1);
}
strncpy(nextpath,path,PATHSIZE);
strncat(nextpath,"/.*",PATHSIZE);
if (glob(nextpath,GLOB_APPEND,NULL,&globres) < 0) {
fprintf(stderr,"glob()");
exit(1);
}
sum = statres.st_blocks;
for (int i = 0;i < globres.gl_pathc;i++){
if (path_noloop(globres.gl_pathv[i]))
sum += mydu(globres.gl_pathv[i]);
}
globfree(&globres);//回收资源
return sum;
}
int main(int argc,char **argv)
{
if (argc < 2) {
fprintf(stderr,"%s\n","Usage...");
exit(1);
}
printf("%ld\t%s\n",mydu(argv[1])/2,argv[1]);
return 0;
}
系统数据文件和信息
在Linux /etc/passwd文件中每个用户都有一个对应的记录行,它记录了这个用户的一些基本属性。系统管理员经常会接触到这个文件的修改以完成对用户的管理工作。
类似于下面的例子:
从上面的例子我们可以看到,/etc/passwd中一行记录对应着一个用户,每行记录又被冒号(:)分隔为7个字段,其格式和具体含义如下:
-
/etc/passwd
账户信息- getpwuid();
- getpwnam();
-
/etc/group
组信息- getgrgid();
- getgrgrnam();
-
/etc/shadow
加密口令-
getspnam();
-
crypt(); //加密
char *crypt(const char *key, const char *salt);
salt参数要的是( 6 6 6fMVz3d6XAGZxRmN2$)这种字符串
-
getpass();
cib: 6 (加密方式) 6(加密方式) 6(加密方式)fMVz3d6XAGZxRmN2(杂质串)$xTUGOARyxLuMZe7qySQs5E8fAuAGZgOR9.KLk‐
WoIqRMSC5znsLhashESRaHVpFSZgnbGzhtiMJhUod1eacy7l. -
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <shadow.h>
#include <string.h>
int main(int argc,char **argv)
{
char *input_passwd;//来自命令行的密码原文
char *crypted_passwd;
struct spwd *shadowline;
if (argc < 2) {
fprintf(stderr,"Usage...\n");
exit(1);
}
input_passwd = getpass("PassWoed:");
shadowline = getspnam(argv[1]);
crypted_passwd = crypt(input_passwd,shadowline->sp_pwdp);
if (strcmp(shadowline->sp_pwdp,crypted_passwd) == 0)
puts("ok!");
else
puts("failed!");
return 0;
}
以上代码编译后(编译要加`-lcryp`) ,要使用root用户执行(普通用户没有权限)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <crypt.h>
#include <shadow.h>
#include <unistd.h>
int main(int argc,char *argv[]){
//struct passwd *pwdline;
char *cryptd;
char *input_pass;
struct spwd *shadow;
if(argc<2){
fprintf(stderr,"Usgae....\n");
exit(1);
}
//加密
input_pass= getpass("Password:");
shadow=getspnam(argv[1]);
cryptd=crypt(input_pass,shadow->sp_pwdp);
if(strcmp(shadow->sp_pwdp,cryptd)==0){
puts("ok!");
}else{
puts("No!");
}
//pwdline=getpwuid(atoi(argv[1]));
//printf("getpwuid===>%s\n",pwdline->pw_name);
exit(0);
}
时间戳
time_t => char * => struct_tm
- time() 从kernel中取出时间戳(以秒为单位)
- gntime() 将时间戳转换为
struct_tm
格林威治时间 - localtime() 将时间戳转换为
struct_tm
本地时间 - mktime() 将struct_tm结构体转换为时间戳,还可以检查是否溢出
- strftime(); 格式化时间字符串
time_t stamp;
struct tm *tm;
time(&stamp);
stamp = time(NULL);
tm = localtime(&stamp);
strftime(buf,BUFSIZE,"%Y-%m-%d",tm);
puts(buf);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#define BUFSIZE 1024
int main()
{
char fmttime[BUFSIZ];
int count = 0;
FILE *fptr = fopen("./log","a+");
if (fptr == NULL) {
perror("fopen()");
exit(1);
}
char buf[BUFSIZE];
while(fgets(buf,BUFSIZE,fptr) != NULL){
count++;
}
char res[BUFSIZE];
while (1){
time_t stamp;
stamp = time(NULL);
struct tm *struct_tm;
struct_tm = localtime(&stamp);
strftime(fmttime,BUFSIZE,"%Y-%m-%d %H:%M:%S",struct_tm);
fprintf(fptr,"%d %s\n",++count,fmttime);
fflush(fptr);
sleep(1);
}
fclose(fptr);
exit(0);
}
//tm->tm_mday+=100;
//(void)mktime(tm);//自动调整时间进位
进程环境
main函数
int main(int argc,char **argv)
进程的终止
- 正常终止
- 从main函数返回
- 调用exit
- 调用
_exit
或者_Exit
(系统调用) - 最后一个线程从其启动例程返回
- 最后一个线程调用
pthread_exit
钩子函数
All functions registered with atexit(3) and on_exit(3) are called,in the reverse order of their registration.
atexit()
-
异常终止
-
调用
abort
C 库函数 void abort(void) 中止程序执行,直接从调用的地方跳出。
-
接到一个信号并终止
-
最后一个线程对其取消请求作出响应
-
命令行参数的分析
getopt();getopt_long();
//#define FMTSTRSIZE 1024
char fmtstr[FMTSTRSIZE];
fmtstr[0]='\0';
//解析命令行
while(1) {
c = getopt(argc,argv,"lt-a"); // - 识别非选项的传参,识别lta;
if (c < 0){
break;
}
switch (c){
case 'l':
f.filesize = flen(argv[1]);
strncat(fmtstr,"filesize:%ld ",FMTSTRSIZE-1);
break;
case 't':
f.filetype = ftype(argv[1]);
strncat(fmtstr,"filetype:%c ",FMTSTRSIZE-1);
break;
case 'a':
PathParse(argv[optind]);
break;
default:
break;
}
}
环境变量
本质就是 KEY = VALUE
export
- getenv()
- setenv()
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
static void getEnv(char *key){
puts(getenv(key));
}
int main()
{
for (int i = 0;environ[i] != NULL;i++){
puts(environ[i]);
}
getEnv("ZSH");
return 0;
}
C程序的存储空间布局
- pmap (1)
库
#ifndef LLIST_H__
#define LLIST_H__
enum{
F = 1,
B = 2,
};
//普通节点
struct llist_node_st{
void *data;
struct llist_node_st *prev;
struct llist_node_st *next;
};
//头节点
typedef struct {
int size;
struct llist_node_st head;
} LLIST; //LLIST就是一个双向链表的头节点类型,对于链表的操作都是用head来进行的
//传入 每个数据节点的数据类型大小
LLIST *llist_careate(int size);
//传入 一个已经创好的链表的头节点,插入的数据,插入的模式
int llist_insert(LLIST *,const void *data,int mode);
//传入
void *llist_find(LLIST *head,const void* ,int (*func)(const void*,const void*));
//
int llist_delete(LLIST *head,const void* ,int (*func)(const void*,const void*));
//
int llist_fetch(LLIST *head,const void* ,int (*func)(const void*,const void*),void *);
//传入 一个已经创建好的链表头节点
void llist_travel(LLIST* head,void (*func)(const void*));
void llist_destroy(LLIST *);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "llist.h"
//传入 每个数据节点的数据类型大小
LLIST *llist_careate(int size){
LLIST *new;
new = malloc(sizeof(*new));
if (new == NULL){
return NULL;
}
new->size = size;
new->head.data = NULL;
new->head.prev = &new->head;
new->head.next = &new->head;
return new;
}
//传入 一个已经创好的链表的头节点,插入的数据,插入的模式
int llist_insert(LLIST *head,const void *data,int mode){
struct llist_node_st *newnode;
newnode = malloc(sizeof(*newnode));
if (newnode == NULL)
return -1;
newnode->data = malloc(head->size);
if (newnode->data == NULL){
return -2;
}
memcpy(newnode->data,data,head->size);
switch (mode) {
case F:
newnode->prev = &head->head;
newnode->next = head->head.next;
break;
case B:
newnode->prev = head->head.prev;
newnode->next = &head->head;
break;
default:
return -3;
}
newnode->prev->next = newnode;
newnode->next->prev = newnode;
return 0;
}
//传入 一个已经创建好的链表头节点,一个辅助遍历函数
void llist_travel(LLIST* head,void (*func)(const void *)){
struct llist_node_st *cur,*next;
for (cur = head->head.next;cur != &head->head;cur = next) {
func(cur->data);
next = cur->next;
}
}
//辅助函数
static struct llist_node_st *find_(LLIST *head,const void *key,int (*func)(const void *,const void *)){
struct llist_node_st *cur;
for (cur = head->head.next;cur != &head->head;cur = cur->next){
if (func(key,cur->data) == 0){
return cur;
}
}
return &head->head;
}
void *llist_find(LLIST *head,const void* key,int (*func)(const void*,const void*)){
return find_(head,key,func)->data;
##### 静态库
}
//
int llist_delete(LLIST *head,const void* key,int (*func)(const void*,const void*)){
struct llist_node_st *node;
node = find_(head,key,func);
if (node == &head->head){
return -1;
}else {
node->prev->next = node->next;
node->next->prev = node->prev;
free(node->data);
free(node);
return 0;
}
}
//
int llist_fetch(LLIST *head,const void* key,int (*func)(const void*,const void*),void *data){
struct llist_node_st *node;
node = find_(head,key,func);
if (node == &head->head){
return -1;
}else {
node->prev->next = node->next;
node->next->prev = node->prev;
data = node->data;
free(node->data);
free(node);
return 0;
}
}
void llist_destroy(LLIST *head) {
struct llist_node_st *cur,*next;
for (cur = head->head.next;cur != &head->head;cur = next) {
next = cur->next;
free(cur->data);
free(cur);
}
free(head);
}
CFLAGS +=-Wall -g -lstdc++ -D_FILE_OFFSET_BITS=64
CC =gcc
TARGET =DoubleLinkList
OBJ =llist.o
src =llist.c
$(TARGET):$(OBJ)
$(CC) main.c $(OBJ) -o $@
$(OBJ):$(src)
$(CC) $(src) $(CFLAGS) -c -o $@
clean:
-rm -f $(OBJ)
-rm -f $(TARGET)
静态库
- libxx.a xx指代库名
ar -cr libxx.a yyy.o
- 发布到
/usr/local/include
/usr/local/lib
- 编译
gcc -L/usr/local/lib -o main mian.o -lxx
-l
参数必须在最后,有依赖
make
ar -cr libllist.a llist.o
gcc -L./ -o main main.c -lllist
动态库
libxx.so
xx是库名gcc -shared -fpic -o libxx.so yyy.c
- 发布到
/usr/local/include
/usr/local/lib
(.h 与 .so) - /sbin/ldconfig 重读
/etc/ld.so.conf
gcc -I/usr/local/include -L/usr/local/lib -o ... lxx
重名用动态库
- 非root用户发布
- sp xx.so ~/lib
- export LD_LIBRARY_PATH=~/lib
函数跳转
适用场景: 在树结构中查找元素,找到后直接回到第一次调用处(跨函数),不用一层一层返回
- setjmp()
- longjmp()
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf save;
static void d(){
printf("%s is called\n",__FUNCTION__);
longjmp(save,2);
printf("%s is returned\n",__FUNCTION__);
}
static void c(){
printf("%s is called\n",__FUNCTION__);
d();
printf("%s is returned\n",__FUNCTION__);
}
static void b(){
printf("%s is called\n",__FUNCTION__);
c();
printf("%s is returned\n",__FUNCTION__);
}
static void a(){
int ret = setjmp(save);
if (ret == 0) {
printf("%s is called\n",__FUNCTION__);
b();
printf("%s is returned\n",__FUNCTION__);
}else {
printf("code %d return \n",ret);
}
}
int main()
{
a();
return 0;
}
资源的获取与控制
getrlimit
setrlimit
_PATH=~/lib
函数跳转
适用场景: 在树结构中查找元素,找到后直接回到第一次调用处(跨函数),不用一层一层返回
- setjmp()
- longjmp()
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
static jmp_buf save;
static void d(){
printf("%s is called\n",__FUNCTION__);
longjmp(save,2);
printf("%s is returned\n",__FUNCTION__);
}
static void c(){
printf("%s is called\n",__FUNCTION__);
d();
printf("%s is returned\n",__FUNCTION__);
}
static void b(){
printf("%s is called\n",__FUNCTION__);
c();
printf("%s is returned\n",__FUNCTION__);
}
static void a(){
int ret = setjmp(save);
if (ret == 0) {
printf("%s is called\n",__FUNCTION__);
b();
printf("%s is returned\n",__FUNCTION__);
}else {
printf("code %d return \n",ret);
}
}
int main()
{
a();
return 0;
}
资源的获取与控制
getrlimit
setrlimit