科林网络_3 Linux操作系统

bin 可执行文件

boot 启动文件

dev 设备文件

etc 系统配置文件和脚本

media 挂载

mnt 用作临时挂载文件系统的标准位置

lib 基本共享库文件和内核模块

sys 系统文件

tmp 临时文件

usr 用户级的系统资源和应用程序

home 用户数据和配置文件

var 存放经常变化的数据

Linux 系统目录结构 | 菜鸟教程 (runoob.com)

一、Linux常用命令

1.1 Shell

1.1.1 Shell家族

1.1.1.1 cat

cat 是一个文本文件查看和连接工具。

cat [-AbeEnstTuv] [--help] [--version] fileName

    查看当前系统下有哪些shell:

1.1.1.2 echo

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

echo string

查看当前系统正在使用的shell

使用$直接引用变量

1.1.1.3 > 重定向

Linux命令之重定向 >、 >>、 1>、 2>、 1>>、 2>>、 <-CSDN博客

创建并写入文件 

1.1.2 Bash

 bash是一个为GNU计划编写的Unix shell,是许多Linux平台的内定shell。

1.2 目录和文件

1.2.1 目录内容 ls

列出当前文件夹下的路径

当前文件的类型 文件link数 创建用户 用户所属组 内存大小 时间 名称

 -表示没有这个位置对应的权限

三级权限:
RWX 当前文件创建者权限

R-X 创建者同用户组其他用户权限

R-- 其他组用户权限

Linux学习笔记—— ls-l 长格式列表_ls -l 格式-CSDN博客

 

链接文件

1.2.2 相对路径和绝对路径

1.2.3 切换目录 cd

用于改变当前工作目录的命令,切换到指定的路径。

若目录名称省略,则变换至使用者的 home 目录 (也就是刚 login 时所在的目录)。

Linux cd 命令 | 菜鸟教程 (runoob.com)

当前路径唯一时,使用TAB键进行补全 

1.2.4 查看命令路径 which

查看指定命令所在的路径

1.2.5 查看路径 pwd

查看当前工作目录的完整路径

1.2.6 树形显示目录文件 tree

按结构树的形状显示当前路径下的目录和文件

1.2.7 创建文件

1.2.7.1 mkdir 创建文件夹

创建目录DIRECTORY,可以一次创建多个。要创建文件夹或目录的用户必须对所创建的文件夹的父文件夹具有写权限。并且,所创建的文件夹(目录)不能与其父目录(即父文件夹)中的文件名重名,即同一个目录下不能有同名的(区分大小写)。

-m, --mode=模式,设定权限<模式> (类似 chmod),而不是 rwxrwxrwx 减 umask

-p, --parents  可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录;

-v, --verbose  每次创建新目录都显示信息

1.2.7.2 touch 修改文件时间

用于修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。

1.2.8 删除文件

1.2.8.1 rmdir 删除空目录

删除空目录,可以一次删除多个。

OPTION如果是-p,表示可以连同空的父目录一起删除。

1.2.8.2 rm 删除文件/目录

使用 -r 参数递归删除 

参数 -f 不询问直接删除

使用应当注意,以防删除根目录 

1.2.9 重命名/移动文件 mv

1.2.10 拷贝 cp

注意文件夹操作使用-r递归拷贝

1.2.11 查看文件内容 cat more less

查看文件里内容,输出到终端,如果cat时没跟文件名,则读标准输入,遇到\n后,输出到标准输出,终端下输入ctrl –d表示结束。

1.2.12 计算文件 wc

1.2.13  数据显示格式 od

1.2.14 查看目录大小 du

1.2.15 查看磁盘使用情况 df

默认以字节为单位,可以使用参数 -h 换算单位

 

1.2.16 链接 ln

 

 原始文件存储分为两个部分,分别是文件属性(文件名、创建用户、时间、权限)和文件内容(数据、代码),通过文件属性找到文件内容。

硬链接,创建了一个新的文件属性,指向文件内容。

操作系统通过计数器记录文件使用,通过计数器对这两个指向同一文件内容的文件属性标号。

软链接,创建新的文件属性,指向原始文件的文件属性。

当删除原始文件时,只删除了原始文件的文件属性,计数器连接数-1.当计数器归0时才删除文件内容

1.3 文件属性和用户用户组

1.3.1 打印用户名 whoami

1.3.2 chmod 更改文件目录访问权限

计算访问权限

一共9个权限分三组,分别换算成8进制,有权限位置为1没有权限位置为0

rwx——111——07

1.3.3 chown 更改文件目录的用户或组

临时提升root用户权限 sudo

