学习视频为黑马Linux全套视频 ,视频链接:https://www.bilibili.com/video/BV1dt411f7TZ?p=1

此篇为学习笔记,参照博主:Linux学习笔记_朝闻道ZY的博客-CSDN博客

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 + H
        删除光标前的所有内容: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 apt-get install xxx(在线下载安装)
	b. 卸载:sudo apt-get remove xxx
	c. 更新:sudo apt-get update (升级所有包同时也升级软件和系统内核。更新软件列表。);sudo apt-get 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 【卸载路径】(注意:你不能在要卸载的目录中)

11. 压缩包管理

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没有该参数)   解压缩目录 ------解压缩语法

12. 进程管理

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. 什么是管道:
                指令1 | 指令2  指令1的输出作为指令2的输入。信息不直接输出到屏幕上
                                    指令2通过一个查找处理输入的信息,处理完毕后将信息输出到屏幕上
	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关闭。

13. 网络管理

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 【域名】

14. 用户管理

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

15. 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 动词
	进一步说明可参考<https://blog.csdn.net/weixin_34280237/article/details/89617656> 	
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上传整个目录及其子目录

16. 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)】

17. 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

18. scp命令

A. scp==super copy
B. 使用该命令的前提条件:目录主机已经成功安装openssh-server
C. 使用格式:
	i. scp -r 【目标用户名@目标主机IP地址】:【/目标文件的绝对路径】【/保存到本机的绝对(相对)路径】(后续输入yes,不能输入y)
	ii. 拷贝目录需要加参数-r

19. 其他命令

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:

20. vim编辑器的使用

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

21. 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调试的时候必须加此参数)

22. 静态库的制作

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,掐头去尾)
            gcc main.c lib/libMytest.a -o sum -Iinclude
            gcc main.c -Iinclude -L lib -l Mytest -o myapp
	iv. nm 【libmytest.a】查看库的内容
C. 发布和使用静态库(两部分文件):
	i. 发布静态库
	ii. 头文件
D. 优缺点:
	i. 优点:
		1) 发布程序的时候不需要提供对应的库
		2) 加载库的速度快
	ii. 缺点:
		1) 库被打包到应用程序中导致库的体积很大
		2) 库发生了改变,需要重新编译

 23.  共享库的制作

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) 没有被打包到应用程序中,相对静态库较慢

 与位置有关的静态库代码段存放在.txt中,它是一个绝对地址,每次程序运行时.o文件都会存在固定位置。而与位置无关的代码段存放在动态库,它是相对路径,.o文件不会直接打包到程序中,而是当程序运行起来时才会动态加载,每次存放的位置不同,通过动态库变化的相对路径结合代码段中的第几行来寻找正确的代码。

 24. 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

 25. makefie的编写

项目源代码管理工具(文件太多时gcc会非常长,这时要用到makefile)
A. makefile的命名:Makefile或makefile
B. makefile的规则:
	i.  makefile第一种方法:

app:main.c add.c sub.c mul.c
    gcc main.c add.c sub.c mul.c -o app

            规则中的三要素:目标、依赖、命令
	    /*第一行*/目标:依赖条件	(.c文件要在同一级目录,否则要指定目录)
	    /*第二行*/(tab缩减)命令		
	ii.  makefile第二种方法:

app:main.o add.o sub.o mul.o
    gcc main.o add.o sub.o mul.o -o app
main.o:main.c
    gcc -c mian.c
add.o:add.c
    gcc -c add.c
sub.o:sub.c 
    gcc -c sub.c
mul.o:mul.c
    gcc -c mul.c

      第一种方法修改后,修改任一.c文件都需要将整个重新编译,很麻烦。所以引申出第二种方法,即全部生成.o文件(先编译),第一行确定总目标是由.o文件生成app,接下来几行是由.c文件生成.o文件(即编译),如果某个.c文件有做修改,那么只需再次编译对应.c为.o文件。(可以理解为第一行子目标是为了生成对应的依赖,第一条中的命令最后被执行。)

add.o:add.c
    gcc -c add.c
                add.o作为依赖其生成时间晚于目标文件add.c,但是如果我们修改了add.c,那么依赖的时间早于目标,当执行make时,检查依赖与目标的时间(依赖早于目标),并执行命令gcc -c add.c

