目录
一、Linux介绍
Linux: 操作系统 和 Windows 操作系统区别:

Linux终端
右击,打开终端选项。
快捷键:ctrl shift + 三键组合 -> 放大字体
ctrl - 二键组合 -> 缩小字体
$:普通用户身份
#:管理员身份
这里进入管理员身份使用的命令为sudo su。
二、Linux系统目录结构
Linux系统的目录结构是一颗倒状数。
三、基础命令
tab键:补全文件信息
1.ls命令 查看路径下所有文件


2.cd命令:切换路径

3.clear 命令
清屏命令
4.pwd 命令
查看当前路径
5.mkdir 命令 创建目录文件
6.rmdir 命令 删除目录文件(空目录)
7.rm 删除文件命令
rm -r 强制删除文件(目录是否是空都可以)
8.touch 命令 创建普通文件

*模糊匹配:
9.文件类型
u:表示文件属主的访问权限
g:表示文件同组用户的访问权限
o:表示其他用户的访问权限
r:可读 值:4
w:可写 值:2
x:可执行 值:1
- :无权限 值:0
10.修改文件权限 chmod
1.数字设定法
采用数字设定法时,权限通常由三位数字组成,,每一位数字代表一种角色的权限。每个角色的数值由其所具有的权限对应的数值之和构成。 r:4.w:2 x:1
2.文字设定法
u表属主g同组人o其他人a所有人“+”表示增加权限“”表示去掉权限
11.文件编辑命令
三种模式介绍
命令模式、插入模式(编辑模式)、末行模式
a:进入到当前光标后开始编辑
A:进入到当前光标所在行的行末开始编辑i1/进入当前光标位置开始编辑
l:进入当前光标所在行的行头开始编辑o/进入当前光标下一行开始编辑
o:进入当前光标上一行开始编辑
s:删除当前光标位置数据,在光标位置插入
在命令模式中,"/"表示文件内容从上向下查找数据,"?"默认从文件的下向上查找。
12.man 命令 查看帮助手册
man 数字 内容
1 -> 命令
2 -> 系统调用函数
3 -> 库函数
如下:
13. cp 命令 拷贝
拷贝main.c到当前路径
拷贝main.c到上一级目录下,并重命名为newmain.c
14.mv 命令
1.重命名 相同路径
mv 文件 文件 相同路径
mv 路径1/文件 路径1/文件
将main.c重命名为aa.c。
2.文件移动(剪切)
mv 路径+文件 路径 解析为移动(剪切)
将当前路径下的main.c移动到上一级目录
15.wc 命令
统计单词个数
16.more 命令 less 命令
more 一页一页查看文件
less 文本内容查看器,查看文件内容,但是文件内容不会显示到界面上
17.head 命令
展示前n行文件内容
18.tail命令
展示后n行文件内容
19.cat 命令
1.重定向
2.追加
ctrl + d 结束输入
3.合并文件
4.cat和tail合用
终端1 cat >> main.c 重定向
终端2 tail -f main.c 实时追踪文件内容信息
tail -f 一般用于跟踪日志文件,实施编写展示。
三、vim使用
vim命令介绍
命令模式
ndd :从光标位置开始删除连续的n行内容
u :撤销上次操作
nyy :拷贝连续的n行内容(复制)
p:粘贴
r :替换字符按键r然后字符
G:光标移动到文件的末尾
gg ∶光标移动到文件开头
shitf+4:光标跳转到当前行尾
shift+6:光标跳转到当前行的行头
末行模式
:wq保存退出 :w只保存 :q只退出不保存 :q!强制退出
/字符串从光标位置开始向下循环一圈查找该字符串的位置
?字符串从光标位置开始向上循环一圈查找该字符串的位置
n,m s/字符串1/字符串2/g
从n~m行之间将字符串1替换为字符串2
: set nu设置行号: set nonu取消行号
四、Linux系统上C程序的编译与调试
1.程序的编译链接过程
什么是可执行文件?
在Windows操作系统中,扩展名为.exe,.bat等的文件是可执行文件,可执行文件由指令和数据构成。Linux是靠文件属性来判断是否可执行。文件权限 x为可执行权限。
2.编译链接过程
3. gcc分布编译链接
1.预编译:gcc -E main.c -o main.i
2.编译:gcc -E main.i -o main.s
3.汇编:gcc -E main.s -o main.o
4.链接:gcc -E main.o -o main
一步编译:gcc -o main main.c
执行可执行程序:
路径+文件名 路径(相对 绝对均可)
./main /home/stu/main
打开z.i。
打开z.s。
五、Linux平台C程序的编译链接
1.自定义头文件使用
vim main.c
vim add.h
在一步编译时我们会遇到一个问题
stdio.h 来源于 /usr/include 而add.h是用户自定义.h文件,故找不到该文件或目录。解决的方法可以将其移动到include中。
完成操作。
在main.c中, #include<add.h> 需要把当前路径下/usr/include/ 可以使用<add.h>。
2.gdb调试
vim test.c
编译完成之后,进行运行
这里我们发现,输入“end”之后程序仍然不会停止,这里使用ctrl+c中止程序。那么问题出现在哪?
我们进行调试
第一步,通过gcc -o main main.c -g
第二步,启动调试:gdb
第三步,(gcb)提示符出现,可以输入命令
输入“l”查看代码
l 行号 查看当前行号前后10行代码内容
b 行号 在行号位置下断点
info break 或者 info b
第四步,运行代码(与逆行代码道断点位置阻塞)
run/r 运行代码
n/next 下一步
p 变量 实时查看当前变量的信息
当输入数据为“end”时,我们可以看到,实际上获取的时“end\n”,而"end\n"不等于“end”,所以即使输入“end”也不会中止。
第五步:调试结束 quit:结束调试
六、makefile文件和进程管理命令
一、makefile文件
我们需要写一个makefile文件,去实现自动化编译,而文件名称必须是makefile,是通过touch创建的普通文件。
1.前提是已经安装好make命令(make命令只针对makefile文件。make执行,默认直接调用makefile文件)
2.编辑C源文件 add.c mul.c main.c
3.编辑makefile文件
4.终端:执行make命令
5.终端执行:make clean命令
二、文件压缩与解压命令
1.find命令
find + 路径 -name 文件名
2.grep命令
grep "int" main.c 过滤出main.c中包含"int"字符的所有行进行输出
一般 | 管道和grep搭配使用
3.关机重启
showdown now 立刻关机
showdown -r now 重启
4.tar 文件压缩与解压
以main.c add.c mul.c 为例
压缩分步:先将三个文件打包,再压缩
解压分步:将压缩包解压再解包
c:创建包文件
f:指定目标为文件而不是设备
v:显示详细过程
t:显示包中的内容而不释放
x:释放包中的内容
z:使得tar有压缩和解压的功能
打包操作:tar cvf file.tar main.c add.c mul.c
将三个文件打包为file.tar
压缩:gzip file.tar 生成file.tar.gz
分布解压:gzip -d file.tar.gz ->file.tar
解包:tar xvf file.tar
一步压缩和解压命令
一步压缩:tar zcvf file.tar.gz main.c add.c mul.c
一步解压:tar zxvf file.tar.gz.0
三、进程管理命令
1.ps
默认显示与当前终端有关的进程信息
-e 显示系统中所有的进程信息
-f 显示更多的进程属性信息(全格式)
-L 展示当前终端上进程信息,线程LWP信息
ps -ef 展示系统上所有进程的详细信息
UID:执行该进程的用户ID
PID:进程号,一个进程唯一的号码对应
PPID:Parent 父进程的进程号
C:CPU使用率
STIME 进程启动时间
TTY :终端是哪个
?:若进程运行,与终端无关显示?
pst/0:由网络连接主机
tty1~tty6:本机
TIME:运行的时间
CMD:进程启动时使用的命令
2.kill
kill 进程PID 结束当前进程
kill pid 结束当前进程
kill -9 pid 强制结束
kill -STOP pid 挂起进程
bg %任务号 进程/挂起程序,调到后台执行
fg % 任务号 将后台进程,调到前台执行
3.& 后台运行程序
启动程序:./main -> 路径+可执行程序 默认执行前台
./main & -> 后台运行进程
4. runlevel 查看系统运行级别
0 关机;1 单用户级别;2 多用户无网络级别;3 多用户文本界面;4 无定义、自定义界面; 5 图形化界面;6 重启。
七、静态库和动态库
库文件是计算机上的一类文件,提供给使用者一些开箱即用的变量、函数或类。库文件分为静态库和动态库,静态库和动态库的区别体现在程序的链接阶段。
一般来说, Windows的静态库文件扩展名是.lib,动态库文件扩展名.是:.dll(Dynamic-Link Libraries),: Linux的静态库扩展名是a;动态库扩展名是.so(Shared Object)。内容一样,都是将函数封装在一起编译后供自己或他人调用。好处在于编译后的库文件看不到源代码,可保密。
库是一组预先编译好的方法的集合。 Linux系统存储的库的位置—般在/lib 和/usr/lb在64位的系统上有些库也可能被存储在/usr/lib64下。库的头文件一般会被存储在/usr/include下或其子目录下。
Linux库有两种,一种是静态库,其命令规则为libxxx.a,一种是共享库,其命今规则为libxxx.so
1.静态库
1.add.c mul.c 编译 add.o mul.o gcc-c add.c mul.c
2.创建静态库 ar crv libfoo.a a.o mul.o
3.使用静态库
-L :指定路径
-l:指定库名称(除开头lib和.a结尾)
查看静态库大小
静态库链接特点:每一个程序,静态链接库文件,生成的可执行文件都有一份副本。
2. 动态库
1.add.c mul.c 编译 add.o mul.o
2. add.o mul.o生成共享库文件
3.共享库的使用
第一种使用方法:
动态库放/lib或者/usr/lib路径下
第二种使用方法:
修改环境变量
修改环境变量,使得动态库链接路径由原来的/usr/lib或/lib修改为自定义路径
命令:export LD_LIBRARY_PATH=.
删除环境变量 unset LD_LIBRARY_PATH
八、Linux基础理论
1.main主程序的三个参数
添加环境变量:export LD_LIBRARY_PATH=.
查看变量的值:echo $L_LIBRARY_PATH
清除环境变量: unset LD_LIBRARY_PATH
2.如何执行程序
用户->计算机硬件->shell->内核->fwrite()->内核函数write(系统调用函数)
shell是用户和linux内核交互的接口程序
shell是终端
在提示符输入命令,经过shell先命令的解释后传递内核
shell通过$PATH寻找可执行程序,若找到可执行程序,被分解为系统调用并传递给内核执行。
3.并发与并行
并行:在同一个时刻,能够同时执行多个进程,每核,CPU在每一时刻执行一个进程,所以要同时进行多个进程的运行,多核CPU。
并发:在某一时间段,需要处理多个任务(进程,单核CPU,在某一时刻只能处理一个任务,多个进程通过进程切换,进程执行。
串行处理:多个任务,单核CPU,一个进程全部处理完成接下来处理下一个进程,等待该进程处理完,再进行下一个进程。
4.printf隐藏的缓冲区
windows无缓存 linux有缓存
\n 行缓冲
刷新缓冲区:1.程序结束前;2.碰见\n;3.碰见fflush(stdout),刷新缓冲区;4.缓冲区存放满
return 关键字,当前功能的结束
exit 函数调用,进程的退出
_exit 内核级别函数:exit函数内部实现结束,调用_exit进程中止。只结束程序,不刷新缓冲区。
九、内存管理与进程复制
一、计算机基本组成
CPU包括控制器和运算器
运算器:也叫算数逻辑单元,完成对数据的各种常规运算,如加减乘除,也包括逻辑运算,移位,比较等。
控制器:它是整个计算机系统的控制中心,它只会计算机各部分协调的工作,保证计算机按照规定的目标和步骤有条不紊地进行操作及处理。
I/O设备就是输入设备和输出设备
系统总线:连接计算机各部件之间的一束公共信息线,它是计算机中传送信息代码的公共途径。
分为地址总线,数据总线,控制总线
计算机工作过程
用户打开程序
系统把程序代码段和数据段送入计算机的内存
控制器从存储器中取指令
控制器分析,执行指令,并为下一条指令做准备。
二、进程概述
1.进程:一个正在运行的程序。进程=程序+数据+PCB
2.PCB:进程控制块,是进程存在的唯一标志。用来描述进程的属性信息。OS是根据PCB来对并发执行的程序进行控制和管理的。
问:一个进程唯一有其标识PCB,一个程序能够跑起来,是现有PCB的产生还是先有进程的产生?
答:PCB
问:程序启动起来,程序终止时,先消失PCB还是进程实体?
答:进程实体。
什么是进程?
操作系统中进程的所有操作都是通过运行相应的程序来实现,当运行某个程序时,就要将其从硬盘调入内存中,以供CPU进行运算和处理,这些系统正在运行的程序就成为进程。
程序只占磁盘空间,不占用系统运行资源。进程由程序产生,进程要占用CPU和内存等系统资源,当关闭进程之后,他所占用的资源也随之释放。
进程是操作系统资源分配和调度的基本单位。Linux是一个多用户多任务的操作系统,多用户是指多个用户可以在同一时间使用同一个linux系统,多任务是指linux中可以同时运行多个程序,执行多个任务,所有的进程都需要CPU进行运算和处理,而CPU在同一时刻只能处理一个进程数据。
进程状态:就绪、运行、阻塞
CPU调度只会从就绪队列中取PCB(进程控制块)。
三、内存
虚拟内存提供的三个重要的能力:
1.它将主存堪称是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,根据需要在磁盘和主存之间来回传送数据,是的能够以运行比内存大的多的进程
2.它为每个进程提供了一致的地址空间,从而简化了存储器管理
3.它保护每个进程的地址空间不被其他进程破坏
物理地址和逻辑地址
物理地址:物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查(notranslation, no paging, no privilege checks)。
逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于当前进程数据段的地址(偏移地址),不和绝对物理地址相干。
分段和分页技术
1.页表
虚拟地址转换为物理地址,要有页表。
页表是一种特殊的数据结构,放在系统空间的页表区,存放逻辑页与物理页帧的对应关系。每一个进程都拥有一个自己的页表,PCB表中有指针指向页表。
分级页表:一个32位(2^32)逻辑地址空间的计算机系统,页大小为4KB(2^12),那么页表有一百万条目(2^(32-12))。假设每个条目占4B,则需要4MB物理地址空间来存储页表本身。利用多级页表,可以减少页表所占用的空间。
一个32位逻辑地址空间的计算机系统,页大小为4KB,那么页表有一百万条目。假设每个条目占4B,则需要4MB物理地址空间来存储页表本身。利用多级页表,可以减少页表所占用的空间。
逻辑页和物理页
十、Linux进程复制
fork:进程复制
#include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main(){
5 int n = 0;
6 char *s = NULL;
7 pid_t pid = fork(); //返回值: 当前父进程 返回>0 子进程 返回值 == 0
8 if(pid == 0){//子进程
9 n = 3;
10 s = "child";
11 }
12 else{
13 n = 7;
14 s = "parent"; //父进程
15 }
16 for(int i=0;i<n;i++){
17 printf("pid=%d,ppid=%d,s=%s,&n=%p\n",getpid(),getppid(),s,&n);//getpid() 获取当前进程的pid getppid()当前进程的父进程
18 sleep(1);
19 }
20 }
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2767,ppid=2766,s=child,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2767,ppid=2766,s=child,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2767,ppid=2766,s=child,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
pid=2766,ppid=2283,s=parent,&n=0x7fff6d436694
问:子进程通过fork进程复制父进程得到的,那么,子进程n与父进程n是不是同一块物理内存,逻辑内存?
如上代码,&n的值,不论是父进程还是子进程的值是一样的。物理地址不同。
写时拷贝
写时拷贝是一种可以推迟甚至免除拷贝数据的技术,内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。
问:不修改n的值,父子进程n的物理内存是否相同?
答:相同,此时父子进程共享n数据,也就是写时拷贝。
关于fork的一些问题:
1.下面代码,经过fork()之后,打印几个“A”?
#include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 printf("A\n");
7 fork();
8 }
答:1个A,fork()之后没有代码,子进程结束,父进程也结束。
2.
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 fork();
7 printf("A\n");
8 }
答:2个A,一个是父进程打印出的A,另一个是子进程打印的A。
3.
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 printf("A");
7 fork();
8 }
答:2个A,A会先进入缓冲区,直到程序结束,释放缓冲区中的数据,父子进程各一个A。
4.
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 for(int i=0;i<2;i++){
7 fork();
8 printf("A\n");
9 }
10 }
答:6个
5.
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 for(int i=0;i<2;i++){
7 fork();
8 printf("A");
9 }
10 }
答:8个,需要考虑缓冲区。
十一、僵尸进程和文件系统调用
#include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main(){
5 int n = 0;
6 char *s = NULL;
7 pid_t pid = fork(); //返回值: 当前父进程 返回>0 子进程 返回值 == 0
8 if(pid == 0){//子进程
9 n = 3;
10 s = "child";
11 }
12 else{
13 n = 7;
14 s = "parent"; //父进程
15 }
16 for(int i=0;i<n;i++){
17 printf("pid=%d,ppid=%d,s=%s,&n=%p\n",getpid(),getppid(),s,&n);//getpid() 获取当前进程的pid getppid()当前进程的父进程
18 sleep(1);
19 }
20 }
sen@sen-virtual-machine:~/test1012$ ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3161 pts/0 00:00:00 main
3162 pts/0 00:00:00 main
3164 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3161,ppid=2283,s=parent,&n=0x7ffda9884284
pid=3162,ppid=3161,s=child,&n=0x7ffda9884284
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3161 pts/0 00:00:00 main
3162 pts/0 00:00:00 main
3165 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3161,ppid=2283,s=parent,&n=0x7ffda9884284
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3161 pts/0 00:00:00 main
3162 pts/0 00:00:00 main <defunct>
3166 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ ppid=3161,ppid=2283,s=parent,&n=0x7ffda9884284
s
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3161 pts/0 00:00:00 main //父进程
3162 pts/0 00:00:00 main <defunct> //子进程(僵死)
3167 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pspid=3161,ppid=2283,s=parent,&n=0x7ffda9884284
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3161 pts/0 00:00:00 main
3162 pts/0 00:00:00 main <defunct>
3168 pts/0 00:00:00 ps
僵尸进程
子进程先于父进程结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。
子进程的退出,父进程未获取子进程的退出码。
解决僵尸问题:wait(),子进程运行结束,父进程把子进程的退出码获取之后就可以解决。
1 #include<stdio.h>
2 #include<sys/wait.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 int main(){
6 int n = 0;
7 char *s = NULL;
8 pid_t pid = fork(); //返回值: 当前父进程 返回>0 子进程 返回值 == 0
9 if(pid == 0){//子进程
10 n = 3;
11 s = "child";
12 }
13 else{
14 n = 7;
15 s = "parent"; //父进程
16 int val; // 4字节,获取子进程的退出码
17 pid_t child_pid = wait(&val);//子进程结束,wait阻塞,直到子进程运行结束,获取子进程的退出码之后,继续当前父进程。
18 if(WIFEXITED(val)){
19 printf("wait的返回值%d 退出码:%d\n",child_pid,WEXITSTATUS(val));
20 }
21 }
22 for(int i=0;i<n;i++){
23 printf("pid=%d,ppid=%d,s=%s,&n=%p\n",getpid(),getppid(),s,&n);//getpid() 获取当前进程的pid getppid()当前进程的父进程
24 sleep(1);
25 }
26 }
pid=3237,ppid=3236,s=child,&n=0x7ffd9edf8f8c
pid=3237,ppid=3236,s=child,&n=0x7ffd9edf8f8c
pid=3237,ppid=3236,s=child,&n=0x7ffd9edf8f8c
wait的返回值3237 退出码:0
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
pid=3236,ppid=2283,s=parent,&n=0x7ffd9edf8f8c
将父进程与子进程的n调换
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3185 pts/0 00:00:00 main
3186 pts/0 00:00:00 main
3187 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3186,ppid=3185,s=child,&n=0x7fff3cffdd34
pid=3185,ppid=2283,s=parent,&n=0x7fff3cffdd34
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3185 pts/0 00:00:00 main
3186 pts/0 00:00:00 main
3188 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3185,ppid=2283,s=parent,&n=0x7fff3cffdd34
pid=3186,ppid=3185,s=child,&n=0x7fff3cffdd34
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3185 pts/0 00:00:00 main
3186 pts/0 00:00:00 main
3189 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3186,ppid=3185,s=child,&n=0x7fff3cffdd34
pspid=3186,ppid=1660,s=child,&n=0x7fff3cffdd34
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3186 pts/0 00:00:00 main
3190 pts/0 00:00:00 ps
[1]+ 已完成 ./main
sen@sen-virtual-machine:~/test1012$ pid=3186,ppid=1660,s=child,&n=0x7fff3cffdd34
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3186 pts/0 00:00:00 main
3191 pts/0 00:00:00 ps
sen@sen-virtual-machine:~/test1012$ pid=3186,ppid=1660,s=child,&n=0x7fff3cffdd34
ps
PID TTY TIME CMD
2283 pts/0 00:00:00 bash
3186 pts/0 00:00:00 main
3192 pts/0 00:00:00 ps
孤儿进程
父进程先于子进程结束,子进程的父进程由init进程接管子进程。
文件操作:fopen fread fwrite fclose
1 #include<fcntl.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 int main(){
6 int fd = open("./a.txt",O_WRONLY|O_CREAT,0600);//打开a.txt 以读写模式打开文件,不存在,则创建文件+权限,再以读写模式打开
7 //fd 文件描述符 fd 对应一个文件 文件关闭后,文件描述符归还操作系统 fd>=0
8 if(fd == -1){
9 printf("open_err\n");
10 exit(1);
11 }
12 close(fd);
13 //open("./a.txt",O_WRONLY); //不存在文件 不会创建 直接返回失败
14 exit(0);
15 }
十二、文件操作和进程替换
操作文件的系统调用
int open(const char* pathname, int flags);//用于打开一个已存在的文件
int open(const char* pathname,int flags,mode_t mode);//用于新建一个文件,并设置访问权限
参数介绍:
pathname:将要打开的文件路径和名称
flags:文件打开方式
O_RDONLY——只读方式打开
O_WRONLY——只写方式打开
O_RDWR——读写方式打开
O_CREAT——如果文件不存在则创建文件
O_APPEND——文件末尾追加
O_TRUNC——清空文件,重新写入
mode:权限,例如“0600”,创建文件的权限不仅受mode的影响,也受到用户掩码umask的掩码
返回值:文件描述符
ssize_t read(int fd, void *buf, size_t count);
参数介绍:
fd:对应的文件描述符
buf:从文件中读出数据到 buf 中
count:计划一次从文件中读多少字节数据
返回值:实际读到的字节数
ssize_t write(int fd, const void *buf, size_t count);
参数介绍:
fd:对应的文件描述符
buf:从 buf 中向文件写入数据
count:计划一次向文件中写多少字节数据
int close(int fd);
参数介绍:
fd:要关闭的文件描述符
可读:
1 #include<fcntl.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 int main(){
6 int fd = open("a.txt",O_RDONLY|O_CREAT,0600);//打开a.txt 以读写模式打开文件,不存在,则创建文件+权限,再以读写模式打开
7 //fd 文件描述符 fd 对应一个文件 文件关闭后,文件描述符归还操作系统 fd>=0
8 if(fd == -1){
9 exit(1);
10 }
11 char buff[128] = {0};
12 int num = read(fd,buff,128);
13 printf("num=%d,buff=%s\n",num,buff);
14 close(fd);
15 exit(0);
16 }
num=6,buff=abcde
可写:
1 #include<fcntl.h>
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 int main(){
6 int fd = open("a.txt",O_WRONLY|O_CREAT,0600);//打开a.txt 以读写模式打开文件,不存在,则创建文件+权限,再以读写模式打开
7 //fd 文件描述符 fd 对应一个文件 文件关闭后,文件描述符归还操作系统 fd>=0
8 if(fd == -1){
9 exit(1);
10 }
11 //char buff[128] = {0};
12 //int num = read(fd,buff,128);
13 //printf("num=%d,buff=%s\n",num,buff);
14 int num = write(fd,"hello",5);
15 printf("num=%d\n",num);
16 close(fd);
17 exit(0);
18 }
num=5
将a.txt拷贝给b.txt
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 #include<assert.h>
6 int main(){
7 //a.txt拷贝给b.txt
8 char* s_name = "a.txt";
9 char* new_name = "b.txt";
10 int fdr = open(s_name,O_RDONLY);
11 assert(fdr!=-1);
12 int fdw = open(new_name,O_WRONLY|O_CREAT,0600);
13 assert(fdw!=-1);
14 //依次读a.txt 写入b.txt
15 char buff[128] = {0};
16 int num = 0;
17 while((num=read(fdr,buff,128))>0){
18 write(fdw,buff,num);
19 }
20 close(fdr);
21 close(fdw);
22 exit(0);
23 }
文件描述符 + fork
先open再fork,共享偏移量
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 #include<assert.h>
6 int main(){
7 int fd = open("a.txt",O_RDONLY);
8 assert(fd!=-1);
9 pid_t pid = fork();
10 assert(pid!=-1);
11 if(pid == 0){
12 char buff[10] = {0};
13 read(fd,buff,1);
14 printf("son:buff=%s\n",buff);
15 sleep(1);
16 read(fd,buff,1);
17 printf("son:buff=%s\n",buff);
18
19 }
20 else{
21 char buff[10] = {0};
22 read(fd,buff,1);
23 printf("father:buff=%s\n",buff);
24 sleep(1);
25 read(fd,buff,1);
26 printf("father:buff=%s\n",buff);
27 }
28 close(fd);
29 exit(0);
30 }
运行结果:
father:buff=a
son:buff=b
father:buff=c
son:buff=d
先fork再open,不共享偏移量
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 #include<assert.h>
6 int main(){
7 pid_t pid = fork();
8 assert(pid!=-1);
9 int fd = open("a.txt",O_RDONLY);
10 assert(fd!=-1);
11 if(pid == 0){
12 char buff[10] = {0};
13 read(fd,buff,1);
14 printf("son:buff=%s\n",buff);
15 sleep(1);
16 read(fd,buff,1);
17 printf("son:buff=%s\n",buff);
18
19 }
20 else{
21 char buff[10] = {0};
22 read(fd,buff,1);
23 printf("father:buff=%s\n",buff);
24 sleep(1);
25 read(fd,buff,1);
26 printf("father:buff=%s\n",buff);
27 }
28 close(fd);
29 exit(0);
30 }
father:buff=a
son:buff=a
son:buff=b
father:buff=b
子进程先将父进程的PCB复制一份,分为两个进程,同时执行fork之后的程序,父子进程同样进行open