1.4 查找与检索

1.4.1 find 文件名查找

模糊表达式 *

1.4.2 grep 文件内容检索

    -c : 只输出匹配行的计数。

    -i: 不区分大小写(只适用于单字符)。

    -h : 查询多文件时不显示文件名。

    -l : 查询多文件时只输出包含匹配字符的文件名。

    -n: 显示匹配行及行号。

    -s: 不显示不存在或无匹配文本的错误信息。

    -v:显示不包含匹配文本的所有行。

    -R:连同子目录中所有文件一起查找。

1.5 安装和卸载软件

1.5.1 apt-get

1.5.2 deb

1.5.3 源码安装

1.6 磁盘管理

1.6.1 mount 挂载

当分配新的磁盘空间时,需要挂载才能使用

文件系统

1.6.2 卸载umount

1.6.3 dd拷贝

1.7 压缩包管理

1.7.1 tar

1.7.2 rar

1.7.3 zip

1.8 进程管理

1.8.1 who 查看当前在线用户

PID 进程号

-uH 空闲时间 进程号 备注

Ctrl C 结束当前进程

1.8.2 ps 监控后台进程

选项部分如下:

    -e 显示所有进程

    -f 全格式

    -h 不显示标题

    -l 长格式

    -w 宽输出。

    a  显示终端上的所有进程,包括其他用户的进程。

    u  以user为第一列

    r  只显示正在运行的进程。

    x  显示没有控制终端的进程。

Head 标头:

    USER 用户名

    UID  用户ID(User ID)

    PID  进程ID(Process ID)

    PPID 父进程的进程ID(Parent Process ID)

    SID  会话ID(Session ID)

    %CPU 进程的CPU占用率

    %MEM 进程的内存占用率

    VSZ 进程所使用的虚存的大小(Virtual Size)

    RSS 进程使用的驻留集大小或者是实际内存的大小,kbytes字节

    TTY 与进程关联的终端(tty)

    STAT 进程的状态:进程状态使用字符表示的(STAT的状态码)

    R    运行  Runnable(on run queue)      正在运行或在运行队列中等待.

    S    睡眠  Sleeping       休眠中,受阻,在等待某个条件的形成或接受到信号

    I   空闲  Idle

    Z    僵死  Zombie(a defunct process)  进程已终止,但进程描述符存在,直到父进程调用wait4()系统调用后释放。

    D   不可中断  Uninterruptible sleep (ususally IO)  收到信号不唤醒和不可运行,进程必须等待直到有中断发生。

    T   停止   Terminate      进程收到SIGSTOP ,SIGSTP,SIGTIN,SIGTOU信号后停止运行。

    P   等待交换页

    W  无驻留页  has no resident pages   没有足够的记忆体分页可分配

    X   死掉的进程

    <   高优先级进程               高优先序的进程

    N   低优先级进程              低优先序的进程

    L   内存锁页    Lock         有记忆体分页分配并缩在记忆体内

    s   进程的领导者(在它之下有子进程)

    l   多进程的(使用 CLONE_THREAD,类似 NPTL pthreads)

    +   位于后台的进程组

    START  进程启动时间和日期

    TIME   进程使用的总CPU时间

COMMAND  正在执行的命令行命令

    NI     优先级(Nice)

    PRI    进程优先级编号(Priority)

    WCHAN 进程正在睡眠的内核函数名称;该函数的名称是从/root/system.map文件中获得的。

    FLAGS  与进程相关的数字标识。  

1.8.3 显示后台作业 jobs

Ctrl Z 挂起

1.8.4 后台/挂起作业移到前台 fg

1.8.5 给进程发送信号 kill

信号

9) SIGKILL 15) SIGTERM

终端,shell解析器,bin bash

后出现的pid号大

sleep 睡眠和run运行状态的都可以直接结束

T terminate 停止运行,挂起状态

15不能杀死T状态的进程

9 都可以

1.8.6 查看当前进程环境变量

env 查看当前用户所有环境变量

通过在环境变量中添加PATH,让操作系统知道你的执行文件在哪里,这样子就可以在任意位置通过名字调用可执行文件

1.9 用户管理

1.9.1 创建用户 useradd

-d 家目录

1.9.2 设置用户组 groupadd

自动创建用户同名用户组

1.9.3 设置密码 passwd

1.9.4 切换用户 su

1.9.5 root用户

root 用户和密码都是自动生成的,不能自己设置但是可以自己修改

sudu su 切换到root

1.9.6 删除用户 userdel

1.10 网络管理

1.10.1 ifconfig 网卡信息

