初学Linux,同样也是第一次写博客。内容部分摘自B站大学、相关书籍和他人博客,并添加了自己的一些总结和理解。希望文中错误的地方能多多指点,我会虚心学习,谢谢。
与大家熟知的 Windows 操作系统软件一样,Linux 也是一个操作系统软件。与 Windows 不同之处在于,Linux 是一套开放源代码程序的、可以自由传播的类 Unix 操作系统软件。
从技术上来说,李纳斯•托瓦兹开发的 Linux 只是一个内核。内核指的是一个提供设备驱动、文件系统、进程管理、网络通信等功能的系统软件,内核并不是一套完整的操作系统,它只是操作系统的核心。一些组织或厂商将 Linux 内核与各种软件和文档包装起来,并提供系统安装界面和系统配置、设定与管理工具,就构成了 Linux 的发行版本。
在 Linux 内核的发展过程中,各种 Linux 发行版本起了巨大的作用,正是它们推动了 Linux 的应用,从而让更多的人开始关注 Linux。因此,把 Red Hat、Ubuntu、SUSE等直接说成 Linux 其实是不确切的,它们是 Linux 的发行版本,更确切地说,应该叫作“以Linux为核心的操作系统软件包”。
Linux 的各个发行版本使用的是同一个 Linux 内核,因此在内核层不存在什么兼容性问题,每个版本有不一样的感觉,只是在发行版本的最外层(由发行商整合开发的应用)才有所体现。
Linux 的发行版本可以大体分为两类:
商业公司维护的发行版本,以 Red Hat、Centos为代表;
社区组织维护的发行版本,以 Debian、Ubuntu为代表。
据了解,Ubuntu适合初学者学习开发,而Red Hat因为其稳定性诸多大企业选择用于开发。但是Red Hat是收费的,所以可以学习其对应的开源发行版本Centos。
Centos的学习可以参照《鸟哥私房菜》学习,而我使用的是Centos(学习的却是Ubuntu,但本质没有差别,只是平台不一样,内核一样),可在此处下载.ios镜像文件:
https://mirrors.tuna.tsinghua.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-Everything-2009.iso
磁盘分区部分我是摘抄了《鸟哥私房菜》中部分我认为重要的内容(我的笔记本部分,很残缺),而且我也只理解了一些皮毛,因为重点还是掌握怎么使用Linux,故对磁盘分区具体内容感兴趣的朋友,请认真翻阅书籍或者找其他大牛的博客,当然我也会继续学习。
在Linux系统中,每个设备都被当成一个文件来对待。在Linux中,几乎所有的硬件设备文件都在/dev这个目录内。
正常的物理机器使用/dev/sd[a~ p]的磁盘文件名,虚拟机环境中,为了加速,可能使用/dev/vd[a~p]这种文件名。第一扇区很重要,存储着重要信息。
Linux系统通过Linux内核检测到磁盘的顺序来命名。
• 所谓的分区知识针对那个64字节的分区表进行设置而已。
• 硬盘默认的分区表仅能写入四组分区信息
• 这四组划分信息我们称为主要或者扩展分区
• 分区最小单位通常为柱面
• 当系统要写入磁盘时,一定会参考磁盘分区表,才能针对某个分区进行数据处理。
/dev/sda[1-4]是保留给主要分区或扩展分区的,所以逻辑分区的设备名称号码由5号开始,这在MBR方式的分区表中是个很重要的特性!!!
MBR主要分区、扩展分区或逻辑分区的特性主要定义:
• 主要分区和扩展分区最多可以有4个(硬盘的限制);
• 扩展分区最多只能有1个(操作系统的限制);
• 逻辑分区由扩展分区持续划分出来的分区;
• 能被格式化后作为数据存取的分区是主要分区与逻辑分区,扩展分区无法格式化;
• 逻辑分区的数量依操作系统而不同,在Linux系统中SATA硬盘已经可以突破63个以上的分区限制。
• MBR仅446字节,而且被破坏后无备份。
P(主分区)+ E(扩展分区)最多只能有四个,其中E最多只能有一个。
例:若要建立六个分区,则可以是P+P+P+E(L+L+L)/P+E(L+L+L+L+L);
Sdb[1-4]是给主要分区和扩展分区的,也就是P和E。例如:3P+E中P为sdb[1-3],E为sdb4,E中的L(logic)为sdb[5-n];又P+E中,P为sdb1,E为sdb2,E中的L是从sdb5开始的,sdb[3-4]被保留下来没有用到。
GPT磁盘分区表:
GPT将磁盘所有区块以此LBA(默认512字节,可以更大)来规划,使用了34个LBA区块来记录分区信息,并且最后34个用来备份。
LBA0(MBR兼容区块):存储了第一阶段的启动引导程序(与MBR一致).LBA1(GPT表头记录),记录了分区表本身的位置和大小及备份GPT分区位置,同时放置了分区表的校验码。GPT2-33,(实际记录分区信息处),从LBA2开始,每个都可以记录四组分区记录。
GPT磁盘分区优点为有备份、分区多,存储量大,但是并不是每一个硬件都支持GPT分区,需借助BIOS和UEFI启动检测程序来检测是否能够读写GPT
BIOS搭配MBR/GPT的启动流程
BIOS是一个写入到主板上的一个固件(固件就是写入到硬件上的一个软件程序)BIOS启动时,计算机系统主动启动第一个程序。
1、BIOS:启动主动执行的固件,会认识第一个可启动的设备;
2、MBR:第一个可启动设备的第一个扇区内的主引导记录块,内含启动引导代码;
3、启动引导程序:一个可读取内核文件来执行的软件;
4、内核文件:开始启动操作系统;
BIOS能从LBA0的MBR兼容区块读取第一阶段的启动引导程序代码,如果启动引导程序支持GPT,则BIOS可以正确读取操作系统内核,反之不可以。
BOOT loader是操作系统安装在MBR上的一个软件,任务如下:
• 提供选项:用户可以选择不同的启动选项。
• 加载内核文件:直接指向可使用的程序区段来启动操作系统。
• 转交给其他启动引导程序
总结:
• 每个分区都有自己的启动扇区(boot sector即LBA0和MBR第一扇区);
• 实际课启动的内核文件是放置到各个分区中的;
• 启动引导程序指挥认识自己的系统分区内的可启动的内核文件,以及其他启动引导程序而已;
• 启动引导程序可直接指向或者渐渐将管理权转交给另一个管理程序。
1. 命令解析器:
shell ----- unix操作系统
bash ----- Linux操作系统
本质:根据命令的名字,调用对应的可执行程序
2. Linux快捷键
A. 命令和路径补充:
B. 主键盘快捷键:
a. 历史命令切换:
历史命令:history
向上遍历:Ctrl + P
向下遍历:Ctrl + N
b. 光标移动:
向左:Ctrl + B
向右:Ctrl + F
移动到头部:Ctrl + A
移动到尾部:Ctrl + E
c. 删除字符:
删除光标后边字符:Ctrl + D
删除光标前边字符:Ctrl + S/Backspace
删除光标前的所有内容:Ctrl + U
清屏:Ctrl + L
3. Linux系统目录结构:
A. 根目录 /:/下挂载下面所有目录
B. /bin:存放经常使用的命令
C. /boot:这里存放的是启动Linux是使用的可惜文件,包括一些链接文件以及镜像文件。
D. /dev:device缩写,存放外部设备,在linux在访问设备的方式和访问文件的方式相同。
E. /etc:存放所有系统管理所需要的配置文件和子目录。
F. /home:各个普通用户的主目录。
G. /lib:存放着系统最基本的动态连接共享库,类似于Windows中的DLL文件,几乎所有文件都要运用到共享库。
H. /lost +found:一般情况是空的,当系统非法关机后,存放一些文件。
I. /media:linux系统会自动识别一些设备,例如U盘,光驱等,当识别后,linux会将这些识别后的设备挂载在该文件下。
J. /mnt:系统提供该目录是为了让用户临时挂载别的文件系统,可将光驱挂载在/mnt/上,然后进入该文件就可以看见光驱中的内容。
K. /opt:主机额外安装软件所摆放的目录。
L. /proc:虚拟目录,系统内存的映射,我们可以直接访问这个目录来获取系统信息,,这个目录的内容不在硬盘上而在内存里,我们可以直接修改里面的某些文件,比如通过下面的命令来屏蔽主机的ping命令,是别人无法ping你的机器。
M. /root:超级用户主目录。
N. /sbin:超级用户使用的系统管理程序。
O. /selinux:redhat/centos特有,类似于防火墙。
P. /srv:存放一些服务启动之后需要提供的数据。
Q. /sys:2.6内核系统文件。
R. /tmp:临时文件。
S. /usr:非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于Windows下的program files。
T. /usr/bin:系统用户使用的应用程序。
U. /usr/sbin:超级用户使用的比较高级的管理程序和系统守护程序。
V. /usr/src:内核源代码的放置目录.
W. /var:这个目录中存放着不断扩充的东西,我们习惯将那些经常被修改的目录存放在这个目录下,包括各种日志文件。
4. 用户目录:
A. 绝对路径:从根目录开始写 /home/itcast/aa
B. 相对路径:相对于当前的工作目录而言
a. . 当前目录
b. .. 当前的上一级目录
c. - 在临近的两个目录直接切换 cd -
C. [czy@localhost ~]$
a. czy:当前登录的用户
b. localhost:主机名
c. ~:家目录,可代替czy@localhost,宿主目录
d. $:当前目录为普通用户
e. #:超级用户root,可用sudo su切换为超级用户,可用exit退回
5. 文件和目录操作:
A. 查看我的目录
a. tree -- 以树状结构成列出所有文件目录
b. ls 查看当前目录所有文件
ls -a查看目录下所有文件,包括隐藏,-la就是看隐藏信息和文件详细信息
ls -l列出文件详细信息
4096代表目录大小
B. 文件之间跳转:
a. 前往某个目录:cd 目录路径
b. 回家目录:cd ~或cd或cd/home/aaa
c. 查看当前目录:pwd或者看~和$之间
C. 创建目录:
a. 创建一个目录:mkdir 【目录名】
b. 创建目录和子目录:mkdir dir/dir1/dir2 -p(三层及以上要加-p)
D. 删除目录:
a. 删除空目录:rmdir dir
b. 删除非空目录:rm -r aa(直接删除,不会进入回收站)
c. 删除时询问是否删除:rm -ri bb
E. 创建文件:touch dir (创建文件或者当文件已存在时修改文件创建时间)
F. 删除所有东西:rm
G. 复制:cp mytest newtest
cp mydir newdir -r
H. 查看文件的具体信息:
a. 将文件内容展示在屏幕上:cat mytest(不能完全显示内容过长的文件)
b. 如果文件过长:more mytest(回车一行行显示,空格一页一页显示,但是只能往后看)
c. 如果想往前翻页:less mytest(ctrl+B向前翻页,ctrl+F向后翻页)
d. 用vi写文件后,可以vi查看文件
e. head默认显示前十行内容,head -5默认显示前五行
f. tail默认显示后十行内容,同理tail -5
I. 文件改名:mv mytest newtest (mv既可用于改名,也可用于移动,如果第二个参数是文件,那么就是改名,如果是目录就是移动至该目录。)
J. 软链接:创建快捷方式,ln -s 【带绝对路径的文件 】 【软链接名字】
K. 硬链接:相当于超链接,不额外占用磁盘资源,映射到inode结点。用ln创建。
6. 文件和目录属性:
A. wc:获取文本文件信息(行数,字数,字节数,文件名字),wc【文件名】
参数: -c:只显示字节数
-l:只显示行数
-w:只显示字数
B. od:查看二进制文件(可执行文件):-t 指定数据的显示格式
参数: c:ASCⅡ字符
d:有符号十进制数
f:浮点型
o:八进制数
u:无符号十进制数
x:十六进制数
C. du:查看当前目录大小,du -h
D. df:查看磁盘使用情况,df -h
E. which命令:命令解析器,查找命令在哪个目录下(只可查外部命令),例如which ls,即可知道ls这个命令在哪个目录下
7. 文件权限、用户、用户组:
A. 查看当前登录用户:
B. 修改文件权限:
a. 文字设定法:chmod [who] [+|-|=] [mode]
1) chmod:change mode
2) who:文件所有者(u)、文件所属组(g)、其他人(o)、所有人(a,如果什么都不写,默认为a)
3) +:增加权限、-:减少权限、=:覆盖原来权限
4) mode:读(r)、写(w)、执行(x)
b. 数字设定法:chmod 777 【文件名】
1) -:没有权限
2) r:读权限(4)
3) w:写权限(2)
4) x:执行权限(1)
5) 7 ---rwx--- 文件所有者
6) 6 ---rw--- 文件所属组
7) 5 ---rx--- 其他人
8) 减权限:chmod -001 【文件名】
9) 覆盖权限:chmod 765 【文件名】
C. 改变文件或目录的所有者或所属者:Linux中,文件可以属于某一所有者并属于某一所属组,但所有者可以不在该所属组中。
用chown 【所有者】【文件】,文件所有者改变,但所属组未改变。chown 【所有者:所属组】【文件】
则可以改变所有者和所属组。
D. 改变文件或目录所属的组:chgrp 【所属组】【文件】则可以改变所属组。
8. 查找和检索:
A. 按文件属性查找:
a. 文件名:find + 查找的目录 + -name +“文件的名字”,若不知道全面,可用通配符:*代表多个字符、?代表1个字符。
b. 文件大小:find + 查找的目录 + -size + 文件的大小(大于10k则为+10k,小于10k则为-10k)
c. 文件类型:find + 查找的目录 + -type + d(目录)/f(普通文件)/b(块设备)/c(字符设备)/s(套接字)/p(管道)/l(链接符号)
B. 按文件内容查找:grep -r “查找内容”+查找路径,-r的目的就是递归查找,与上面同理。
9. 软件的安装和卸载:
A. 在线安装:(CentOs下为yum下载rpm包,Ubuntu下为apt-get下载deb包,Ubuntu下的aptitude相比于apt-get处理依赖最佳)
a. 安装:sudo yum -y install xxx(在线下载安装)
b. 卸载:sudo yum -y remove xxx
c. 更新:sudo yum -y update (升级所有包同时也升级软件和系统内核。更新软件列表。);sudo yum -y upgrade(只升级所有包,不升级软件和系统内核。)
d. 清空:sudo yum -y clean(情况安装包缓存,实际清理的是:/var/cache/apt/archives目录下的.deb文件)
B. deb包/rpm包:
a. 安装:sudo dpkg -I 【deb包】/sudo rpm -ivh 【rpm包】
b. 卸载:sudo dpkg -r 【卸载包】/sudo rpm -e gcc
C. 源码安装:
1) 解压缩源代码包
2) 进入到安装目录:cd dir
3) 检测文件是否缺失,创建Makefile,检测编译环境:./configure
4) 编译源代码,生成库和可执行程序make
5) 把库和可执行程序,安装到系统目录下:sudo make install
6) 删除和卸载软件:sudo make distclean
7) 上述安装步骤并不绝对,应该查看附带的README文件,会有详细说明。
10. U盘的挂载和卸载:
A. 挂载(mount命令):
a. 系统默认挂载目录:/media
b. 手动挂载目录:/mnt(sudo mount 【设备名】【/mnt】),可用sudo fdisk -l查看设备信息
B. 卸载(umount命令):sudo umount 【卸载路径】(注意:你不能在要卸载的目录中)
1. 压缩包管理:
a. 初级版:
i. gzip --- .gz格式的压缩包,不会打包压缩也不会保留原文件。gzip 【文件名】
ii. bzip2 --- .bz2格式的压缩包,不能压缩目录,只能压缩文件,可以保留原文件(bzip2 -k 【文件名】即可保留)。
b. 进阶版:
i. tar:-------不使用z/j参数,该命令这只能对文件或目录打包
1) 参数:
a) c -- 创建
b) x -- 释放(与c互斥,不能一起用)
c) v -- 显示提示信息 -- 压缩解压缩时可以省略
d) f -- 指定压缩文件名字(压缩解压缩时都要使用)
e) z -- 使用gzip方式压缩文件 -- .gz
f) j -- 使用bzip2方式压缩文件 -- .bz2
2) 压缩:
a) tar zcvf 【生成的压缩包的名字(xxx.tar.gz)】【要压缩的文件或目录】
b) tar jcvf 【生成的压缩包的名字(xxx.tar.bz2)】【要压缩的文件或目录】
3) 解压缩:
a) tar zxvf 【压缩包的名字】(解压到当前目录)/tar zxvf 【压缩包的名字】-C 【路径】(压缩到指定路径)
b) tar jxvf 【压缩包的名字】(解压到当前目录)/tar jxvf 【压缩包的名字】-C 【路径】(压缩到指定路径)
ii. rar:-------必须手动安装该软件
1) 参数:
a) 压缩:a
b) 解压缩:x
2) 压缩:rar a 【生成的压缩文件的名字(无需指定后缀,会自动生成)】【要压缩的文件或目录】
3) 解压缩:rar x 【压缩文件名】/rar x 【压缩文件名】【指定路径】
iii. zip:
1) 参数:压缩目录需要加递归参数 -r/解压缩要加-d
2) 压缩:zip 【生成的压缩包名字】【压缩到的文件或目录】
3) 解压缩:unzip 【压缩包的名字】/unzip 【压缩包的名字】 -d 【解压缩到指定路径】
c. 总结:
i. 相同之处:
1) tar/rar/zip 参数 生成的压缩文件的名字 压缩的文件或目录 ------压缩时的语法
2) tar/rar/unzip 参数 压缩包的名字/tar/rar/unzip 参数 压缩包的名字 参数(rar没有该参数) 解压缩目录 ------解压缩语法
2. 进程管理:
a. 查看当前在线用户的情况:who
b. 查看整个系统内部运行的进程状况:ps
i. 参数:一般使用aux,三个可以一起连用
1) a(查看所有用户信息)
2) u(用户的信息)可与a连用:ps au
3) TTY(终端),TTY1-6为文字终端,TTY7为图形界面终端,可通过Ctrl+Alt+F1-7切换,:0代表TTY7
4) x(查看没有终端的应用程序)ps aux,终端的作用是与用户进行交互。
c. 管道:如果想对文件进行过滤就需要管道(-p)
i. 什么是管道:
ii. 指令:ps aux | grep xxx(其实就是查找进程里的xxx进程,ps aux是查看所有进程信息,grep是查找这些进程中有没有xxx进程)
d. 终止进程:
i. 查看信号编号:PID为启动的进程ID,可通过PID找到对应的程序。
ii. 杀死进程:kill -l(查看64个信号,每个信号干的事不一样,第九个SIGKILL是杀死信号),kill -9 【进程PID】
e. 查看当前进程的环境变量:env(所有环境变量设置)/env |grep PATH(该操作为将环境变量信息中的PATH单独过滤出来)
i. Linux下的环境变量的格式:key - value
key(图中PATH)=value(value值可以有多个,每个value值之间用:分开)
f. 任务管理器:top命令,只能看,无法操作,Ctrl+C关闭。
3. 网络管理:
a. 获取网络接口配置信息:ifconfig命令/Windows下为ipconfig
i. eth0:当前电脑网卡,如果还有网卡则为eth1以此类推
ii. 硬件地址(MAC地址)
iii. inet(IP地址)
iv. IO:回环地址
b. 测试与目标主机是否联通:
i. ping 【IP】-c 4(后面参数-c 4意思为回馈四条信息即可,也可以不加参数)
ii. ping 【域名】(例如ping www.baidu.com)
c. 查看服务器域名对应的IP地址:nslookup 【域名】
4. 用户管理:
a. 创建用户:
i. sudo adduser 【用户名(不能包含大写字母)】(是一个脚本)
ii. sudo useradd -s 【/bin/bash】 -g 【xxx】-d 【/home/xxx】 -m 【xxx】
1) -s指定新用户登录时shell类型
2) -g指定所属组,该组必须已经存在
3) -d用户家目录
4) -m用户家目录不存在时自动创建该目录
5) 如果组不存在,则sudo groupadd 【组名】
6) 退出:exit
b. 设置用户组:sudo groupadd 【组名】
c. 删除用户:
i. sudo deluser 【用户名】
ii. sudo userdel -r 【用户名】(-r的作用是把用户的主目录一起删除)
d. 切换用户:su 【用户】
e. 设置密码:
i. 修改密码:sudo passwd 【用户】
ii. 修改当前用户密码:passwd(直接passwd)
iii. 修改root密码:sudo passwd root
5. ftp服务器搭建 -- vsftpd软件。作用:文件的上传和下载
a. 服务器端:
i. 修改配置文件:(配置哪些用户可以登录,登陆后权限是只允许下载还是都可以之类的)
1) 进入/etc目录下
2) ls -l vsftpd.conf(修改此配置文件)
3) 用文本编辑器Vi或者gedit修改配置
4) anonymous_enable=YES(匿名用户使用权限)
5) local_enable=YES(本地用户使用权限)
6) write_enable=YES(实名登录用户拥有写权限(上传数据))
7) local——vmask=022(设置本地掩码为022)
8) anon_upload_enable=YES(匿名用户可以向服务器上传数据)
9) anon_mkdir_write_enable=YES(匿名用户可以在FTP服务器上创建目录)
ii. 重启服务:sudo service vsftpd restart(修改后不能立马生效需要重启服务,!!!但是此为通用命令,centos7已经不再使用)
iii. centos7下重启服务:systemctl restart vsftpd.service(重启)/systemctl status vsftped.service(查看状态)
以此,在Centos7 以后,凡是要service ... 动词的命令,如果执行不了,可以尝试下systemctl 动词
进一步说明可参考
b. 客户端:
i. 实名用户登录:
1) ftp+IP(server)
2) 输入用户名(server)
3) 输入密码
4) 上传put 【文件名】(必须是登录时所在目录里)
5) 下载get 【文件名】
6) 只允许上传、下载文件,如果想操作文件,只能打包tar/rar/zip
ii. 匿名用户登录:
1) ftp+serverIP
2) 用户名:anonymous
3) 密码:无(直接回车)
4) 不允许匿名用户在任意目录直接切换,只能在一个指定的目录范围内工作,需要在ftp服务器上创建一个匿名用户的目录----匿名用户的根目录
5) 步骤:指定匿名用户根目录:
a) 自己指定:mkdir 【目录名】
b) 默认目录:/srv/ftp/
c) anon_root=【指定目录路径】---->匿名用户根目录(在vi vsftpd.conf中随便找一行添加这一段话设置匿名用户根目录)
6) 创建目录:供匿名用户使用
a) mkdir anonDir
b) 修改目录所有者:sudo chown ftp anonDir/修改目录权限:chmod 777 anonDir(二选一)
7) 修改配置文件:sudo gedit(vi)/dev/vsftpd.conf
8) 重启服务器
iii. lftp客户端访问ftp服务器:
1) lftp是ftp的一个客户端工具,可以上传和下载目录。
2) 软件安装:sudo apt-get install lftp
3) 登录服务器:
a) 匿名:
i) lftp【服务器IP】(回车)
ii) login
b) 实名:
i) lftp【username@IP】(回车)
ii) 输入服务器密码
4) 操作:
a) put上传文件
b) mput上传多个文件
c) get下载文件
d) mget下载多个文件
e) mirror下载整个目录及其子目录
f) mirror -R上传整个目录及其子目录
6. nfs服务器搭建:(相当于Windows下的共享文件夹)net file system ---->网络文件系统,它允许网络中的计算机之间通过TCP/IP网络共享资源。
a. 服务器端:
i. 安装:sudo apt-get install nfs-kernel-server
ii. 创建共享目录:mkdir 【目录名】(找个合适的地方创建)
iii. 修改配置文件:
1) sudo vi /etc/exports
2) 把第11行删掉重新写,【共享目录路径】*(rw,sync)(路径后加个*,代表IP网段,后面这句括号要加,rw代表读写权限,ro代表只读,sync代表实时更新数据到磁盘上)
iv. 重启服务:sudo service nfs-kernel-server restart/systemctl restart service nfs-kernel-server
b. 客户端:
i. 挂载服务器共享目录:sudo mount 【serverIP】:【shareIP】【挂载路径(一般为/mnt)】
7. ssh服务器:远程操作服务器
a. 服务器端:
i. 安装ssh:
1) sudo apt-get install openssh-server
2) 查看SSH是否安装:sudo aptitude show openssh-server
b. 客户端:
i. 远程登录:ssh 【登录名@IP】(确认连接的时候一定要写yes/no)
ii. 退出登录:logout
8. scp命令:
a. scp==super copy
b. 使用该命令的前提条件:目录主机已经成功安装openssh-server
c. 使用格式:
i. scp -r 【目标用户名@目标主机IP地址】:【/目标文件的绝对路径】【/保存到本机的绝对(相对)路径】(后续输入yes,不能输入y)
ii. 拷贝目录需要加参数-r
9. 其他命令:
a. 翻页:
i. Shift+PageUp -->上翻页
ii. Shift+PageDown -->下翻页
b. 清屏:clear/Ctrl + l
c. 创建终端:Ctrl + Alt + T(Ubuntu)/Ctrl + Shift + T 添加新标签页
d. 看手册:man man ---->共九个章节(1可执行程序或shell命令,2系统调用(内核提供的函数),3库函数(程序库中的函数),5文件格式和规范(如/etc/passwd))
e. 设置查看别名:
i. 查看:alias
ii. 设置:alias pag=‘ps aux |grep’(需要长久有效需要设置配置文件:.bashrc)
f. echo命令:在显示器上显示数据
i. 普通数据:echo 字符串
ii. 显示环境变量:echo $PATH
iii. 显示上一次程序退出值:echo $?($:取值,?:最近一次程序退出时的返回值)
g. 关机:poweroff
h. 重启:reboot
i. shutdown:
1. vim编辑器的使用:vim是从vi发展过来的一款文本编辑器
a. 命令模式下的操作:打开文件(vi 【文件名】)之后,默认进入命令模式。
i. 光标的移动:vim中不能使用鼠标,如果想要移动光标,需要使用HJKL四个按键,H(前)、L(后)、J(下)、K(上)、0(行首)、$(行尾)、gg(文本首)、shift+G(文本尾),N+G(跳到N行)
ii. 删除(vi中删除的本质是剪切)操作:x(删除光标上的字符(也就是光标后的))、shift+x(X删除光标前的字符)、dw(删除整个单词(光标后面的字符一直到空格),如果要删整个单词,要把光标放在单词首字母)、d0(从光标往前删到行首)、d$(删除光标以后的部分)、D(光标后边所有部分)、dd(删除整行)、4dd(删除从光标开始数四行)
iii. 撤销操作:u(撤销)、Ctrl+R(反撤销)
iv. 复制操作:p(粘贴,粘贴至光标所在行的下一行)、shift+p(粘贴至光标所在行)、yy(复制光标所在行)、Nyy(复制N行)
v. 可视模式:v(切换为可视模式,然后用hjkl控制光标移动选择所需内容进行复制(y)或者删除(d))
vi. 查找操作:
1) /【要查找的单词】:在左下角输入【/+查找内容】(输入n可在查找高亮目标进行切换)(从光标位置向下查找,到尾部后,返回到头部)
2) ?【要查找的单词】:也是查找(在光标位置向上查找,到头部后,返回尾部)
3) #:将光标放在要查找的单词上,按#键即可查找。
vii. r:替换当前字符(光标所指),只能替换单个字符
viii. 查看man文档,光标移动到要查的命令(单词),输入shift+k即可跳到man文档,3+shift+k(跳到man文档第三章)
ix. 缩减:向右>>,向左<<
b. 文本(编辑)模式下的操作:需要输入一些命令,切换到编辑模式。(命令(aios)———>编辑)
i. a:插入字符,输入到光标后面(A:输入字符到行尾)
ii. i:插入字符到光标前面(I:输入到当前行行首)
iii. o:在光标所在行下面一行创建新的一行,输入文本内容(O:光标上面一行创建新的行)
iv. s:删除光标后边的一个字符并开始在光标处输入(S:删除光标所在一整行并在该行输入)
c. 末行模式下的操作:在末行模式下可以输入一些命令。(编辑模式不能直接切换为末行模式!!!)(:!【命令】即可操作相关命令,如:(:!pwd))
i. 光标跳转:直接输入N,就能跳到N行
ii. 替换:
1) 先将光标移动到要替换的单词那一行,
2) 输入:切换至末行模式,输入s/【要替换的内容】/【替换成的内容】
3) 若要替换当前行所有的这个内容,需输入s/【要替换的内容】/【替换成的内容】/g
4) %s/【要替换的内容】/【替换成的内容】:替换文本中每一行第一个该内容
5) s/【要替换的内容】/【替换成的内容】/g:替换文本中所有该内容
6) 27,30s/【要替换的内容】/【替换成的内容】/g:替换27到30行
iii. 保存:w
iv. 退出:q(退出)、wq(保存并退出,输入x相当于wq)、q!(退出不保存)或者命令模式下按ZZ(两个大写)
d. 分屏操作:
i. 水平分屏:末行模式下输入sp,Ctrl+w+w可以上下切换屏幕操作,wqall全部退出,不加all就是操作一个
ii. 垂直分屏:末行模式下输入vsp,Ctrl+w+w可以在两个屏幕之间切换,vsp 【当前目录下文件名字】即可用一个分屏操作另一个文件。
e. vim打造IDE:
i. 系统级配置文件目录:/etc/vim/vimrc
ii. 用户级配置文件目录:~/.vim/vimrc
iii. 修改配置文件:vimrc或者.vimrc
2. gcc编译器:
a. gcc工作流程:
b. gcc hello.c -o myapp(默认执行中间的步骤,直接由C文件生成exe文件)
c. gcc的一些参数(esc)使用:
i. -E:预处理
ii. -S:C转汇编(例如gcc -S hello.i -o hello.s,其他同理)
iii. -c:汇编转二进制(.o文件)
iv. -o:生成文件
v. -I:gcc hello.c -I 【头文件路径】-o app(要么头文件与C文件在同一目录下,要么指定头文件目录)
vi. -D:gcc hello.c -I 【头文件路径】-o app -D 【宏】(编译时定义宏)
vii. -O:优化(0、1、3三个等级优化,可以优化冗余代码,例如-O0没有优化、-O1缺省值、-O3优化级别最高)
viii. -Wall:输出警告信息
ix. -g:添加调试信息(文件会变大)(gdb调试的时候必须加此参数)
3. 静态库的制作:
a. 命名规则:lib + 库的名字 + .a(所有静态库都是.a结尾),例如libmytest.a(库的名字就是mytest)
b. 制作步骤:
i. 生成对应的.o文件(xxx.c---->xxx.o,只需要-c即可转换):gcc -c *.c
ii. 将生成的.o文件打包(打包工具ar)ar rcs 【静态库的名字】【生成的所有的.o文件】
iii. -L(指定库的目录),-l(指定库的名字mytest,掐头去尾)
iv. nm 【libmytest.a】查看库的内容
c. 发布和使用静态库(两部分文件):
i. 发布静态库
ii. 头文件
d. 优缺点:
i. 优点:
1) 发布程序的时候不需要提供对应的库
2) 加载库的速度快
ii. 缺点:
1) 库被打包到应用程序中导致库的体积很大
2) 库发生了改变,需要重新编译
4. 共享库的制作:
a. 命名规则:lib+名字+.so
b. 制作步骤:
i. 生成与位置无关的代码(生成与位置无关的.o(静态库是与位置有关的.o)):gcc -fPIC -c *.c
ii. 将.o打包成共享库(动态库)(这里不是使用ar打包):gcc -shared -o 【生成的共享库名字libmytest.os】 *.o -Iinclude
c. 发布和使用共享库:
i. 发布:将libmytest.so和include发布给用户即可。
ii. 使用:
1) 第一种:gcc main.c libmytest.os -o app -Iinclude
2) 第二种:gcc main.c -Iinclude -L【库的路径】 -lmytest -o app
d. 解决程序执行时动态库无法被加载的问题:
i. ldd myapp(第二种方式执行时如果报错未找到动态库则执行ldd命令,查看myapp所有使用的库信息,动态库和执行程序之间有个动态链接器需要配置)
1) 粗暴设置:将库复制粘贴到lib目录即可使用动态链接器(PATH环境变量),sudo cp lib/libmytest.so /lib即可运行(!!!不要这样做,因为怕扰乱系统文件)
2) 临时设置:可以将动态库路径指定到环境变量:LD_LIBRARY_PATH(临时的,关掉就没有了,开发过程中测试场景)
a) echo $LD_LIBRARY_PATH(发现没有值)
b) 赋值:$LD_LIBRARY_PATH=【动态库路径】
c) export $LD_LIBRARY_PATH(将设置的值导入到系统环境变量中)
3) 永久设置(不推荐):
a) 在家目录下,ls -a找到.bashrc(配置文件,重启终端生效)
b) vi .bashrc
c) G将光标移动到最后一行,将export LD_LIBRARY_PATH=【绝对路径】写在在最后一行即可永久操作动态库。
4) 第四种设置方法:
a) 需要找到动态链接器的配置文件————ls -l ld.so.conf(/etc目录下)
b) 动态库的路径写到配置文件中————sudo vi ld.so.conf
c) o添加一行(将动态库绝对路径写上即可),wq
d) 更新————sudo ldconfig -v(-v信息输出,tar zxvf中有)
e) ldd发现链接好了
e. 优缺点:
i. 优点:
1) 执行程序体积小
2) 动态库更新不需要重新编译(前提是接口不变,内容可以变)
ii. 缺点:
1) 需要把动态库提供给用户
2) 没有被打包到应用程序中,相对静态库较慢
1. gdb调试:gcc *.c -o app -g(-g的作用是输出调试信息)
a. 启动gdb:gdb 【应用程序名字】
b. 查看代码:l (默认打开包含main的文件)、l 【文件名】:20(打开其他代码文件的第20行代码)、l【文件名】:【函数名】(再按l继续查看下面的内容)
c. 设置断点:break 7(在第七行打断点)break也可简写为b
i. 设置当前文件断点:break、b
ii. 设置指定文件断点:b 【文件名】:行号/【函数名】
iii. 设置条件断点:b 15 if i==15(如果i==15则停)
iv. 删除断点:
1) info break(查看断点信息,找到要删除的断点编号)
2) d 【编号】
d. 查看设置的断点:i b (info break)查看断点信息
e. 开始执行gdb调试:
i. 执行一步操作:start (只执行一步)
ii. 继续执行:n(单步执行、跳过函数体)、c(继续执行,知道停在断点位置)、s(单步、可进入函数体内部)
iii. 执行多步,最直接停在断点处:run或者r
f. 单步调试
i. 进入函数体内部:s
ii. 从函数体内部跳出:finish
iii. 不进入函数体内部:n
iv. 退出当前循环:u(跳出单次循环)
g. 查看变量的值:p 【要查看的值】
h. 查看变量的类型:ptype 【要查看的变量】
i. 设置变量的值:set var i=10
j. 设置追踪变量:display 【要追踪的变量】
k. 取消追踪变量:
i. info dispaly(第一步要先查看设置的追踪变量的编号)
ii. undisplay 【追踪变量的编号】
l. 退出gdb调试:quit
2. makefile的编写:项目源代码管理工具(文件太多时gcc会非常长,这时要用到makefile)
a. makefile的命名:Makefile或makefile
b. makefile的规则:
i. 规则中的三要素:目标、依赖、命令
/*第一行*/目标:依赖条件 app:main.c add.c sub.c mul.c (.c文件要在同一级目录,否则要指定目录)
/*第二行*/(tab缩减)命令 gcc main.c add.c sub.c mul.c -o app
ii. makefile第二种方法:
第一种方法修改后,修改任一.c文件都需要将整个重新编译,很麻烦。所以引申出第二种方法,即全部生成.o文件(先编译),如图,第一行确定总目标是由.o文件生成app,接下来几行是由.c文件生成.o文件(即编译),如果某个.c文件有做修改,那么只需再次编译对应.c为.o文件。(可以理解为第一行子目标是为了生成对应的依赖,第一条中的命令最后被执行。)
c. makefile工作原理
d. makefile第三种方法(取变量中的值用$(变量))
原先的程序: -------------------------------------------------->>>>>>>>>>>>>修改简化:
app:main.o add.o sub.o mul.o obj = main.o add.o sub.o mul.o 用变量代替,$()取变量的值
gcc main.o add.o sub.o mul.o -o gcc target=app
main.o:main.c $(target):$(obj)
gcc -c main.c gcc $(obj) -o $(target)
add.o:add.c %.o:%.c
gcc -c add.c gcc -c $< -o $@
sub.o:sub.c
gcc -c sub.c
mul.o:mul.c
gcc -c mul.c
e. makefile中的变量:
i. 普通变量:
1) 变量取值:foo=$(obj)
2) makefile中自己维护的变量:(大写的都是makefile库中自己维护的变量,部分有默认值,用户都可以自己设定默认值)
a) CC : 默认值为cc
b) CPPFLAGS 预编译工程中的参数选项,可以自己设定默认值
c) 还有如下变量:
i.CPPFLAGS 预处理器需要的选项,如-I
ii.CFLAGS 编译的时候使用的参数,如-Wall -g -c
iii.LDFLAGS 链接库使用的选项,如-L -l
d) 用户可以自己设定默认值CC=gcc
ii. 自动变量:
1) 变量:
a) $<:规则中的第一个依赖
b) $@:规则中的目标
c) $^:规则中的所有依赖
d) 都必须在规则中的命令使用
2) 横式规则:
a) 在规则的目标定义中使用%
b) 在规则的依赖条件中使用%
f. makefile中函数的使用
i. src=$(wildcard 【指定目录】/*.c) — — — — 函数wildcard:在指定目录下查找所有.c文件
ii. obj=$(patsubst【指定目录】/%.c,【指定目录】/%.o,$(src)) — — — — 匹配替换,将src找到的.c文件替换为.o文件
iii.
然后make clean,则只执行make clean命令
例,makefile中输入如下:
hello:
echo “hello,makefile”
退出makefile,并在unbuntu下输入make hello,则只执行makefile文件中的hello命令。
3. 系统IO函数C库:
a. 文件描述符:
b. 虚拟地址空间:程序运行时相当于一个进程,会分配一个虚拟地址空间,32位的电脑是2^32(即4G)空间
cpu为什么要使用虚拟空间地址与物理空间地址映射?解决了什么样的问题?
1) 方便编译器和操作系统安排程序的地址分布。
程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
2) 方便进程之间隔离
不同进程使用虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。
3) 方便OS使用你那可怜的内存
程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓存区。当物理内存的供应量变小时,
内存管理器会将物理内存页(通常为4KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
c. 库函数与系统函数的关系:
d. open函数:(man man查看open函数,在第二章系统调用函数,输入man 2 open即可查看)
e. open函数中的errno:(extern int errno ,用extern声明的值就是全局变量)
f. open函数的使用:
i. 打开文件步骤:
#include
#include
#include
int main()
{
int fd;
//打开已经存在的文件
fd=open("bucunz",O_RDWR);
if(fd==-1)
{
perror("open file");
exit(1);
}
}
ii. 关闭文件步骤:
关闭文件函数close(int fd),运用到头文件,如果关闭不成功返回-1,若成功返回文件描述符fd
//关闭文件
int ret=close(fd);
printf("ret=%d\n",ret);
if(ret ==-1)
{
perror("close file");
exit(1);
}
另外exit用到头文件,perror运用到头文件
iii. 使用open函数创建新文件:
//创建文件
fd=open("myhellow",O_RDWR|O_CREAT,0777);
if(fd==-1)
{
perror("open file");
exit(1);
}
printf("fd=%d\n",fd);
创建文件后发现权限不是777,为什么呢?
因为O_CREAT创建文件时文件权限内含一个本地掩码,文件的实际权限=给定权限与(&)取反(~)本地掩码
可以通过umask命令得到本地掩码,然后计算出实际权限
例:给定权限777,本地掩码0002
则给定权限为111111111
本地掩码取反111111101
与操作(&) 111111101
实际权限为775,实际上是操作系统对文件的保护机制。Ubuntu键入umask 022 ,则掩码修改为022,掩码可修改
iv. 判断文件是否存在:参数O_EXCL
//判断文件是否存在
fd=open("myhello",O_RDWR|O_CREAT|EXCL,0777);
if(fd==-1)
{
perror("open file");
exit(1);
}
printf("fd=%d\n",fd);
v. 文件截断(即文件原本有内容,执行完后清空(截断)为0),参数O_TRUNC
//将文件截断为0
fd=open("myhello",O_RDWR|O_TRUNC);
if(fd==-1)
{
perror("open file");
exit(1);
}
printf("fd=%d\n",fd);
g. read函数的使用:
i. map 2 read查看read函数的使用
ii. 头文件:
iii. ssize_t read(int fd,void *buf,size_t count)
1) size_t是int类型无符号返回值,ssize_t是有符号 — — — —为什么要有符号呢?因为有符号需要返回-1
2) fd文件描述符
3) buf(buffer)用户给定缓冲区
4) count缓冲区大小
iv. 返回值
1) -1 ———读文件失败
2) 0 ———文件已读完
3) >0 ——— 读取的字节数,fread返回值也是字节数
h. write函数的使用
i. map 2 write
ii. 头文件:
iii. ssize_t write(int fd,void *buf,size_t count)
i. lseek函数的使用(移动文件指针或获取文件长度)
i. map 2 lseek
1) 获取文件大小
int ret =lseek(fd,0,SEEK_END);
printf("file length=%d\n",ret);
2) 移动文件指针
3) 文件拓展:只能向后拓展,中间位置或者尾部向后,不能向前拓展。将文件变长。
//文件扩展
int ret =lseek(fd,2000,SEEK_END);
printf("return value %d\n",ret);
//实现文件拓展后,需要再最后做一次写操作
write(fd,"a",1);
从最后的位置SEEK_END向后拓展2000byte,并写入字符为1的内容a
ii. off_t lseek(int fd,off_t offset,int whence)
1) off_t是int类型数
2) fd文件描述符
3) offset文件指针偏移量
4) whence有三个值
a) SEEK_SET:将读写位置指向文件头后再增加offset个位移量。
欲将读写位置移到文件开头时:lseek(int fildes,0,SEEK_SET);
b) SEEK_CUR:以目前的读写位置往后增加offset个位移量。
欲将读写位置移到文件尾时:lseek(int fildes,0,SEEK_END);
c) SEEK_END:将读写位置指向文件尾后再增加offset个位移量。
想要取得目前文件位置时:lseek(int fildes,0,SEEK_CUR);
5) 返回值:
a) 当调用成功时则返回目前的读写位置,也就是距离文件开头多少个字节。若有错误则返回-1,errno 会存放错误代码。
b) 可能设置erron的错误代码:
i) EBADF:fildes不是一个打开的文件描述符。
ii) ESPIPE:文件描述符被分配到一个管道、套接字或FIFO。
iii) EINVAL:whence取值不当。
j. //lseek代码示例:
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
int readfd, writefd;
long filelen=0;
int ret=1;
char buffer[BUFFER_SIZE];
char *ptr;
/*打开源文件*/
if((readfd=open("test.txt", O_RDONLY|O_CREAT)) == -1)
{
printf("Open Error\n");
exit(1);
}
/*创建目的文件*/
if((writefd=open("dest.txt", O_WRONLY|O_CREAT)) == -1)
{
printf("Open Error\n");
exit(1);
}
/*测得文件大小*/
filelen= lseek(readfd,0L,SEEK_END);
lseek(readfd,0L,SEEK_SET);
printf("read file size is %d\n",filelen);
/*进行文件拷贝*/
while(ret)
{
ret= read(readfd, buffer, BUFFER_SIZE);
if(ret==-1)
{
printf("read Error\n");
exit(1);
}
write(writefd, buffer, ret);
filelen-=ret;
bzero(buffer,BUFFER_SIZE);
}
close(readfd);
close(writefd);
exit(0);
}
k.读写文件代码示例:
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 2048
int main()
{
//打开一个已经存在的文件
int fd = open("english.txt",O_RDONLY);
if(fd == -1)
{
perror("open");
exit(1);
}
//创建一个新文件 --写操作
int fd1 = open("newfile",O_CREAT|O_WRONLY,0664);
if(fd == -1)
{
perror("open1");
exit(1);
}
//read file
char buf[BUFFER_SIZE]={0};
int count = read(fd,buf,sizeof(buf));
if(count=-1)
{
perror("read");
exit(1);
}
while(count)
{
//将读出的数据写入另一个文件中
int ret = write(fd1,buf,count);
printf("write bytes %d\n,ret");
//continue read file
count = read(fd,buf,sizeof(buf));
}
//close file
close(fd);
close(fd1);
}
1. Linux文件操作相关函数
a. stat函数:
i. (获取文件属性,从inote上获取)stat【文件名】
ii. 返回值0、-1
iii. Inode号:通过Inote号查找磁盘文件,文件本身就是一个硬链接,该文件没有其他链接。
iv. man 2 stat(stat不打开文件,直接告知路径。fstat打开文件)
1) 头文件:、、
2) int stat(const char *pathname,struct stat *buf)
const char *pathname(传入路径)
struct stat *buf(结构体指针,定义了一块缓存区,此处为传出数据,将数据写入缓存区)
v. struct stat *buf (文件属性)
st_mode:
1) 该变量占2byte共16位,整型
2) 掩码的使用st_mode &(与运算)掩码(8进制)(获得全部权限需要&掩码,如果是单一权限则直接用前面的即可)
3) 其他人权限(0-2bit)(掩码:S_IRWXO 00007过滤st_mode中除其他人权限以外的信息)
a) S_IROTH 00004
b) S_IWOTH 00002
c) S_IXOTH 00001
4) 所属组权限(3-5bit)(掩码:S_IRWXU 00070过滤st_mode中除所属组权限以外的信息)
a) S_IRGRP 00040
b) S_IWGRP 00020
c) S_IXGRP 00010
5) 文件所有者权限(6-8bit)(掩码:S_IRWXU 00700过滤st_mode中除所有者权限以外的信息)
a) S_IRUSR 00400
b) S_IWUSR 00200
c) S_IXUSR 00100
6) 特殊权限位(9-11bit)(很少用)
a) S_ISUID 0004000 设置用户ID
b) S_ISGID 0002000 设置组ID
c) S_ISVTX 0001000 黏着位
7) 文件类型(12-15bit)(掩码:S_IFMT 0170000过滤st_mode中除文件类型以外的信息)
与掩码做与运算过滤掉除文件类型以外的信息得到文件类型的八进制码与下面七个文件类型比对即可找出是哪个文件类型
a) S_IFSOCK 0140000 套接字
b) S_IFLINK 0120000 符号链接(软链接)
c) S_IFREG 0100000普通文件
d) S_IFBLK 0060000 块设备
e) S_IFDIR 0040000目录
f) S_IFCHR 0020000字符设备
g) S_IFIFO 0010000 管道
vi. 示例:(通过stat函数实现ls -l功能)
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]) //argc参数个数,argv数组
{
if(argc <2 ) //传输两个
{
printf("./a.out filename\n");
exit(1);
}
struct stat st;
int ret =stat(argv[1],&st);
if(ret == -1)
{
perror("stat");
exit(1);
}
//存储文件类型和访问权限
char perms[11]={0};
//判断文件类型
switch (st.st_mode & S_IFMT)
{
case S_IFLNK:
perms[0]='1';
break;
case S_IFDIR:
perms[0]='d';
break;
case S_IFBLK:
perms[0]='b';
break;
case S_IFCHR:
perms[0]='c';
break;
case S_IFSOCK:
perms[0]='s';
break;
case S_IFIFO:
perms[0]='P';
break;
default:
perms[0]='?';
break;
}
//判断文件的访问权限
//文件所有者
perms[1] = (st.st_mode & S_IRUSR) ? 'r':'-';
perms[2] = (st.st_mode & S_IRUSR) ? 'w':'-';
perms[3] = (st.st_mode & S_IRUSR) ? 'x':'-';
//文件所属组
perms[4] = (st.st_mode & S_IRGRP) ? 'r':'-';
perms[5] = (st.st_mode & S_IRGRP) ? 'w':'-';
perms[6] = (st.st_mode & S_IRGRP) ? 'x':'-';
//其他人
perms[7] = (st.st_mode & S_IROTH) ? 'r':'-';
perms[8] = (st.st_mode & S_IROTH) ? 'w':'-';
perms[9] = (st.st_mode & S_IROTH) ? 'x':'-';
//硬链接计数
int linkNum =st.st_nlink;
//文件所有者
char* fileUser = getpwuid(st.st_uid)->pw_name;
//文件所属组
char* fileGrp = getgrgid(st.st_gid)->gr_nmae;
//文件大小
int filesize = (int)st.st_size;
//修改时间
char* time = ctime(&st.st_mtime);
char mtime[512] ={0};
strncpy(mtime,time,strlen(time)-1);
char buf[1024];
sprintf(buf,"%s %d %s %s %d %s %s",perms,linkNum,fileUser,fileGrp,filesize,mtime,argv[1]);
printf("%s\n",buf);
return 0;
}
b. lstat函数:lstate和state区别
i. state函数:穿透(追踪)函数 ——软连接,即根据软连接追踪到所需执行的文件查看大小
ii. lstate函数:不穿透(不追踪)函数 ——直接读软连接文件的大小
iii. 区别就是操作软连接时是否追踪,
c. access函数:
#include
#include
#include
int main(int avgc,char *argv[])
{
if(argc<2) //两个文件,一个a.out在argv[0],一个.txt文件在argv[1]
{
printf("a.out filename\n");
exit(1);
}
int ret=access(argv[1],W_OK);
if(ret==-1)
{
perror("access");
exit(1);
}
printf("you can write the file.\n");
return 0;
}
i. 作用:测试指定文件是否拥有某种权限
ii. 原型:int access(const char *pathname,int mode);
1) 参数:
a) pathname -->文件名
b) mode -->权限类别
i) R_OK 是否有读权限
ii) W_OK 是否有写权限
iii) X_OK 是否有执行权限
iv) F_OK 测试一个文件是否存在
2) 返回值:
a) 0 -->所有欲查核的权限都通过了检查
b) -1 -->有权限被禁止
d. chmod函数:
#include
#include
#include
int main(int avgc,char *argv[])
{
if(argc<2)
{
printf("a.out filename\n");
exit(1);
}
int ret=chmod(argv[1],0755);
if(ret==-1)
{
perror("chmod");
exit(1);
}
printf("you change the file's mode.\n");
return 0;
}
i. 作用:改变文件的权限
ii. 原型:int chmod(const char *filename,int pmode)
1) 参数:
a) filenam -->文件名
b) pmode -->权限(必须是一个8进制数)(可用strtol函数传入一个数转换为八进制数)
iii. 返回值:
1) 0:改变成功
2) -1:失败
e. chown函数:
#include
#include
#include
int main(int avgc,char *argv[])
{
if(argc<2)
{
printf("a.out filename\n");
exit(1);
}
//user ftp 116,group ftp 125
int ret=chown(argv[1],116,125);
if(ret==-1)
{
perror("chown");
exit(1);
}
return 0;
}
i. 作用:chown改变文件所有者,fchown文件操作,lchown不穿透
ii. 参数:(在/etc/passwd下找uid和pid)
1) path文件路径
2) owner文件所有者uid
3) group文件所属组gid
f. truncate函数:(lseek也能实现文件拓展,将指针偏移量偏移n来实现,但是要额外执行一步写操作,truncate则不需要。)
i. 作用:将参数path指定的文件大小改为参数length指定的大小。如果原来的文件大小比参数length的大,则超过的部分会被删去
ii. int truncate(const char *path,off_t length);
1) path文件路径
2) length指定的文件长度 :如果文件本身长为100,length指定为20,那么后面80长度的字符被截断,只保留前面20个字符。如果指定为300,则文件被扩展,后面扩展的内容以空洞(@符号)的形式显示
g. 链接函数:
i. link函数:
1) 作用:创建一个硬链接
2) 原型:int link(const char *oldpath,const char *newpath);
ii. symlink函数:作用:创建一个软链接(符号链接)
iii. readlink函数:作用:读软连接对应的文件名,不是读内容。readlink(const char*path,char*buf,size_t bufsize)只能读软链接,buf读出来的是软链接所指文件的绝对路径。
#include
#include
#include
int main(int argc,char *argv[])
{
if(argc<2)
{
printf("a.out softlink\n");
exit(1);
}
char buf[512];
int ret = readlink(argv[1],buf,sizeof(buf));
if(ret==-1)
{
perror("readlink");
exit(1);
}
buf[ret]=0;
printf("buf=%s\n",buf);
return 0;
}
iv. unlink函数:int unlink(const char *pathname)
1) 作用:删除一个文件的目录项并减少它的链接数,若成功返回0,失败返回-1,错误信息存与errno。
2) 如果想要通过调用这个函数来成功删除文件,你必须拥有这个文件的所属目录的写(w)和执行(x)权限
3) 使用:
a) 如果是符号链接,删除符号链接
b) 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode
c) !!!如果文件硬链接为0,但有进程已打开该文件,并持有文件描述符,则等进程关闭该文件时,kernel才真正去删除该文件
i) 利用该特性创建临时文件,先open或creat创建一个文件,马上unlink此文件
#include
#include
#include
#include
#include
#include
int main()
{
int fd=open("tempfile",O_CREAT|O_RDWR,0664);//创建临时文件
if(fd==-1)
{
perror("open");
exit(1);
}
//删除临时文件
int ret=unlink("tempfile");
//write file
write(fd,"hello\n",6);
//重置指针
lseek(fd,0,SEEK_SET);
//read file
char buf[24]={0};
int len=read(fd,buf,sizeof(buf));
//将读出的内容写在屏幕上
/*0对应std::cin标准输入,
1对应std::cout标准输出,
2对应std::error标准错误*/
write(1,buf,len);
//关闭文件
close(fd);
return 0;
}
h. rename函数:
i. 作用:文件重命名
ii. 头文件:stdio.h
iii. 函数原型:int rename(const char *oldpath,const char *newpath)(oldpath旧名字,newpath新名字)
i. 目录操作函数:
i. chdir函数:(与getcwd结合一起理解)
1) 修改当前进程的路径
2) 原型:int chdir(const char *path);
ii. getcwd函数:
1) 获取当前进程工作目录
2) 原型:char* getcwd(char *buf,size_t size);
iii. mkdir函数:
1) 作用:创建目录
2) !!!注意!!!:创建的目录需要有执行权限,否则无法进入目录
3) 原型:int mkdir(const char *pathname,mode_t mode);
iv. rmdir函数:
1) 作用:删除一个空目录
2) 函数原型:int rmdir(const char *pathname);
v. opendir函数
1) 作用:打开一个目录
2) 原型:DIR*opendir(const *name);
3) 返回值:
a) DIR结构指针,该结构是一个内部结构,保存所打开的目录信息,作用类似于FILE结构(openfile -->read--->write--->close)
b) 函数出错返回NULL
vi. readdir函数!!!(指定一个while循环,遍历目录中的文件,将文件信息通过返回值反馈直至反馈NULL值即遍历完成,当在遍历过程中又遇到目录,则进入目录继续遍历,对树状结构遍历最好的方法即递归)
1) 作用:读目录
2) 函数原型:struct dirent *readdir(DIR *dirp);
3) 返回值(返回一条记录项)
//ino 此目录进入点的inode
//off 目录文件开头至此目录进入点的位移,偏移量
//d_reclen d_name(文件名)的长度,不包含NULL字符
//d_type d_name所指文件类型
//d_name[NAME_MAX+1] 文件名
4) d_type:
a) DT_BLK 块设备
b) DT_CHR 字符设备
c) DT_DIR 目录
d) DT_LNK 软链接
e) DT_FIFO 管道
f) DT_REG 普通文件
g) DT_SOCK 套接字
h) DT_UNKOWN 未知
5) -D_BSD_SOURCE 编译时添加宏定义
v.closedir:关闭目录
#include
#include
#include
#include
//定义一个函数实现递归功能
int getFileNum(char* root)
{
//打开目录
DIR* dir=NULL;
dir=opendir(root);
int total=0;
if(dir==NULL) //如果目录能打开,返回的肯定是非0值
{
perror("opendir");
exit(1);
}
//遍历当前打开的目录
struct dirent* ptr=NULL;//因为readdir是一个结构体类型的指针,所以我们要定义一个这样的指针
char path[1024]={0};
while(ptr=readdir(dir)!=NULL)
{
//过滤掉. 和 ..
if(strcmp(ptr->d_name,".")==0||strcmp(ptr->d_name,"..")==0)
continue;
if(ptr->d_type == DT_DIR)
{
//递归 读目录
sprintf(path,"%s/%s"),root,ptr->d_name;
total += getFileNum(path);
}
//如果是普通文件
if(ptr->d_type == DT_REG)
{
total ++
}
closedir(dir);
return total;
}
}
int main(int argc,char* argv[])
{
if(argc <2)
{
printf("./a.out dir\n");
exit(1);
}
int total=getFileNum(argv[1]);
printf("%s has file numbers %d\n",argv[1],total);
return 0;
}
j. dup、,dup2函数
i. 作用:复制现有的文件描述符(重定向文件描述符)
ii. int dup(int oldfd);返回值是文件描述符中没有被占用的最小的文件描述符。
(由前面的文件描述符的知识可知,0、1、2是已经被系统占用(常开)的文件描述符,那么文件会从第三个文件描述符开始使用,例如该文件使用的是第三个文件描述符,那么使用了dup函数后,将第四个文件描述符(未占用且最小)复制给他)
iii. int dup2(int oldfd,int newfd);将old复制给new,如果new是一个被打开的文件描述符,在拷贝之前先关掉new,如果old和new是同一个文件描述符,则不会关闭而是返回同一个文件描述符old。
iv. dup示例:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd = open("a.txt",O_RDWR);
if(fd==-1)
{
perror("open");
exit(1);
}
printf("file open fd = %d\n",fd);
//找到进程文件描述表中 ==第一个== 可用的文件描述符
//将参数指定的文件复制到该描述符后,返回这个描述符
int ret=dup(fd);
if(ret==-1)
{
perror("dup");
exit(1);
}
printf("dup fd =%d\n",ret);
char *buf = "你是猴子搬来的救兵吗????\n";
char *buf1="你大爷的,我是程序猿!!!\n";
write(fd,buf,strlen(buf));
write(ret,buf1,strlen(buf1));
close (fd);
return 0;
}
v. dup2示例:
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd = open("english.txt",O_RDWR);
if(fd==-1)
{
perror("open");
exit(1);
}
int fd1 = open("a.txt",O_RDWR);
if(fd1==-1)
{
perror("open");
exit(1);
}
printf("file open fd = %d\n",fd);
printf("file open fd1 = %d\n",fd1);
//找到进程文件描述表中 ==第一个== 可用的文件描述符
//将参数指定的文件复制到该描述符后,返回这个描述符
int ret=dup2(fd1,fd);
if(ret==-1)
{
perror("dup2");
exit(1);
}
printf("current fd =%d\n",ret);
char *buf = "主要看气质!!!!!!!\n";
write(fd,buf,strlen(buf));
write(fd1,"hello,world!",13);
close (fd);
close (fd1);
return 0;
}
k. fcntl函数:
i. 作用:改变已经打开的文件的属性,例如打开文件的时候(文件为只读),修改文件的时候,如果要添加追加O_APPEND,可使用fcntl在文件打开时修改。
ii. 原型:
1) int fcntl(int fd,int cmd);
2) int fcntl(int fd,int cmd,long arg);!!!
3) int fcntl(int fd,int cmd,struct flock *lock);
iii. 功能:
1) 复制一个现有的描述符 -- cmd F_DUPFD
2) 获得/设置文件描述符标记 -- cmd F_GETFD/F_SETFD
3) 获得/设置文件标志标记(打开文件时追加)!!!
a) cmd F_GETFL
i) 只读打开 O_RDONLY
ii) 只写打开 O_WRONLY
iii) 读写打开 O_RDWR
iv) 执行打开 O_RXEC
v) 搜索打开目录 O_SEARCH
vi) 追加写 O_APPEND
vii) 非阻塞模式 O_NONBLOCK
b) cmd F_SETFL 可更改的几个标识
i) O_APPEND
ii) O_NONBLOCK
4) 获得/设置异步I/O所有权 --cmd F_GETOWN/F_SETOWN
5) 获取/设置记录锁 -- cmd
a) F_GETLK
b) F_SETLK
c) F_SETLKW
iv. 示例:
#include
#include
#include
#include
#include
int main()
{
int fd;
int flag;
//测试字符串
char *p="我们是一个有中国特色社会主义的国家!!!!!!";
char *q="社会主义好!!!";
//只写的方式打开文件
fd = open("test.txt",O_WRONLY);
if(fd==-1)
{
perror("open");
exit(1);
}
//输入新的内容,该部分会覆盖原来的旧内容
if(write(fd,p,strlen(p))==-1)
{
perror("write");
exit(1);
}
//使用F_GETFL命令得到文件状态标志
flag=fcntl(fd,F_GETFL,0);
int(flag ==-1)
{
perror("fcntl");
exit(1);
}
/*将文件状态标准添加“追加些”选项,追加写就是文件指针在文件末尾写,WRONLY是在文件
开头写,所以每次写都会覆盖原先的内容*/
flag |= O_APPEND;
//将文件状态修改为追加写
if(fcntl(fd,F_SETFL,flag)==-1)
{
perror("fcntl --append write");
exit(1);
}
//再次输入新内容,该内容会追加到就内容后面
if(write(fd,q,strlen(q))==-1)
{
perror("write again");
exit(1);
}
//关闭文件
close(fd);
return 0;
//最后功能为:一大段文本内容中,第一句话覆盖掉了文本中的字符,第二段话追加到文本最末尾处。
}
目前Linux知识的笔记暂时做到这里,还会有管道、进程、TCP等知识另开文章总结。因为都是手打的,难免会有纰漏和错误,望指正,如果有知识性错误或者补充麻烦一定要指出来,因为我是初学者,这对我的成长有帮助。
路漫漫其修远兮,吾将上下而求索,共勉,谢谢大家。