C. makefile工作原理
        如下图.
D. makefile第三种方法(取变量中的值用$(变量))

obj = main.o add.o sub.o mul.o       #用变量代替,$()取变量的值                                                                               
target=app
$(target):$(obj)                                                                 
    gcc $(obj) -o $(target)                                                                                                                  
%.o:%.c                                                                           
    gcc -c $< -o $@

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文件
G. makefile的第四种写法
    如下图
  然后make clean,则只执行make clean命令   

第三种写法:

第四种写法:使用函数

 

makefile工作原理:

 

 26. 系统IO函数C库

A. C库IO函数工作流程:

 

为了提高C库函数的效率,提供一个缓冲区。在右侧三种情况下将缓冲区的数据写到磁盘上。

B. 文件描述符:

 拱用户能打开的文件个数为1024-3,因为前3个默认总是被打开(可被关闭)。文件描述符存在于内核区进程管理PCB进程控制块中。

C. 虚拟地址空间:

程序运行时相当于一个进程,会分配一个虚拟地址空间,32位的电脑是2^32(即4G)空间

    0-3G属于用户区,3-4G属于Linux内核区,内核去不允许用户访问。文件描述符位于内核区。
    通过file a.out 查看文件的类型,可执行文件格式为ELF。

i. ELF段:
    主要包含三个段
    a.out的源代码放在.text段;
    未初始化的全局变量等于0;
ii. 受保护的地址:
    #define NULL (void*) 0 定义0~4k为受保护地址,不允许用户访问。可执行文件.o存放在代码段中,然后是全局变量地址,紧接着是局部变量,存放在栈空间,即每次用到int之类的定义局部变量时,会在栈空间中自上而下划分内存空间存放局部变量,每定义一个划分一个。当程序中有new或者malloc,则从堆空间从下往上划分空间。
iii. 共享库:
    当调用了C/Linux标准函数,调用的都是一些动态库,拿到的不是真正的源代码。在调用fread时,会加载C标准库,放到共享库时,哪有空闲加载到哪里,因此起始地址不确定,使用的是相对位置。
iv. cpu为什么要使用虚拟空间地址与物理空间地址映射?解决了什么样的问题?
	1) 方便编译器和操作系统安排程序的地址分布。
		程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
	2) 方便进程之间隔离
		不同进程使用虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。
	3) 方便OS使用你那可怜的内存
		程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓存区。当物理内存的供应量变小时,
	内存管理器会将物理内存页(通常为4KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。

D. 库函数与系统函数的关系:

 

应用层函数操作用户空间(虚拟地址中的0-3G空间);
write(fd, "hello", 5) 获得文件描述符得到5个字符的字符串;
sys_write()操作内核空间,调用显示器驱动,在显示器中显示。

 E. open函数

 (man man查看open函数,在第二章系统调用函数,输入man 2 open即可查看)

pathname: 文件名
flags:权限,即O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(可读可写)、O_CREAT(创建文件)
mode:文件不存在时创建文件,输入权限777(0777 八进制)
每打开一个文件占用一个文件描述符,返回-1则表示打开错误
errno:打开文件失败,全局变量errno会被赋值(即错误信息)

F. open函数中的errno:

(extern int errno,用extern声明的值就是全局变量)

G. open函数的使用

i. 打开文件步骤

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
	int fd;		
	//打开已经存在的文件
	fd=open("bucunz",O_RDWR);
	if(fd==-1)
	{
		perror("open file");
		exit(1);
	}
}

先引用open函数所需的三个头文件。然后打开文件open,如果不知道open()函数括号中应该输入什么参数,可以将光标放在open函数上,然后键入shift+K(大写K),就会跳转至man man 中查询。然后用if判断文件是否打开成功,perror中的参数自己想写什么都可以,最后exit推出程序。(perror: 把一个描述性错误消息输出到标准错误 stderr)

ii. 关闭文件步骤:
close()

关闭文件函数close(int fd),运用到头文件<unistd.h>,如果关闭不成功返回-1,若成功返回文件描述符fd。
关闭文件程序:

//关闭文件
int ret=close(fd);
printf("ret=%d\n",ret);
if(ret ==-1)
{
	perror("close file");
	exit(1);
}