1.10.2 ping ICMP request包

1.10.3 netstat 网络接口信息

1.10.4 nslookup 查询IP地址和域名服务器

1.10.5 finger 查询用户信息

1.11 其他命令

1.11.1 man 看手册

命令 1

系统函数 2

库函数 3

1.11.2 clear 清屏

1.11.3 alias 设置指令别名

在配置文件中设置

1.11.4 当前时间 date

1.11.5 权限掩码 umask

默认掩码0002(减去w权限)

文件夹0775

文件0664

创建时:

文件    0666

文件夹 0777

umask 0002

与掩码异或计算后得到实际权限

1.11.6 创建终端

         创建终端标签:Ctrl+Shift+t

         切换标签  Alt+n (n=1)

         新开终端 ctrl+shift+n

1.12 关机重启

1.12.1 poweroff

1.12.2 shutdown

shutdown –t 秒数 [-rkncfF] 时间 [警告讯息]

1.12.3 reboot

1.13 VIM

1.13.1 vi 文本编辑工具

1.13.1.1 进入插入模式:

i: 插入光标前一个字符

I: 插入行首

a: 插入光标后一个字符

A: 插入行未

o: 向下新开一行,插入行首

O: 向上新开一行,插入行首

1.13.1.2 进入命令模式:

ESC:从插入模式或末行模式进入命令模式

删除命令:

dd: 删除光标所在行,n dd 删除指定的行数

撤销命令:

u: 一步一步撤销

U: 一次性撤销当前行所作的所有操作

Ctr-r: 反撤销

重复命令:

.  : 重复上一次操作的命令

复制粘贴:

yy: 复制当前行,n yy 复制 n 行

p: 在光标所在位置向下新开辟一行,粘贴

查看宏定义:

