概述
Linux命令
目录
Linux命令-Linux系统编程
类Unix系统目录结构
目录和文件操作
软连接和硬链接
创建修改用户和用户组
find
grep和xargs
xargs加强和awk说明
软件包的安装
压缩命令gzip和bzip2
其他命令
vim的三种工作模式
vim的基本操作-跳转和删字符
vim基本操作-删除
vim基本操作—复制粘贴
vim基本操作-查找替换
vim基本操作-其他
vim基本操作-其他
vim配置思路
gcc编译四步骤
gcc编译常用参数
动态库和静态库理论对比
静态库制作
静态库使用及头文件对应
动态库制作及使用
动态库加载错误原因及解决方式
数据段合并
gdb调试基础指令
gdb调试其他命令
gdb常见错误说明
makefile
系统编程阶段说在前面的话
open函数
read和write实现cp
系统调用和库函数比较
文件描述符
阻塞与非阻塞
fcntl的属性
lseek函数
传入传出参数
目录项和inode
stat函数
Istat和stat
传出参数与返回值
link和unlink隐式回收
文件目录rwx权限差异
目录操作函数
递归遍历目录思路分析
递归遍历目录代码预览
dup和dup2
fcntl实现dup描述符
进程和程序以及CPU有关
虚拟内存和物理内存映射关系
pcb进程控制块
环境变量
Fork函数原理
Fork创建子进程
getpid和getppid
循环创建N个子进程
父子进程共享哪些内容
父子进程gdb调试
exec函数族原理
-execlp和execl函数
execlp函数
exec函数族的特性
孤儿进程和僵尸进程
孤儿进程
僵尸进程
wait回收子进程
获取子进程退出值和异常终止信号
Linux基础命令学习资源
-Linux系统编程
1.终端是一系列输入输出设备的总称。
2.Linux的终端是虚拟终端,有输入和输出。
3.终端里面内嵌了一个SHELL,是一个命令解析器。
4.cat /etc/shells
输入后会显示当前操作系统支持哪些SHELL。
5.echo $SHELL
查看当前操作系统正在用的SHELL。
6.bash和SHELL有一定差别
7.命令和路径补齐
敲tab可以补齐命令和文件
history
8.主键盘快捷键
Ctrl-p向上
Ctrl-n向下
Ctrl-b向左
Ctrl-f向右
Ctrl-a移到首位
Ctrl-e移到末尾
Ctrl-u清空
类Unix系统目录结构
1.cd ..回到上一级目录
ls查看
pwd查看当前目录
cd 目录 跳转到某一目录
2.根目录下重要的目录
bin存放可执行文件
boot存放开机启动历程
dev存设备,Linux系统所见皆文件
sudo cat mice查看鼠标文件
etc存当前用户相关的配置,比如password
home用户的目录
lib存库路径,如C库libc.so.6
media与磁盘有关
opt与linux编程进程有关
root用户,cd不进去
sudo su cd /root可以进去
user存用户的目录,用户存的第三方应用
3.不要随便用root用户
目录和文件操作
1.相对路径和绝对路径
cd ./home
cd - 返回到上一个目录
cd ~回到家目录
2.在home目录下面创建
mkdir 28_Linux
用绝对路径去28_Linux
cd /home/itcase/28_Linux/
或者
cd ~/28_Linux/
3.touch
新建空文件,或更新时间标记
4.ls -l
显示详细信息
ls -a
显示隐藏文件
ls -d
显示目录
ls -l dir
查看目录
ls -dl dir
查看目录本身
ls -R
递归记录
5.文件第一条为文件权限,第二个是硬链接计数,所有者,所属组,大小,时间,文件名。
6.权限分为:文件类型,所有者权限,同组用户权限,其他人权限。
7.Linux系统文件类型:
普通文件 -
目录文件 d
字符设备文件 c
块设备 b
软连接 l
硬链接是一种普通文件
管道文件 p
套接字 s
未知文件
8.which
查看指定命令所在路径,即命令所在目录位置
9.pwd
查看当前所在路径,即当前的目录位置
10.可以用终端配置取消掉目录显示
11.rmdir
删除空目录,目录非空不能删
12.rm -r可以删除非空目录
(-r意思是递归
13.rm dir -rf(rf意思是强制删除)
强制删除一个目录,有些版本非强制删除会提示
14.cp
拷贝命令,可以拷贝到目录。
15.略过目录dir
cp dir ..拷贝dir到上一级目录下,目录非空时,该命令不成功
cp -r dir ..
cp -a dir ..
cp dir1 dir2 -r
16.cat适合读小文件
查看文件中的内容cat hello.c
cat直接回车,会返回显示
tac 会让文件倒着显示
Linux维护,查看日志文件,用到tac
17.more
more stdio.h
空格翻页
可以分屏显示,适合查看大文件。
Ctrl+C,q终止显示
18.less
Ctrl+C,q终止显示
19.head
显示默认前10行
head -5显示前五行
tail -15 从后边查看
20.tree
不是自带的命令,安装命令是sudo apt-get install tree
把当前目录结构以树状展示
21.wc
统计字数和字节数
-c字节数
-l列数
-w字数
22.od 显示当前文件,指定数据显示格式
23.du查看目录大小
24.df查看磁盘剩余空间
软连接和硬链接
1.创建软连接
ln -s file file.s
给file创建软连接file.s
相当于windows的快捷方式
文件信息文件大小为访问路径大小
创建软连接到某目录用绝对路径,建立后可以用mv进行移动
软连接权限为rwx,不影响源文件权限
2.硬链接
ln file file.h
任意修改一个文件,会影响所有文件
创建修改用户和用户组
1.whoami 查看当前用户
2.chmod 修改一个文件属性
chmod u+x a.c(字母修改)
chmod o-r a.c
数字修改
r为4,w为2,x为1
chmod 471 a.c
chown 修改用户
sudo adduser wangwu
sudo addgroup g88
sudo chown wangwu a.c
sudo chgrp g88 a.c
sudo chown nobody:nogroup a.c
sudo deluser wangwu删除用户
find
1.find ./ -type 'l' 按类型查找
2.find ./ -name '*.jpg' 按文件名字查找
3.find ./ -maxdepth 2 -name '*.jpg' 路径层数
4.find ./ -size +20M -size -50M 按文件大小搜索
find ./ -size +20k -size -50k
find ./ -size +20G -size -50G
5.块是磁盘的最小区域,块为512字节。
6.-atime
-mtime
-ctime
find ./ -ctime 1
按修改时间
7.-exec
find /user/ -name "tmp" -exec ls -l { } ;
find /user/ -name "tmp" -exec rm -r { } ;
find /user/ -name "tmp" -ok rm -r { } ;
8.xargs 当find的搜索结果集执行某一指定命令时,当结果集数量过大时,可以分片映射
9.-print0
grep和xargs
1.grep 根据内容搜索
grep -r 'copy' ./
grep -r 'copy' ./ -n(加-n会出现行号)
ps aux 显示后台进程
ps aux | grep kernel
结合管道搜索后台进程
find -maxdepth 1 -type f | xargs ls -l
2.touch abc xyz 会创建两个文件,想创立一个文件,解决办法是touch 'abc xyz'
touch abc xyz
此时用find -maxdepth 1 -type f | xargs ls -l
不会搜到abc xyz,该命令搜索会按空格拆分
find -maxdepth 1 -type f -print0 | xargs -print0 ls -l
可以将拆分依据改为0
xargs加强和awk说明
1.find -maxdepth 1 -type f -print0 | xargs -print0 ls -l
可以简化为
find -maxdepth 1 -type f -print0 | xargs -0 ls -l
2.awk按照列拆分
sed按照行拆分
软件包的安装
1.apt-get更新源服务器列表。
sudo apt-get install tree
2.安装一个程序需要
联网
更新软件资源列表到本地 sudo apt-get update
安装 sudo apt-get install 软件名
卸载 sudo apt-get remove 软件名
3.有安装包时,可以离线安装
ubuntu安装包都以.deb结尾
sudo dpkg -i sl_3.03.deb
sudo apt-get
4.原码安装
cd dir
./configure
make
sudo make install
sudo make distclean
压缩命令gzip和bzip2
1.tar zcvf 要生成的压缩包名 压缩材料
tar不是用来压缩是用来打包
2.gzip 文件名
gunzip 解压缩
只能压缩文件
3.file 文件可以查看文件
4.bzip2不能压缩多个文件和目录
tar jcvf
解压 :将压缩命令中的c替换为x
1.rar需要安装
rar a -r newdir dir
unrar x 文件名
2.sudo apt-get install apititude 安装apititude
sudo aptitude / apt-get install rar/tree/unrar
sudo aptitude show tree 显示是否安装
sudo aptitude install tree 用apititude进行安装
3.zip压缩和zip解压
压缩命令: zip -r 压缩包名称 文件或普通目录
解压命令: unzip 压缩包名称
其他命令
1.who
查看是谁,显示进程
whoami
2.jobs
当前shell下正在运行哪些作业
cat & 让在后台运行
3.fg把指定的后台作业或挂起的作业移到前台
bg把挂起的进程提到后台执行
4.kill杀死进程
5.env 查看当前操作系统所有的环境变量
env | grep SHELL
6.top 文字版的任务管理器
7.sudo passwd itcast
设置密码
8.su 用户 用来切换用户
9.sudo su 变成root用户
设置root密码
passwd
10.删除用户 sudo userdel -r itcast
11.查看网卡信息,当前的IP地址:ifconfig
关闭网卡 sudo ifconfig eth0 down
开启网卡 sudo ifconfig eth0 up
给eth0配置临时IP:sudo ifconfig eth0 IP
12.man 是一个帮助手册 掌握123章
13.clear清屏 可以用Ctrl+L
14.date 显示时间
15.alias 可以给命令起名
例如alias pg='ps aux | grep'
16.umask 指定用户创建文件文件时的掩码
掩码没有执行权限
输入umask出现数字首位是0,是8进制
17.创建终端
创建终端标签Ctrl+Shift+t
切换标签Alt+n(n=1)
新开终端Ctrl+Shift+n
18.关机重启
poweroff直接关机
shutdown
reboot 重启
uname -a 查看内核版本信息
free -m 查看空闲内存
vim的三种工作模式
1.vi有三种工作模式:命令模式,文本模式,末行模式
2.vi hello.c 进入命令模式
i a o,I A O,s S切换到文本模式
文本模式进入末行模式,需要切换成命令(点击ESC),再输入":"进入末行模式
3.末行命令 w保存 q退出
4.可以从命令模式直接退出 ZZ,ZZ是保存退出
5.末行模式按ESC可以回到命令模式,输入w也可以回到命令模式
vim的基本操作-跳转和删字符
1.输入S会删除一行进入文本模式
输入s会删除一个字符进入文本模式
2.h左移
l右移
j下移
k上移
hjkl左下上右
3.跳转到指定行
命令模式输入行号+G
末行模式输入行号可以进入指定行
4.跳转文件首
gg(命令模式)
5.跳转文件尾
G(命令模式)
6.自动格式化程序
gg=G(命令模式)
7.大括号对应
%(命令模式)
8.删除单个字符
x(命令模式)
9.删除一个单词
dw(命令模式,光标置于首字母)(delete word)
10.删除某个字符到结尾
D(命令模式)
d$(命令模式)
11.删除光标模式到行首
d0(命令模式)
12.光标移到行首
0(命令模式)
13.光标移到行位
$(命令模式)
vim基本操作-删除
1.在命令模式下
光标选中字符
按r按要替换的字符可以替换
2.命令模式下按V,用hjkl挪移光标选中要删的部分,按d(delete)
3.dd,删除指定行,光标所在行按dd
4.删n行,光标放在要删的第一行,n(n为要删的行数)dd
vim基本操作—复制粘贴
1.复制一行
yy
复制多行 nyy
2.粘贴
p
会粘贴到光标所在行下一行
P
会粘贴到光标所在行的上一行
3.所有删除的内容都是剪切的内容,可以粘贴
vim基本操作-查找替换
1.找设想内容,
命令模式下按”/“,输入查找的词,回车
使用n检索下一个
2.找看到的内容
把单词选在单词任意一个字符上,按*往后找
按#往前找
3.光标移到要替换的行上
换到末行模式
输入:s /原数据/替换数据
4.通篇替换
进入末行模式
:%s /原数据/替换数据 (只是替换末行第一个)
:%s /原数据/替换数据/g(可以替换每行所有数据)
5.替换某个区域的数据
进入末行模式
:1起始行号,终止行号s /原数据/替换数据/g
6.切换到末行模式
Ctrl+p切换到上一条命令
Ctrl+n切换到下一条命令
vim基本操作-其他
1.命令模式下u,撤销命令
反撤销(撤销撤销命令)Ctrl+r
2.分屏
:sp(末行模式)
切为上下两部分
可以不断分屏
Ctril+ww可以切换光标所在分屏
:q
退出当前分屏
:qall
退出当前所有分屏
:vsp
分竖屏
3.将光标置于要查看单词上面
命令模式按K,指定卷nK
进入man page
4.查看宏定义
{d(命令模式)
查看定义语句
vim基本操作-其他
5.在末行模式下,
:! shell命令
vim配置思路
cd /etc/vim
vi vimrc
上引号开头的是注释
gcc编译四步骤
1.四步骤:预处理,编译,汇编,链接
2.hello.c预处理hello.c
预处理:展开宏,头文件
替换条件编译
删除注释,空行,空白
gcc -E
3.编译得到hello.s
编译:检查语法规范
gcc -S
4.汇编得到hello.o,是一个二进制文件
汇编:将汇编指定翻译成机器指令
gcc -c
5.链接生成a.out
链接:数据段合并,地址回填
6.消耗时间和系统资源最多是编译
gcc编译常用参数
1.头文件和原码不在同一级目录下,需要指定头文件
gcc -I头文件所在目录 hello.c -o hello
-I: 指定头文件所在目录位置
2.-c:只做预处理,编译,汇编,得到二进制文件
3.-g:编译时添加调试语句
gdb调试工具,编写程序时出现的语法错误之外的东西
4.-On n=1~3 编译优化,n越大优化越多
5.-Wall 显示所有的警告信息
6.-D:向当前的程序当中“动态”注册一个宏定义
一般作为开关,需要用到调试语句时打开
动态库和静态库理论对比
1.使用库可以提高编写效率
2.静态库是以静态文件的形式存放
静态库需要和源程序一起编译到可执行文件当中
优点是调用速度快
适用于:对空间要求较低,对时间要求较高的核心程序中
启动程序用到的是静态库
3.动态库(共享库)
适用于:对时间要求较低,对空间要求较高
4.大多数情况下使用动态库
静态库制作
1.静态库编译:
第一步:先将编写的c文件编译
gcc -c 文件名.c -o 文件名.o
第二步:ar rcs lib库名.a file1.o
2.编写程序用到库中的内容时,需要将库与C语言原程序一起编译
gcc 文件名.c lib库名.a -o test
./test
静态库使用及头文件对应
1.gcc 文件名.c lib库名.a -o test -Wall
-Wall会显示所有的警告信息
2.需要有函数定义和函数声明,否则系统会做隐式声明
隐式声明所识别的是int add(int,int);
不是隐式声明的格式会出现错误而不是警告
3.头文件首位
#ifndef MYMATH_H
#define MYMATH_H
#endif
在要执行的C语言原程序中输入
#include "mymath.h"
动态库制作及使用
1.制作过程
第一步:将.a文件生成.o文件(要求生成与位置无关的代码 -fPIC)
gcc -c 文件名.c -o 文件名.o -fPIC
第二步: 使用 gcc -shared 制作动态库
gcc -shared -o lib库名.so 文件名.o(这些原文件与制作静态库的原文件不同,差-fPIC)
第三步:编译可执行程序时,指定所使用的动态库
-l用来指定库
-L指定库路径
gcc test.c -o a.out -l动态库名(要去掉lib和.so) -L./lib
第四步:运行可以执行的程序
./a.out
2.延迟绑定:动态库绑定地址相较静态库要晚
3.LSB表示小端码存储
动态库加载错误原因及解决方式
1.
error while loading shared libraries: libraries.so: cannot open shared object file: No such file or directory
运行可以执行的程序
./a.out 出错!!!!!
原因:链接器: 工作于链接阶段,工作时需要 -l 和 -L
动态链接器: 工作于程序运行阶段,工作时需要提供动态库所在目录位置
2.
解决办法:通过环境变量:export LD_LIBRARY_PATH=./lib(动态库路径)
./a.out
3.
永久生效办法:环境变量是进程概念,关掉终端再次打开环境变量不在生效,应该修改配置文件
.bashrc建议使用绝对路径
第一步:vi ~/.bashrc
第二步:写入export LD_LIBRARY_PATH=./lib 然后:wq
第三步: . .bashrc或者source .bashrc
第四步:重启终端
./a.out
4.C库是动态库
5.ldd a.out
会显示运行程序之后会加载哪些动态库
6.拷贝自定义动态库到/lib(标准C库所在位置)
sudo cp lib库名.so /lib/x86_64-linux-gnu/
不建议使用该方法
7.配置文件法(永久修改)
第一步:sudo vi /etc/ld.so.conf
第二步:写入动态库绝对路径,保存
第三步:sudo ldconfig -v
a.out
改变动态库的绝对路径会使这个方法失效
数据段合并
1.
kernel(内核数据) |
---|
环境变量,main参数 |
stack(栈) |
.so加载位置 |
heap(堆) |
.bss |
.data |
.rodata(只读数据段) |
.text(代码段) |
stack高地址向低地址用
heap低地址向高地址用
@plt位于user
2.text段不能任意改变,只读 ro
rodata存放常量和只读数据 ro
text rodata可以合并
bss和data权限为rw
bss和data可以合并
3.Linux下,一次最少使用4K内存
gdb调试基础指令
1.gcc检查语法错误
2.-g 使用该参数编译可以执行文件,得到调试表
gcc -g main.c -o main(加了-g才能用gdb)
3.gdb检查逻辑错误
4.gcc 文件名.c -o gdbtest
5.gcc 文件名.c -o gdbtest2 -g
6.gcc gdbtest.c -o a.out -g 编译
gdb a.out 进入调试
在(gdb)后面加“l”或者“list”
在(gdb)后面加“list 1”,使得从第一行开始显示十行,再在(gdb)后面再写”l“,会出现后续的行
7.设置断点
在gdb后面输入“break”和“b”
e.g
b 52
8.(gdb)后输入run,执行程序到断点处,断点处语句没有执行
(gdb)后面输入“s”或“step”,下一条命令,不会越过函数
(gdb)后面输入“n”或“next”,下一条命令,会越过函数
9.如果函数是系统函数只能用“n“,不能用”s”
10.until 循环次数 可以退出循环
11.p 变量
print 变量
可以查看变量
12.continue
继续执行断点后续命令
13.quit:退出gdb当前调试
gdb调试其他命令
1.用gdb调试查找段错误出现位置
段错误是访问了不可访问的内存
用gdb打开一个程序,直接“run”,程序停下的位置就是段错误的位置
2.start
默认从当前程序第一行开始运行
3.finish 可以结束当前函数调用,回到函数的调用点
4.set args 设置main函数命令行参数
可以有带参数的main函数,在“./文件名”后面加上参数
在(gdb)后面加上“set args 参数 参数”(要设置在start之前),可以加入参数
5.run 参数 参数2,设置main函数参数
6.display
7.info b 查看断点信息
8.条件断点:b 行号 if 变量=数据
e.g b 20 if i=5
一般用于循环
9.ptype 变量 查看变量类型
在子函数中不能查看没有通过参数传入子函数的变量
10.栈里面存放栈帧
栈帧是随着函数调用而在stack上开辟的一片内存空间,用于存放函数调用时时产生的局部变量和临时值
kernel(内核数据) |
---|
环境变量,main参数 |
stack(栈) |
.so加载位置 |
heap(堆) |
.bss |
.data |
.rodata(只读数据段) |
.text(代码段) |
有一个函数调用就会产生一个栈帧,调用结束,栈帧消失
调用子函数main函数的栈帧不会消失,因为main函数没有结束
子函数栈帧中有形参和局部变量,子函数调用结束,子函数栈帧消失
11.利用指针可以在一片栈帧去修改另一片栈帧的值
12.backtrace:查看函数调用的栈帧和层级关系
frame:根据栈帧编号切换栈帧
bt:查看栈帧(#后面的是栈帧号)
frame 栈帧号
13.display 变量 设置跟踪变量
undisplay 变量 取消设置跟踪变量
gdb常见错误说明
常见错误
编译没有加“-g”
用gdb打开文件
list会出现 没有符号被读取,请使用“file”命令
解决方法
1.quit,重新编译
2.file a.out(a.out必须经过gdb调试)
makefile
1.Makefile实际上是一个脚本文件,脚本把一些命令的集合批量放在一个文件里,统一执行。脚本可以干任何事情,Makefile用处单一。
2.Makefile里面内容
gcc hello -o hello
hello:hello.c
gcc hello.c -o hello(必须有一个tab缩进)
3.Makefile重要内容
命名:
1.makefile
2.Makefile
备注:如果是其他名字,则不能使用make命令
一个规则:
目标:依赖条件
(一个tab缩进)命令
1.目标的时间必须晚于依赖条件的时间,否则,更新目录
2.依赖条件如果不存在,找寻新的规则去产生依赖
默认第一个是终极目标,可以写入“ALL:文件名”,规定生成的最终文件
基本规则:1.若想生成目标,检查规则中的依赖条件是否存在,如不存在,则需要寻找是否有规则用来是生成该依赖文件
2.检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任意一个被更新,则目标必须更新
两个函数:
wildcard函数
src=$(wildcard*.c) :匹配当前工作目录下所有.c文件,将文件名赋值给列表,赋值给变量src
patsubst把src变量里所有后缀为.c的文件替换成.o
obj=$(patsubst %.c,%.o,$(src))将参数3中,包含参数1的部分,替换成参数2
3个自动变量:
$@ 表示规则中的目标,在规则的命令中(在依赖条件中不行),表示规则中目标
$< 表示规则中的第一条件(再规则的命令中)
$^ 表示规则中的所有条件,组成一个列表(再规则的命令中)如果将该变量应用在模式规则中,它可将依赖条件列表中依赖一次取出,套用模式规则
4.Makefile下另一种写法
hello:hello.o
gcc hello.o -o hello
hello.o:hello.c
gcc -c hello.c -o hello.o
5.终端输入命令“make”
得到编译后的文件
6.多个文件生成可执行文件用makefile
a.out:hello.c add.c sub.c div1.c -o a.out
gcc hello.c add.c sub.c div.c -o a.out
7.
a.out:hello.o add.o sub.o div1.c -o a.out
gcc hello.o add.o sub.o div.o -o a.out
hello.o:hello.c
gcc -c hello.c -o hello.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
div.o:div.c
gcc -c div.c -o div.o
8.makefile可以写入
clean: (clean没有依赖条件)
-rm -rf 文件名
‘-’作用是删除不存在文件时,不报错
终端输入
make clean -n
模拟执行
make clean
执行
9.“#”是makefile里面的注释符号
10.模式规则
比如都是.o文件依赖于.c文件
可以写成:
%.o:%.c
gcc -c $< -o $@
系统编程阶段说在前面的话
1.系统调用:
内核是当前操作系统的核心程序,服务于上层应用
Linux2.6有270多个函数
库函数数量很大
操作系统可以粗略看成内核
2.Linux原码没有open函数
3.浅封装
4.内核主要包括驱动程序,每一个驱动程序对应一个硬件设备,比如显卡,声卡,网卡
5.一个普通用户不能进内核,只有用系统调用才能进内核
比如printf函数通过write函数传给sys_write,进入内核,找到驱动
open函数
1.open/close函数
2.open函数有两种,一个有两个参数,一个有三个参数
3.const表示只读
pathname路径名
4.open函数会返回一个整数,打开成功返回-1
5.mode_t八进制的整型
6.fiag取值
O_RDONLY O_WRONLY O_RDWR
O_APPEND(追加) O_CREAT(创建) O_EXCEL(是否存在) O_TRUNC(截断) O_NONBLOCK(非阻塞)
7.#include<unistd.h>包含open
#include<fcntl.h>包含O_RDONLY
8.示例
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
• int fd;
• fd=open("./dict.cp",O_RDONLY | O_CREAT,0644);
• printf("fd=%dn",fd);
• close(fd);
• return 0;
}
9.O_TRUNC
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
int fd;
fd=open("./dict.cp",O_RDONLY | O_CREAT,0644 | O_TRUNC,0644);
printf("fd=%dn",fd);
close(fd);
return 0;
}
文件若存在,截断成0,文件不存在,创建文件
10.创建文件时,指定文件访问权限,权限同时受umask影响
文件权限=mode&~umask
11.open常见错误
错误一:打开文件不存在:
#include<unisted.h>
• #include<fcntl.h>
• #include<stdio.h>
#include<errno.h>
#include<string.h>
• int main()
• {
• int fd;
• fd=open("./dict.cp4",O_RDONLY);
• printf("fd=%d,errno=%d:%sn",fd,errno,strerror(errno));
• close(fd);
• return 0;
• }
输出:fd=-1,errno=2:No such file or dictionnary
errno是内核变量,不需要定义
strerror翻译errno
错误二:以写方式打开只读文件
Permission denied 权限被拒绝
错误三:以只写方式打开目录
fd=-1,errno=21:Is a directory
read和write实现cp
1.read成功返回读到的字节数,失败返回-1,并设置相应的errno 的值
2.参数
fd:文件描述符
buf:存数据的缓冲区
count:缓冲区的大小
3.write
参数:
fd:文件描述符
buf:待写出数据的缓冲区
count:数据大小
返回值:返回写入的字节数
0表示没有什么可写的
失败返回-1,设置errno
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
• char buf[1024];
• int fd1=open(argv[1],O_RDONLY);
if(fd1==-1)
{
perror("open argv1 error");
exit(1)
}
• int fd2=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);
if(fd2==-1)
{
perror("open argv2 error");
exit(1)
}
• while((n=read(fd1,buf,1024))!=0)
• {
if(n<0)
{
perror("read error");
exit(1);
}
• write(fd2,buf,n);
• }
• close(fd1);
• close(fd2);
• return 0;
}
4.void perror(const char*s)
perror("open error");
系统调用和库函数比较
1.write是系统调用函数,putc是库函数
2.fgetc比read,write快一点
3.内核有系统级缓冲,默认大小有4096,即4kb大小,会一次性往磁盘写4kb
4.从用户区到内核比较耗时,比磁盘访问时间短,从用户区到内核是电子的操作,从内核到磁盘是物理操作
5.read,write函数会在用户与内核交换时浪费时间
6.fputc函数有一个缓冲,在这个空间缓冲了4kb数据才会进入内核,加快了4096倍速度
7.read,write被称为无缓冲IO(Unbuffered I/O),但不保证不使用内核缓冲
8.strace可以查看一个命令的系统调用,例如:strace ./read_cmp_getc
9.预读入,缓输出
10.系统函数不比库函数高级,系统函数效率不一定比库函数高
一般优先使用库函数
11.系统调用和库函数各有各的使用场景
文件描述符
1.PCB,进程控制块(进程描述符)process control block
本质是一个结构体,其中有一个指针指向是文件描述符表
2.文件描述符是本质可以理解为指针(实际上不是指针,是键值对映射),
3.指针指向
struct file
{
文件信息
}
4.访问打开的文件,只需要有文件描述符
5.0对应STDIN_FILENO,标准输入
1对应STDOUT_FILENO,标准输出
2对应STDERR_FILENO,标准错误
一个进程最多打开1024个文件,最大下标是1023,修改需要改内核
6.新打开文件一定是可用文件描述符中最小的那个
7.FILE结构体包括:文件的偏移量,文件的访问权限,文件的打开标志,文件内核缓冲区的首地址
阻塞与非阻塞
1.读常规文件不会阻塞,不管有多少字节
当读设备文件和读网络文件时会阻塞
2./dev/tty对应终端文件
3.阻塞是文件的属性
4.改变终端阻塞的属性要加O_NOBLOCK
5.fd=-1:并且erron=EAGAIN或EWOULDBLOCK,说明不是read失败,而是read在读一个设备文件(网络文件),并且文件无数据
6.解决非阻塞需要设置超时
fcntl的属性
1.改变一个文件的访问控制属性
2.一个参数是文件描述符fd,一个参数是命令cmd,命令的类型个数决定了后续的参数个数
3.获取文件状态 F_GETFL
设置文件状态 F_SETFL
4.位图
以二进制位画图
5.int flags=fctl(0,F_GETFL)
flgs|=O_NONBLOCK
fcntl(0,F_SETFL,flgs)
lseek函数
1.fd文件描述符
offset偏移量
whence起始偏移位置:SEEK_SET/SEET_CUR/SEEK_END
2.off_t seek(int fd,off_t offset,int whence)
3.返回值是
成功:较起始位置偏移量
失败:-1 errno
4.文件的读写是同一个偏移位置
5.
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
int main(){
• int fd,n;
• char msg[]="It's a test for lseekn";
• char ch;
• fd=open("lseek.txt",O_RDWR|O_CREAT,0644);
• if(fd<0){
• perror("open lseek.txt error");
• exit(1)
}
write(fd,msg,strlen(msg));
lseek(fd,0,SEEK_SET);
while(n=read(fd,&ch,1)){
• if(n<0){
• perror("read error");
• exit(1);
return 0;
}
}
}
6.应用场景: 文件的读写使用同一偏移位置
使用lseek获取,拓展文件大小(要想使文件大小真正拓展,必须引起IO操作)
7.
获取文件大小:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
• open(argv[1],O_RDWR);
• if(fd==-1)
• {
• perror("open error");
• exit(1);
• }
int length=lseek(fd,0,SEEK_END);
• printf("file size:%dn",length);
close(fd);
return 0;
}
拓展文件大小:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
open(argv[1],O_RDWR);
if(fd==-1)
{
perror("open error");
exit(1);
}
int length=lseek(fd,111,SEEK_END);
printf("file size:%dn",length);
write(fd,"a",1);
close(fd);
return 0;
}
8.^@称为文件空洞行
9.od -tcx filename 查看文件的16进制表示形式
od -tcd filename 查看文件的10进制表示形式
10.turncate拓展文件
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
• int ret=truncate("dict.cp",250);
• printf("ret=%dn",ret);
• return 0;
}
传入传出参数
传入参数:
1.指针作为函数参数
2.通常有const关键字修饰
3.指针指向有效区域,在函数内部做读操作
传出参数:
1.指针作为函数参数
2.在函数调入之前,指针指向的空间可以无意义,但必须有效
3.在函数内部,做写操作
4.函数调用结束后,充当函数返回值
传入传出参数:
1.指针作为函数参数
2.在函数调用之前,指针指向的空间有实际意义
3.在函数内部,先做读操作,后做写操作
4.函数调用结束后,充当函数的返回值
目录项和inode
inode
本质是一个结构体,存储文件的属性信息,权限,类型,大小,时间,用户,盘块位置
dentry(目录项)
存储文件名,inode
文件由inode和dentry组成
硬链接只有dentry不同
磁盘空间没有擦除,只有覆盖
stat函数
1.获取文件属性(从inode结构体中获取)
2.int stat(const char *path,struct stat *buf)
参数:
path:文件路径
buf:传出参数:存放文件属性
返回值:成功:0
失败:-1 errno
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
struct stat sbuf
• int ret=stat(argv[1],&sbuf)
• if(ret==-1)
{
• perror("stat error");
• exit(1);
}
printf("file size:%dn",sbuf.st_size);
return 0;
}
3.宏函数返回值是真假
Istat和stat
1.
需要头文件#include<sys/stat.h>
if(S_ISREG(sb.st_mode))
{
printf("It's a regularn");
}
2.mkfifo
创建管道
3.stat穿透,默认可以穿透符号链接
不想穿透要换函数,lstat不会穿透
4.cat可以穿透符号链接,vim不可以
5.获取文件大小:buf.st_size
获取文件类型:buf.st_mode
6.文件权限是16位(2字节)后九个是rwxrwxrwx,对应u,g,o
5~7是特殊权限位
最前面四位是文件类型
前4位是掩码
获取前四位信息
switch(sb.st_mode&S_IFMT) { • case S_IFBLK: printf("block devicen") }
传出参数与返回值
1.传出参数有返回值的功能,但是没有替代返回值
2.传出参数也可以向函数返回值,传出参数可以充当函数返回值
link和unlink隐式回收
1.link是实现硬链接的函数
2.LInux下删除文件的机制:不断将st nlink-1,直到减到0为止,没有目录对应的文件,将会被操作系统择机释放
3.我们删除文件,从某种意义上说,只是让文件具备了被释放的条件
4.unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放掉,要等所有打开文件的进程关闭该文件,才会择机释放
文件目录rwx权限差异
1.readlink读符号链接
2.getcwd 获取进程当前工作目录
3.vi打开目录,目录的内容叫目录项
4.文件可读是文件内容可以看,可以用cat,more,less
目录可读是目录可以浏览,可以执行ls,tree
文件可以写,可以修改文件内容
目录的写权限是可以添加删除目录项,执行mv,touch,mkdir
文件可执行是可以运行产生一个进程,“./文件名”
目录的执行权限是可以被进入,没有执行权限cd不进去
目录操作函数
opendir
根据传入的目录名打开一个目录(库函数)
DIR*opendir(const char *name);
成功返回指向该目录的结构体指针,失败返回NULL
参数支持相对路径,绝对路径两种方式:例如:打开当前目录
1.getcwd(),opendir()
2.opendir(".")
closedir
关闭打开的目录
int closedir(DIR *dirp);
成功:0
失败:-1 设置errno
readdir
读取目录(库函数)
‘ struct dirent *readdir(DIR *dirp);
成功返回目录项结构体指针
失败返回NULL设置errno为相应值
readwinder
回卷目录读写位置至起始
void rewinder(DIR *dirp)
返回值:无
telldir/seekdir
获取目录读写位置
long telldir(DIR *dirp);成功:与dirp相关目录当前读写位置。
失败-1,设置errno
修改目录读写位置
void seekdir(DIR *dirp,long loc)
返回值:无
参数loc一般由telldir函数的返回值来决定
递归遍历目录思路分析
ls-R.c
1.判断命令行参数,获取用户查询的目录名。argv[1]
argc==1-->./
2.判断用户指定的是否是目录 stat S_ISDIR();
3.读目录:
opendir(dir)
while(readdir()){
普通文件:直接打印
目录:拼接目录访问绝对路径 dir/d_name(sprintf(path,“%s/%s”,dir,dir_name))
递归调用自己。-->opendir(path) readir closedir
}
closedir()
递归遍历目录代码预览
1.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<sys/stat.h>
#include<dirent.h>
void isFile(char *name)
//打开目录,读取目录,处理目录
void read_dir(char *dir,void(*func)(char *))
{
• char path[256];
• DIR *dp;
• struct dirent *sdp;
• dp=opendir(dir);
• if(dp==NULL){
• perror("opendir error");
• return;
• }
• //读取目录项
• while(sdp=readdir(dp)!=NULL){
• if(strcmp(sdp->d_name,".")==0||strcmp(sdp->d_name,"..")==0){
• continue;
• }
fprintf(dp);
• //目录项目本身不可访问,拼接目录/目录项
• sprintf(path,"%s/%s,dir,sdp->d_name)
• //判断文件类型,目录递归进入,文件显示名字/大小
• isFile(path);
(*func)(path);
• }
• closedir(dp);
• return;
}
void isFile(char *name)
{
• int ret=0;
• struct stat sd;
• //获取文件属性,判断文件类型
• ret=stat(name,&sb);
• if(ret==-1){
• perror("stat error");
• return;
• }
• //是目录文件
• if(S_ISDIR(sb.st_mode)){
• read_dir(name);
• }
• //是普通文件,显示名字/大小
• printf("%st%dn",name,sb.st_size);
• return;
}
int main(int argc,char *argv[])
{
//判断命令行参数
if(argc==1){
isFile(".");
}
else{
isFile(argv[1]);
}
}
2.stat函数原型
int stat(const char *path,struct stat *buf)
dup和dup2
1.dup英文为重复
2.cat myls.c > out
3.dup
int dup(int oldfd);
oldfd:已有文件描述符
返回:新文件描述符
头文件:#include<unistd.h>
4.
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
fd=open(argv[1],O_RDONLY);
int newfd=dup(fd);
printf("newfd=%d",newfd);
return 0;
}
5.dup2(dupto)
int dup2(int oldfd,int newfd);
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main(int argc,char *argv[])
{
fd1=open(argv[1],O_RDONLY);
fd2=open(argv[2],O_RDONLY);
int fdret=dup2(fd1,fd2);//返回文件描述符fd2
printf("fdret=%dn",fdret);
int ret=write(fd2,"1234567"); //写入fd1指向的文件
printf("ret=%dn",ret);
dup2(fd1,STDOUT_FILENO); //将屏幕输入,重定向给fd1所指向的文件
printf("ghjkjhghjikjhgn");
return 0;
}
6.cat hello.c > out
将应该输出在终端的数据写入文件
7.dup2可以理解为后指向前,newfd指向oldfd
fcntl实现dup描述符
1.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>
int main(int argc,int *argv[])
{
• int fd1=open(argv[1],O_RDWR);
• printf("fd1=%dn",fd1);
• int newfd=fcntl(fd1,F_DUPFD,0);
• printf("newfd=%dn",newfd);
}
进程和程序以及CPU有关
1.进程是运行起来的程序,进程要占用系统资源
进程要占用内存,执行计算指令要占用CPU,总线
2.程序是死的,程序占磁盘空间
3.一个可执行文件可以在多个终端中运行
4.程序能看到,进程看不到
5.并发:在操作系统中,一个时间段中有多个进程都处于已启动到运行完毕的状态
6.单道程序设计:所有进程一个一个排队,若A阻塞,B只能等待,即使CPU处于空闲状态。在人机交互时阻塞的出现是必然的。所有这种模型在系统资源利用上都及其不合理,存在不就就淘汰了。
比如微软的道斯系统
7.多道程序设计:在计算机内存中存放几道相互独立的程序,它们在管理程序的控制下,相互穿插的运行,多道程序设计需要硬件作为保证。
8.单核CPU也能并发,A进程完成一点,未全部完成,B开始运行,B没有完成,C开始运行
通过时钟中断来执行
9.存储介质
-
寄存器
-
cache(缓存)
-
内存(电信号)
-
硬盘(物理方法读取)
-
网络(几乎可以认为存储是无线的)
从上到下存储量越来越大,速度越来越慢
一个寄存器是4096字节,4kb
10.CPU和MMU
CPU由ALU算数逻辑单元(只会算加减),寄存器堆,译码器,预取器组成
MMU是虚拟内存映射单元,在CPU内部
虚拟内存和物理内存映射关系
1.程序不占用内存,进程占用内存
2.kernel有pcb进程控制块,有一些成员变量,进程描述符
3.
虚拟地址(真正的物理地址在内存条上)./a.out
4G(可用) |
---|
kernel(内核数据) |
环境变量,main参数 |
stack(栈) |
.so加载位置 |
heap(堆) |
.bss |
地址8000.data(int a=10;) |
.rodata(只读数据段) |
.text(代码段) |
./b.out
kernel(内核数据) |
---|
环境变量,main参数 |
stack(栈) |
.so加载位置 |
heap(堆) |
.bss地址 |
8000.data(int a=10;) |
.rodata(只读数据段) |
.text(代码段) |
想把虚拟地址放在内存条上就需要MMU
如果两个进程虚拟内存地址一样,分块映射
4.一个page是4kb,MMU是4kb,一个寄存器是4kb
5.当申请的内存过大,需要连续空间时,MMU需要映射多个内存条区域
6.操作系统只有一个,所有进程的kernel共享,所以所有进程位于kernel的pcb都映射到内存条的一块区域内
7.MMU可以修改访问级别,CPU会将内存分级(Windows分4级,Linux分2级),MMU可以进行权级切换
pcb进程控制块
1.一个寄存器只有4Byte(32bit)
2.进程控制块:每个进程在内核中都有一个进程控制块来维护进程相关信息,Linux内核的进程控制块是task struct结构体
/user/src/linux-headers-3.16.0-30/include/linux/sched.h文件可以查看struct task_struct结构体的定义
3.需要掌握
进程id:每个进程有唯一的id
进程的状态:初始,就绪(等待CPU分配时间片),运行,挂起(等待除CPU以外的其他资源主动放弃CPU),停止
进程切换时需要保存和恢复的一些CPU寄存器(CPU正在计算,时间片结束了,需要将这个进程的寄存器数据存起来,再次运行这个进程时将值放回寄存器)
描述虚拟地址空间的信息
描述控制终端的信息
当前工作目录位置
umask掩码(不同进程有不同的umask掩码)
文件描述符表,包含很多指向file结构体的指针
和信号相关的信息
用户id和主id
会话Session和进程组
进程可以使用的资源上限
ps aux查询进程,第二列PID就是进程
环境变量
LD_LIBRARY_PATH动态连接器
PATH记录可执行文件目录位置
echo $PATH可以将这个环境变量打印
SHELL解析命令
TERM当前终端类型
LANG语言和locale,决定字符编码以及时间,货币等信息的显示格式
HOME当前用户的宿主目录
Fork函数原理
1.创建一个子进程
返回值为整数没有参数
2.父进程有的子进程都有,子进程执行fork();下面的
成功子进程会给父进程返回一个0,父进程返回子进程的id
Fork创建子进程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
• printf("before fork--1-------n");
• printf("before fork--2-------n");
• printf("before fork--3-------n");
• printf("before fork--4-------n");
• printf("before fork--5-------n");
• pid_t pid=fork();
• if(pid==-1)
• {
• perror("fork error");
• exit(1);
• }
• else if(pid==0)
• {
• printf("-------child is createdn");
• }
• else if(pid>0)
• {
• printf("-------parent process:my chid is%dn",pid);
• }
• printf("===========end of filen");
• return 0;
}
before fork 1,2,3,4执行一次
===========end of file执行两次,因为fork之后的子进程和父进程都会执行
getpid和getppid
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
printf("before fork--1-------n");
printf("before fork--2-------n");
printf("before fork--3-------n");
printf("before fork--4-------n");
printf("before fork--5-------n");
pid_t pid=fork();
if(pid==-1)
{
perror("fork error");
exit(1);
}
else if(pid==0)
{
printf("-------child is created,pid=%dn,parent_pid=%d",getpid(),getppid());
}
else if(pid>0)
{
printf("-------parent process:my chid is%d,mypid=%d,myparent_pid=%dn",pid,getpid(),getppid());
}
printf("===========end of filen");
return 0;
}
ps aux | grep 7876
父进程的父进程是bash
循环创建N个子进程
1.一个fork函数调用可以创建一个子进程,那么创建N个子进程应该如何实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
int i;
for(i=0;i<5;i++){
pid_t pid=fork(); //每次所有父进程,子进程,子进程产生的子进程都会产生一个子进程
if(pid==-1)
{
perror("fork error");
exit(1);
}
else if(pid==0)
{
printf("-------child is created,pid=%dn,parent_pid=%d",getpid(),getppid());
}
else if(pid>0)
{
printf("-------parent process:my chid is%d,mypid=%d,myparent_pid=%dn",pid,getpid(),getppid());
}
}
return 0;
}
2.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
int i;
for(i=0;i<5;i++){
if(fork()==0)
break;
}
if(i==5)
printf("I'm parent");
else
printf("I'm %dth child",i+1);
return 0;
}
每个进程会争取cpu,导致顺序不一致
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
int i;
for(i=0;i<5;i++){
if(fork()==0)
break;
}
if(i==5)
{
sleep(5);
printf("I'm parent");
}
else{
sleep(i);
printf("I'm %dth child",i+1);
}
return 0;
}
父子进程共享哪些内容
1.父子进程之间在fork之后
相同处:全局变量,data,text,栈,堆,环境变量,用户ID,宿主目录,进程工作目录,信号处理方式
不同处:进程Id,fork返回值,父进程id,进程运行时间,闹钟(定时器),未决信号集
似乎,子进程复制了父进程0~3G用户内容,以及父进程的PCB,但是pid不同,实际不是,父子进程间遵循读时共享写时复制的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销
2.躲避父子进程共享全局变量的知识误区
3.共享:1.文件描述符(打开文件的结构体) 2.mmap建立的映射区
父子进程gdb调试
1.使用gdb调试的时候,gdb只能跟踪一个进程。可以在fork函数调用之前,通过指令设置gdb调试工具跟踪父进程或者是跟踪子进程。默 认跟踪父进程
2.set follow-fork-mode child命令设置gdb在fork之后跟踪子进程
3.set follow-fork-mode parent跟踪父进程,默认父进程
exec函数族原理
1.fork创建子进程后执行的是和父进程相同的程序(但是可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
2.进程还是父进程的子进程,但是执行的内容不一样了,exec调用的部分不会返回给父进程,进程id不变
-execlp和execl函数
execlp函数
加载一个进程,借助PATH环境变量
该函数通常调用系统程序。如:ls,date,cp,cat等命令
int execlp(const char *file,const char *arg,...);
execlp("ls","-l","-d)
失败返回-1
int execl(const *path,const char *arg,...);
指定路径
execl("/bin/ls","ls","-l","-F",NULL);
exec函数族的特性
1.execvp函数
加载一个进程
int execvp(const char *file,char *const argv[]);
char *argv[]={"ls","-l","-h",NULL};
execvp("ls",argv);
2.exec函数一旦调用成功即执行新的程序,不返回,只有失败才会返回,错误值-1。所以通常直接在exec函数调用后直接调用perror()和exit(),无需if判断
孤儿进程和僵尸进程
孤儿进程
父进程先于进程结束,则子进程称为孤儿进程。子进程的父进程成为init进程,称为init进程领养孤儿进程。
orphan.c
ps aux
僵尸进程
进程终止,父进程尚未回收,子进程残留资源(PCB)存放在内核,变成僵尸进程。
PCB标记子进程死亡原因
[zoom]<defunct>
wait回收子进程
1.一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但是它的PCB还保留着,内核在其中保存了一些信息,如果是正常终止则保存退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。一个进程的退出状态可以在shell中用特殊变量$查看因为shell是他的父进程,当它终止时Shell调用wait或waitpid得到它退出的状态同时彻底清除这个进程
2.父进程调用wait函数可以回收子进程的终止信息,该函数有三个功能: 1.阻塞等待子进程退出
2.回收子进程残留资源
3.获取子进程结束状态(退出原因)
3.pid_t wait(int *status);
pid_t waitpid(pid_t pid,int *status,int *options);
成功返回子进程的进程id,失败-1
获取子进程退出值和异常终止信号
1.使用宏函数
1.if(WIFEXITED(status))
为真说明子进程正常终止
WEXITSTATUS(status);
子进程返回值
2.WIFSIGNAL(status)
为真,异常终止
WTERMSIG(status)
取得使进程终止的那个信号的编号
2.所有进程终止都是信号
Linux基础命令学习资源
【麻省理工学院】MIT 计算机操作环境导论 Missing Semester (超强的计算机工具课)-_哔哩哔哩_bilibili
黑马程序员新版Linux零基础快速入门到精通,全涵盖linux系统知识、常用软件环境部署、Shell脚本、云平台实践、大数据集群项目实战等_哔哩哔哩_bilibili
最后
以上就是疯狂墨镜为你收集整理的Linux基础和系统编程pcb进程控制块的全部内容,希望文章能够帮你解决Linux基础和系统编程pcb进程控制块所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复