另外exit用到头文件<stdlib.h>,perror运用到头文件<stdio.h>。

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);

H. read/write函数的使用

1. read函数的使用:
	i. map 2 read查看read函数的使用
	ii. 头文件:<unistd.h>
	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返回值也是字节数
2. write函数的使用
	i. map 2 write
	ii. 头文件:<unistd.h>
	iii. ssize_t write(int fd,void *buf,size_t count)

3. 读写文件代码示例

read.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#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); 
}

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取值不当。

iii. //lseek代码示例:

lseek.c

#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);   
}

27. Linux文件操作相关函数

A. stat 函数

a. stat函数:
	i. (获取文件属性,从inote上获取)stat【文件名】

 

	ii. 返回值0、-1
	iii. Inode号:通过Inote号查找磁盘文件,文件本身就是一个硬链接,该文件没有其他链接。

	iv. man 2 stat(stat不打开文件,直接告知路径。fstat打开文件)

		1) 头文件:<sys/types.h>、<sys/stat.h>、<unistd.h>
		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 管道

stat.c

 vi. 示例:(通过stat函数实现ls -l功能)
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdlib.h>
#include<pwd.h>
#include<grp.h>

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 函数

b. lstat函数:lstate和state区别
   i. state函数:穿透(追踪)函数 ——软连接,即根据软连接追踪到所需执行的文件查看大小
   ii. lstate函数:不穿透(不追踪)函数 ——直接读软连接文件的大小
   iii. 区别就是操作软连接时是否追踪,

lstat.c

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

int main(int argc, char* argv[])
{
	if (argc < 2) {
        printf("./out filename\n");
        exit(0);
    }
    struct stat st;
    int ret = lstat(argv[1], &st);
    if (ret == -1) {
        perror("stat:");
        exit(1);
    }
    //获取文件大小
    int size = (int)st.st_size;
    printf("file sieze = %d\n", size);
}

 

C. access 函数

c. access函数:
	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 -->有权限被禁止

access.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
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;
}

 D. chmod 函数

d. chmod函数
	i. 作用:改变文件的权限
	ii. 原型:int chmod(const char *filename,int pmode)
		1) 参数:
			a) filename -->文件名
			b) pmode -->权限(必须是一个8进制数)(可用strtol函数传入一个数转换为八进制数)
	iii. 返回值:
		1) 0:改变成功
		2) -1:失败

chmod.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h> #include<unistd.h> 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; }

 E. chown 函数

e. chown函数
	i. 作用:chown改变文件所有者,fchown文件操作,lchown不穿透
	ii. 参数:(在/etc/passwd下找uid和pid)
		1) path文件路径
		2) owner文件所有者uid
		3) group文件所属组gid

 

 

chown.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

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;
}

 F. truncate 函数

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. 链接函数

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读出来的是软链接所指文件的绝对路径。

readlink.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
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此文件				

unlink.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
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 函数

h. rename函数:
	i. 作用:文件重命名
	ii. 头文件:stdio.h
	iii. 函数原型:int rename(const char *oldpath,const char *newpath)(oldpath旧名字,newpath新名字)

 I. 目录操作函数

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:关闭目录

 

 chdir.c

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

int main(int argc, char* argv[])
{
	if (argc < 2) {
        printf("a.out dir\n");
        exit(1);
    }
    int ret = chdir(argv[1]);
    if (ret == -1) {
        perror("chdir:");
        exit(1);
    }
    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0777);
    if (fd == -1) {
        perror("open");
        exit(1);
    }
    close(fd);
    
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("current dir: %s\n", buf);
    
    return 0;
}

 查找目录下文件个数

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<dirent.h>
//定义一个函数实现递归功能
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函数

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示例:

dup.c

			#include<stdio.h>
			#include<stdlib.h>
			#include<string.h>
			#include<unistd.h>
			#include<sys/types.h>
			#include<sys/stat.h>
			#include<fcntl.h>
			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;
}

dup2.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
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函数

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. 示例:

 

fcntl.c

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
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;
    //最后功能为:一大段文本内容中,第一句话覆盖掉了文本中的字符,第二段话追加到文本最末尾处。
}

 

 

 待更。。。

版权声明:本文为KYZH原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/kyzh-lhl/p/16807980.html