[-d: 可以查看宏定义,必须先包含此宏所在的头文件

代码排版:

gg=G: 代码自动缩进排版

1.13.1.3 末行模式:“:”

退出

ESC退出插入模式进入命令行模式,:进入末行模式

q 退出(没有修改文件才能直接退出)

wq 保存并退出 可以创建文件并保存

q!不保存强制退出

1.13.2 分屏

分屏操作:

sp: 上下分屏,后可跟文件名

vsp: 左右分屏,后可跟文件名

Ctr+w+w: 在多个窗口切换

启动分屏:

1.使用大写O参数进行垂直分屏

$ vim -On file1 file2 ...

2.使用小写o参数进行水平分屏

$ vim -on file1 file2 ...

注: n是数字,表示分屏的数量,n要大于等于文件个数

关闭分屏

1.  关闭当前窗口

ctrl+w c

2.  关闭当前窗口,如果只剩最后一个,则退出vim

ctrl+w q

编辑中分屏

1.  上下分割当前打开的文件

ctrl+w s

2.  上下分割,并打开一个新的文件

:sp filename

3.  左右分割当前打开的文件

ctrl+w v

4.  左右分割,并打开一个新的文件

:vsp filename

分屏编辑中光标的移动

vi中的光标键是h,j,k,l,要在各个屏之间切换,只需要先按一下ctrl+w

1.  把光标移动到上边的屏

ctrl+w k

2.  把光标移动到下边的屏

ctrl+w j

3.  把光标移动到右边的屏

ctrl+w l

4.  把光标移动到左边的屏

ctrl+w h

5.  把光标移动到下一个的屏

ctrl+w w

二、Linux下C语言编译

2.1 编译 gcc + 源文件

-o 可执行文件名字

2.2 编译多个

2.3 分文件夹

-I 添加头文件路径

2.4 gcc

-v / –v / –version 查看gcc版本号

-I 指定头文件目录,注意-I和之间没有空格

-c 只编译,生成.o文件,不进行链接

-g 包含调试信息 -On n=0∼3 编译优化,n越大优化得越多

quit 退出

list n 从n行开始,查看10行代码

start 开始调试,默认停在main函数第一行

单步调试:

逐过程 next

逐语句 step

Enter 继续执行上次的命令

查看变量的值 print 变量名

持续监控 dispay 变量名

结束监控 undisplay 监控编号

断点

breakpoint 行号

breakpoint 函数名(到函数的第一行代码)

查看断点信息

info breakpoint

run 开始调试,从第一个断点处开始,没有断点运行到结束

continue 继续运行到下一个断点处

禁用断点 disable 断点编号

enable 重新启用断点

-Wall 提示更多警告信息

-D 编译时定义宏,注意-D和之间没有空格

常用于debug时打印日志

-E 生成预处理文件

-M 生成.c文件与头文件依赖关系以用于Makefile,包括系统库的头文件

-MM 生成.c文件与头文件依赖关系以用于Makefile,不包括系统库的头文件

2.5 Makefile 将编译命令写成脚本

优点:

1、便捷的管理代码,不用重复写复杂的命令

2、重用性极强,不同的项目可以反复使用

3、大大节省编译时间

编译过程:预处理、编译、汇编、链接

编译时间:预处理、编译、汇编,生成目标文件(.o)

链接时间:生成可执行文件

采用空间换时间的方式,第一次编译的时候保存生成的目标文件。下一次编译的时候,只编译修改的源文件,不修改的源文件直接使用保存的.o文件进行链接。

识别修改的源文件:比较源文件的修改时间和.o文件的生成时间。

2.5.1 节省编译时间

目标:依赖

        命令

一个makefile里只能由一个最终目标,其他目标必须与最终目标有依赖性 

当修改某个文件

2.5.2 复用性

2.5.2.1 变量

自定义变量:无类型,都是字符串。文件名由字母数字下划线构成,数字不能开头,一般都大写。注意使用时需要加“$”用来声明这是变量。

 

功能目标没有依赖项,只有命令。

内置变量:makefile自带的具有特殊意义的变量

$@ 代表目标名

$^ 代表所有依赖项

$< 第一个依赖项

2.5.2.2 函数

需要在$()中写函数

获取当前文件夹内所有.c文件

 SRCFILE=$(wildcard *.c)
由.c文件动态获取.o文件

DFILE=$(patsubst %.c,%.o,$(SRCFILE))
字符串处理函数patsubst,将.c变成.o

内建语法

%.o:%.c

三、文件IO

3.1 C标准函数与系统函数的区别

文件操作函数:fopen fclose fread fwrite

FILE*结构体指针:

1. f_pos 光标位置,默认在文件头

2. buffer 缓冲区,大小默认为8192Byte。对磁盘进行IO操作时消耗资源大,先将用户数据存到buffer中,当缓冲区满自动写入文件,或者手动flash写入文件。

3. 文件描述符

3.2 PCB

用户空间3G,内核空间1G

每个进程都有一个PCB用来管理进程,files_struct用来管理进程打开的所有文件

int数组用来存文件描述符,分配原则:当前可用的最小的文件描述符

进程会默认打开三个文件描述符:标准输入(0)、标准输出(1)、标准出错(2)

3.3 open/close

3.3.1 文件描述符

一个进程默认打开3个文件描述符

STDIN_FILENO     0

STDOUT_FILENO   1

STDERR_FILENO   2

新打开文件返回文件描述符表中未使用的最小文件描述符。

open函数可以打开或创建一个文件。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int open(const char *pathname, int flags); 
int open(const char *pathname, int flags, mode_t mode); 

返回值:成功返回新分配的文件描述符,出错返回-1并设置errno 

pathname参数是要打开或创建的文件名,和fopen一样,pathname既可以是相对路径也可以是绝对路径。flags参数有一系列常数值可供选择,可以同时选择多个常数用按位或运算符连接起来,所以这些常数的宏定义都以O_开头,表示or。

必选项:以下三个常数中必须指定一个,且仅允许指定一个。

* O_RDONLY 只读打开

* O_WRONLY 只写打开

* O_RDWR 可读可写打开

以下可选项可以同时指定0个或多个,和必选项按位或起来作为flags参数。可选项有很多, 这里只介绍一部分,其它选项可参考open(2)的Man Page:

* O_APPEND 表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。

* O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode,表示该文件的访问权限。

* O_EXCL 如果同时指定了O_CREAT,并且文件已存在,则出错返回。

* O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断(Trun-cate)为0字节。

* O_NONBLOCK 对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O(Nonblock I/ O),非阻塞I/O在下一节详细讲解。

close函数关闭一个已打开的文件:

#include <unistd.h>

int close(int fd); 

 返回值:成功返回0,出错返回-1并设置errno

参数fd是要关闭的文件描述符。

例:文件描述符

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
	//open aa
	int fd_aa = open("aa",O_RDONLY);
	if(fd_aa==-1){
		printf("open aa fail\n");
		return 1;
	}
	printf("fd_aa = %d\n", fd_aa);
	//open bb
	int fd_bb = open("bb",O_RDONLY);
	if(fd_bb==-1){
		printf("open bb fail\n");
		return 1;
	}
	printf("fd_bb = %d\n", fd_bb);
	//close aa
	close(fd_aa);
	//open cc
	int fd_cc = open("cc",O_RDONLY);
	if(fd_cc==-1){
		printf("open cc fail\n");
		return 1;
	}
	printf("fd_cc = %d\n", fd_cc);
	//close bb cc
	close(fd_bb);
	close(fd_cc);
	return 0;
}
	

 

 例:touch创建文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char* argv[]){
	//判断用户输入的参数是否合法
	if(argc<=1){
		printf("请输入要创建的文件名\n");
		return 1;
	}
	//循环创建文件
	int fd=0;
	for(int i=1;i<argc;i++){
		//创建文件
		fd=open(argv[i],O_CREAT,0666);
		//判断是否创建文件成功
		if(fd==-1){
			printf("open file [%s] fail\n",argv[i]);
			continue;
		}
		//如果创建成功,关闭文件
		close(fd);
	}
	return 0;
}

3.3.2 最大打开文件个数

查看当前系统允许打开最大文件个数(整个操作系统允许的)

cat /proc/sys/fs/file-max

当前默认设置最大打开文件个数1024(当前进程允许的)

ulimit -a

修改默认设置最大打开文件个数为4096

ulimit -n  4096

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
	int fd=0;
	char name[100]="";
	int i=1;
	while(1){
		sprintf(name,"file%d",i);
		fd=open(name,O_CREAT,0666);
		if(fd==-1){
			perror("open fail");
			printf("i=%d\n",i);
			return 1;
		}
		i++;
	}
	return 0;
}

当前进程允许最大打开文件是1024个,可是只创建了1021个文件?

因为进程默认打开三个文件: 标准输入、标准输出、标准错误

3.4 read/write

read函数从打开的设备或文件中读取数据。

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count); 

返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0

参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移。

write函数向打开的设备或文件中写数据。

#include <unistd.h>                   

ssize_t write(int  fd, const  void  *buf, size_t  count); 

返回值:成功返回写入的字节数,出错返回-1并设置errno

写常规文件时,write的返回值通常等于请求写的字节数count,而向终端设备或网络写 则不一定。

例:cp拷贝文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char* argv[]){
	//判断参数个数是否合法
	if(argc!=3){
		printf("参数不合法\n");
		return 1;
	}
	//打开源文件,以只读方式打开
	int fd_src=open(argv[1],O_RDONLY);
	if(fd_src==-1){
		printf("open file [%s] fail\n",argv[1]);
		return 1;
	}
	//打开目标文件,以只写|不存在就创建|长度截断为0方式打开
	int fd_dst=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
	if(fd_dst==-1){
		printf("open file [%s] fail\n",argv[2]);
		close(fd_src);
		return 1;
	}
	//循环读取源文件
	int len=0;
	char buf[100]="";
	while(len=read(fd_src,buf,sizeof(buf))){
		//判断读取是否成功
		if(len>0){
			//成功,把读取的内容写入目标文件'
			write(fd_dst,buf,len);
		}
	}
	//关闭源文件和目标文件
	close(fd_src);
	close(fd_dst);
	return 0;
}

因为掩码默认为0002,可以通过掩码清零来设定想要的权限

    umask(0000);
	int fd_dst=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);

打印错误码

#include <errno.h>
#include <string.h>

	//打开源文件,以只读方式打开
	int fd_src=open(argv[1],O_RDONLY);
	if(fd_src==-1){
		//printf("open file [%s] fail\n",argv[1]);
		printf("errno=%d\n",errno);
		perror("open fail");
		printf("open fail:%s\n",strerror(errno));
		return 1;
	}

3.5 阻塞和非阻塞

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

//阻塞
#include <unistd.h>

int main(){
	char buf[10]="";
	int len=read(STDIN_FILENO,buf,sizeof(buf));
	write(STDOUT_FILENO,buf,len);
	return 0;
}
//非阻塞
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc,char*argv[]){
	//标准输入文件是已经默认阻塞打开的文件,改变文件为非阻塞(1、关闭文件,再以非阻塞方式打开;2、fcntl函数改变为非阻塞)
	//获取标准输入文件的原属性
	int flags=fcntl(STDIN_FILENO,F_GETFL);
	//在原属性的基础上追加非阻塞
	flags=flags|O_NONBLOCK;
	//把新的属性设置到标准输入文件上
	fcntl(STDIN_FILENO,F_SETFL,flags);
	//循环读
	int len=0;
	char buf[10]="";
	while(1){
		//从标准输入文件读取数据
		len=read(STDIN_FILENO,buf,sizeof(buf));
		//判断返回值
		if (len==-1){ 	//没有读入或者读入有误
			//判断errno
			if (EAGAIN == errno){
				//暂时没有数据,休眠1秒,继续
				sleep(1);
				continue;
			}
			else {
				//读取失败,打印错误日志,结束
				perror("read fail");
				return 1;
			}
		}
		else{
			//读取成功,写入标准输出文件,结束
			write(STDOUT_FILENO,buf,len);
			break;
		}
	}
	return 0;
}

STDIN_FILENO:接收键盘的输入

STDOUT_FILENO:向屏幕输出

3.6 fcntl 改变文件属性

可以用 fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这 些标志称为File Status Flag),而不必重新open文件。

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd); 
int fcntl(int fd, int cmd, long arg); 
int fcntl(int fd, int cmd, struct flock *lock);

3.7 lseek 移动光标位置

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

若lseek成功执行,则返回新的偏移量,因此可用以下方法确定一个打开文件的当前偏移量:

off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);

SEEK_SET:基准位置为文件开头

SEEK_CUR:基准位置为文件当前位置

SEEK_END:基准位置为文件末尾

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(){
	// 打开aa文件
	int fd=open("aa",O_RDONLY);
	if(fd==-1){
		perror("open fail");
		return 1;
	}
	//把光标移动到文件尾
	int size=lseek(fd,0,SEEK_END);
	//打印文件大小
	printf("size=%d\n",size);
	//把光标移动到文件开头后10个字节
	lseek(fd,10,SEEK_SET);
	//从光标位置开始读取100个字节的数据
	char buf[100]="";
	int len=read(fd,buf,sizeof(buf));
	//把读取的数据写入标准输出文件
	write(STDOUT_FILENO,buf,len);
	//关闭文件
	close(fd);
	return 0;
}

四、文件系统

4.1 ext2文件系统

磁盘一个扇区是512字节

10分钟学懂磁盘的结构(盘片、磁道、扇区、柱面) - 知乎 (zhihu.com)

Boot Block(启动块):不论什么类型的文件系统都要有1kb的空间保留

对于每个block group内容都是一样的

data block(数据块):由多个块组成,用来存储文件内容。块大小默认为4kb,可以通过修改块大小让块小一点,来解决空间浪费问题,同时会导致读写速度慢。

inode table:存储多个inode节点,包括文件属性和数据块指针。通过数据块指针找到文件内容

inode bitmap(位图):每一位对应一个inode节点,若为1,表示该inode节点已经被使用了

block bitmap(块位图):记录整个block group,用来找空闲的data block

GDT(块组描述符表):记录不同块的起始位置

超级块:mount挂载到一个文件夹内

4.1.1 存文件

从boot block开始,查看在哪个磁盘。

查GDT是否有空闲inode,查各个块的起始位置

从inode bitmap中找空闲节点,写入文件属性,并置1

从block bitmap申请data block写入文件内容,并置1

将占用的块写入数据块指针

4.1.2 读文件

boot block 查看磁盘分区

GDT找inode table的起始位置

从inode table根据inode节点编号,通过偏移量(128*(n-1))找到想要的

读取文件属性,看是否有权限,通过数据块指针读取data block中的内容

4.1.3 删除文件

boot block磁盘分区

GDT找block bitmap和inode bitmap起始位置

block bitmap和inode bitmap对应标志位置为0

只有写入新的数据覆盖原来旧的数据才是彻底的删除

4.1.4 间接寻址--大文件存储

无论文件多大,一个文件都只有一个inode节点。

一个地址大小为4B,数据块指针大小为60B,block一共15个指针。

0~11这12个指针都正常指向一个data block,一个data block可以存储4KB内容。当所需存储的文件内容过大占用地址超过12个时,会使用间接寻址存储地址。

一级间接寻址指向一个data block,存取地址(1024*4KB=4M)

二级间接寻址指向一个data block,存储地址又指向一个存储地址的data block(1024*1024*4K=4G)

同理,三级间接寻址(1024*1024*1024*4K=4T)

4.1.5  目录中记录项文件类型

4.2 stat 获取文件信息

atime(最近访问时间):

mtime(最近更改时间):指最近修改文件内容的时间

ctime(最近改动时间):指最近改动Inode的时间

源文件 touch aa

符号链接 ln -s aa aa1

 硬链接 ln aa aa2

 源文件和硬链接节点编号一样,符号链接节点编号与源文件无关(时间关系)

#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h>
 
int stat(const char *path, struct stat *buf); 
int fstat(int fd, struct stat *buf); 
int lstat(const char *path, struct stat *buf);
struct stat {
         dev_t   st_dev;                         /* ID of device containing file */ 
         ino_t   st_ino;                           /* inode number */ 
         mode_t   st_mode;                 /* protection */ 
         nlink_t   st_nlink;                    /* number of hard links */ 
         uid_t   st_uid;                           /* user ID of owner */ 
         gid_t   st_gid;                           /* group ID of owner */ 
         dev_t   st_rdev;                       /* device ID (if special file) */ 
         off_t   st_size;                          /* total size, in bytes */ 
         blksize_t   st_blksize;              /* blocksize for file system I/O */
         blkcnt_t   st_blocks;                /* number of 512B blocks allocated */ 
         time_t   st_atime;                            /* time of last access */ 
         time_t   st_mtime;                 /* time of last modification */ 
         time_t   st_ctime;                            /* time of last status change */
};

stat既有命令也有同名函数,用来获取文件Inode里主要信息,stat 跟踪符号链 接,lstat不跟踪符号链接

//stat.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(){
	struct stat st;
	if(stat("aa1",&st)){
		perror("stat fail");
		return 1;
	}
	printf("inode=%d,size=%d\n",st.st_ino,st.st_size);


	if(lstat("aa1",&st)){
		perror("stat fail");
		return 1;
	}
	printf("inode=%d,size=%d\n",st.st_ino,st.st_size);
	return 0;
}

4.3 access 查看文件是否存在

可以按实际用户ID和实际组ID测试

可以用来判断是否为跟踪符号链接

#include <unistd.h>

int access(const char *pathname, int mode);

参数mode

R_OK 是否有读权限

W_OK 是否有写权限

X_OK 是否有执行权限

F_OK 测试一个文件是否存在

4.4 chmod 修改权限

#include <sys/stat.h> 

int chmod(const char *path, mode_t mode); 
int fchmod(int fd, mode_t mode);

将aa2权限从0664改为0777 

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>

int convert(int o){
	int d=0;
	int count=1;
	while(o>0){
		d+=(o%10)*count;
		o=o/10;
		count*=8;
	}
	return d;
}

int main(int argc,char* argv[]){
	//1.判断参数个数
	if(argc!=3){
		printf("paramater error\n");
		return 1;
	}
	
	//2.判断目标文件是否存在
	if(access(argv[2],F_OK)==-1){
		//3.文件不存在,报错
		perror("access fail");
		return 1;
	}
	else{
		//4.文件存在,修改权限
		int mod =atoi(argv[1]);
		mod=convert(mod);
		if(chmod(argv[2],mod)){
			perror("chmode fail");
			return 1;
		}
	}

	return 0;
}

 

4.5 chown 修改文件所有人和组

#include <unistd.h>

int chown(const char *path, uid_t owner, gid_t group); 
int fchown(int fd, uid_t owner, gid_t group); 
int lchown(const char *path, uid_t owner, gid_t group);

4.6 utime 改变文件访问和修改时间

 

4.7 truncate  截断

#include <unistd.h> 
#include <sys/types.h>

int truncate(const char *path, off_t length); 
int ftruncate(int fd, off_t length);

可以截断变短,也可以截断边长

4.8 rename 重命名

4.9 chdir 改变当前进程的工作目录

#include <unistd.h>

int chdir(const char *path); 
int fchdir(int fd);

4.10 getcwd 获取当前进程的工作目录

#include <unistd.h> 

char *getcwd(char *buf, size_t size);

4.11 pathconf 获取路径相关配置

4.12 link 创建硬链接

创建一个硬链接

当rm删除文件时,只是删除了目录下的记录项和把inode硬链接计数减1,当硬链接计数减为0时,才会真正的删除文件。

#include <unistd.h>

int link(const char *oldpath, const char *newpath);

* 硬链接通常要求位于同一文件系统中,POSIX允许夸文件系统

* 符号链接没有文件系统限制

* 通常不允许创建目录的硬链接,某些unix系统下超级用户可以创建目录的硬链

* 创建目录项以及增加硬链接计数应当是一个原子操作

4.12.1 symlink 建立文件符号连接

int symlink(const char *oldpath, const char *newpath)

详解C语言中symlink()函数和readlink()函数的使用-CSDN博客

4.12.2 readlink 取得符号连接所指的文件

读符号链接所指向的文件名字,不读文件内容

ssize_t readlink(const char *path, char *buf, size_t bufsiz)

4.12.3  unlink 删除连接

int unlink(const char *pathname)

1. 如果是符号链接,删除符号链接

2. 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode

3. 如果文件硬链接数为0,但有进程已打开该文件,并持有文件描述符,则等该进程关闭该文件时,kernel才真正 去删除该文件

4. 利用该特性创建临时文件,先open或creat创建一个文件,马上unlink此文件

当程序异常关闭时,这种方式仍能删除临时文件,在系统重启时会删掉

4.13 目录操作

4.13.1 mkdir 创建目录

#include <sys/stat.h> 
#include <sys/types.h>

int mkdir(const char *pathname, mode_t mode);

4.13.2 rmdir 删除目录

#include <unistd.h> 

int rmdir(const char *pathname);

4.13.3 opendir/fdopenr 打开目录

#include <sys/types.h> 
#include <dirent.h>

DIR *opendir(const char *name); 
DIR *fdopendir(int fd);

指针指向记录项第一条

4.13.4 readdir 读取记录项

#include <dirent.h>

struct dirent *readdir(DIR *dirp);
struct dirent { 
ino_t d_ino;                            /* inode number */ 
off_t d_off;                              /* offset to the next dirent */ 
unsigned short d_reclen;     /* length of this record */ 
unsigned char d_type;          /* type of file; not supported by all file system types */ 
char d_name[256];                /* filename */ 
};

readdir每次返回一条记录项,DIR*指针指向下一条记录项

4.13.5 rewinddir 指针回到起始位

#include <sys/types.h>
#include <dirent.h>

void rewinddir(DIR *dirp);

4.13.6 telldir/seekdir移动指针

#include <dirent.h> 

long telldir(DIR *dirp);

#include <dirent.h> 

void seekdir(DIR *dirp, long offset);

4.13.7 closedir 关闭目录

#include <sys/types.h> 
#include <dirent.h>

int closedir(DIR *dirp

4.13.8 递归遍历目录,实现ls -R功能

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

//进入文件夹,读取文件夹记录项
void walkDir(char* path){
	//打开文件夹
	DIR* dir=opendir(path);
	if(dir==NULL){
		perror("opendir fail");
		return ;
	}
	//循环读取记录项
	char name[1024]="";
	struct dirent* dp=NULL;
	while(dp=readdir(dir)){
		if(strcmp(dp->d_name,".")==0 || strcmp(dp->d_name,"..")==0)
			continue;
		if(sizeof(name)<strlen(path)+strlen(dp->d_name)+2){
			printf("path [%s/%s] is too long\n",path,dp->d_name);
			continue;
		}
		//拼接路径和文件名
		sprintf(name,"%s/%s",path,dp->d_name);
		//打印拼接后的文件
		printFile(name);
	}
	//关闭文件夹
	closedir(dir);
}

//打印文件名和文件大小
void printFile(char* path){
	//判断文件是否存在,并且获取文件属性
	struct stat st;
	if(stat(path,&st)==-1){
		perror("stat fail");
		return ;
	}
	//判断文件是否是文件夹
	if((st.st_mode & S_IFMT)==S_IFDIR){
		//如果是文件夹,就进入文件夹
		walkDir(path);
	}
	else {
		//如果是文件,就打印文件名:文件大小
		printf("%s:%d\n",path,st.st_size);
	}
}

int main(int argc,char* argv[]){
	if(argc==1){
		//读取当前目前
		printFile(".");
	}
	else if(argc>1){
		//读取多个目录
		for(int i=1;i<argc;i++){
			printFile(argv[i]);
		}
	}
	else{
		//打印错误日志
		printf("paramater error\n");
		return 1;
	}
	return 0;
}

4.14 VFS虚拟文件系统

解决不同系统之间能互通

对于不同文件描述符指向同一个文件:用的是同一个读写指针,所以两个操作是接着的

对于两个进程操作同一个文件:file结构体是独立的,后的进程会覆盖前一个进程的操作

 dup/dup2 复制一个现存的文件描述符,使两个文件描述符指向同一个file结构体

如果两个文件描述符指向同一个file结构体,File Status Flag和读写位置只保存一份在file结构体中,并且file结构体的引用计数是2。如果两次open同一文件得到两个文件描述符,则每个描述符对应一个不同的file结构体,可以有不同的File Status Flag和读写位置。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
	//保存一份标准输出文件的文件描述符
	int fd_save=dup(STDOUT_FILENO);

	//打开xx文件
	int fd_xx=open("xx",O_RDWR);
	if(fd_xx==-1){
		perror("open fail");
		return 1;
	}

	//让STOUT-FLIENO指向xx文件
	dup2(fd_xx,STDOUT_FILENO);

	//调用printf函数
	printf("this is a test\n");

	//关闭xx和标准输出文件(原本会自己关闭的)
	close(fd_xx);
	close(fd_save);

	return 0;
}

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值