96SEO 2026-02-20 07:36 16
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ub0TQGxh-1684633973964)(D:\Typora笔记\c\Linux系统编程.assets\image-20221112123423275.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AoxNPfnp-1684633973965)(D:\Typora笔记\c\Linux系统编程.assets\image-20221112123457163.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BCOgGldQ-1684633973966)(D:\Typora笔记\c\Linux系统编程.assets\image-20221112123511760.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ji9Gs8X0-1684633973966)(D:\Typora笔记\c\Linux系统编程.assets\image-20221112132216369.png)]
blog:http://blog.51cto.com/zpf666全局配置关闭vi兼容模式
guifontCourier_New:h10:cANSI设置颜色
guibg#00ff00高亮显示普通txt文件需要txt.vim脚本
foldcloseall用空格键来代替zo和zc快捷键实现开关折叠
go在执行宏命令时不进行显示重绘在宏命令执行完成后一次性重绘以便提高性能
backspaceeol,start,indent允许空格键和光标键跨越行边界
fencsutf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936设置文件编码
completeoptlongest,preview,menu共享剪切板
在shell脚本开头自动增加解释器以及作者等版权信息新建.py,.cc,.sh,.java文件自动插入文件头
#Blog:http://blog.51cto.com/zpf666)call
将LD_LIBRARY_PATH环境变量加到用户级别的配置文件~/.bashrc中,
编辑器(如vi、记事本)是指我用它来写程序的编辑代码而我们写的代码语句电脑是不懂的我们需要把它转成电脑能懂的语句编译器就是这样的转化工具。
就是说我们用编辑器编写程序由编译器编译后才可以运行
编译器是将易于编写、阅读和维护的高级计算机语言翻译为计算机能解读、运行的低级机器语言的程序。
开发的编程语言编译器。
gcc原本作为GNU操作系统的官方编译器现已被大多数类Unix操作系统如Linux、BSD、Mac
file指定生成的输出文件名为file-E只进行预处理-S(大写)只进行预处理和编译-c(小写)只进行预处理、编译和汇编-v
n0~3编译优化n越大优化得越多-Wall提示更多警告信息-D编译时定义宏
生成的程序比较大需要更多的系统资源在装入内存时会消耗更多的时间库函数有了更新必须重新编译应用程序
动态链接连接器在链接时仅仅建立与所需库函数的之间的链接关系在程序运行时才将所需资源调入可执行程序。
在需要的时候才会调入对应的资源函数简化程序的升级有着较小的程序体积实现进程之间的资源共享避免重复拷贝
前面我们编写的应用程序大量用到了标准库函数系统默认采用动态链接的方式进行编译程序若想采用静态编译加入-static参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rIPlYZXb-1684633973967)(D:\Typora笔记\c\Linux系统编程.assets\image-20230505163136717.png)]
所谓“程序库”简单说就是包含了数据和执行码的文件。
其不能单独执行可以作为其它执行程序的一部分来完成某些功能。
库的存在可以使得程序模块化可以加快程序的再编译可以实现代码重用,可以使得程序便于升级。
静态库可以认为是一些目标代码的集合是在可执行程序运行前就已经加入到执行码中成为执行程序的一部分。
按照习惯,一般以“.a”做为文件后缀名。
静态库的命名一般分为三个部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77mO84MK-1684633973968)(D:\Typora笔记\c\Linux系统编程.assets\image-20230505164313804.png)]
guojiahuiguojiahui-virtual-machine:~/Heima/day1$
add.步骤2使用打包工具ar将准备好的.o文件打包为.a文件
假设测试文件为main.c静态库文件为libtest.a头文件为head.h
-L表示要连接的库所在为当前目录-l(小写L)指定链接时需要的库去掉前缀和后缀
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qjybuUq-1684633973968)(D:\Typora笔记\c\Linux系统编程.assets\image-20230505164553292.png)]
共享库在程序编译时并不会被连接到目标代码中而是在程序运行是才被载入。
不同的应用程序如果调用相同的库那么在内存里只需要有一份该共享库的实例规避了空间浪费问题。
动态库在程序运行是才被载入也解决了静态库对程序的更新、部署和发布页会带来麻烦。
用户只需要更新动态库即可增量更新。
按照习惯,一般以“.so”做为文件后缀名。
共享库的命名一般分为三个部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRuIf56V-1684633973970)(D:\Typora笔记\c\Linux系统编程.assets\clip_image002-1527511145606.jpg)]
/lib/x86_64-linux-gnu/libc.so.6
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-no2kpmTK-1684633973970)(D:\Typora笔记\c\Linux系统编程.assets\image-20230505215355781.png)]
当系统加载可执行代码时候能够知道其所依赖的库的名字但是还需要知道绝对路径。
此时就需要系统动态载入器(dynamic
对于elf格式的可执行程序是由ld-linux.so*来完成的它先后搜索elf文件的
LD_LIBRARY_PATH$LD_LIBRARY_PATH:库路径
LD_LIBRARY_PATH$LD_LIBRARY_PATH:库路径设置到~/.bashrc或者
LD_LIBRARY_PATH$LD_LIBRARY_PATH:路径
编辑/etc/ld.so.conf文件加入库文件所在目录的路径
dengitcast:~/share/3rd/2share_test$
/home/deng/test/6share_test/libtest.so
一个工程中的源文件不计其数其按类型、功能、模块分别放在若干个目录中makefile定义了一系列的规则来指定哪些文件需要先编译哪些文件需要后编译哪些文件需要重新编译甚至于进行更复杂的功能操作因为
makefile就像一个Shell脚本一样其中也可以执行操作系统的命令。
Makefile带来的好处就是——“自动化编译”一旦写好只需要一个make命令整个工程完全自动编译极大的提高了软件开发的效率。
make是一个命令工具是一个解释makefile中指令的命令工具一般来说大多数的IDE都有这个命令比如Delphi的make[Visual
C](https://baike.baidu.com/item/Visual
C%2B%2B)的nmakeLinux下GNU的make。
可见makefile都成为了一种在工程方面的编译方法。
大项目中源代码比较多手工维护、编译时间长而且编译命令复杂难以记忆及维护
把代码维护命令及编译命令写在makefile文件中然后再用make工具解析此文件自动执行相应命令可实现代码的合理编译
在改动其中一个文件的时候能判断哪些文件被修改过可以只对该文件进行重新编译然后重新链接所有的目标文件节省编译时间
makefile和Makefile都可以推荐使用Makefile。
通常是要产生的文件名称目标可以是可执行文件或其它obj文件也可是一个动作的名称
make执行的动作一个规则可以含几个命令可以没有有多个命令时每个命令占一行
make默认在工作目录中寻找名为GNUmakefile、makefile、Makefile的文件作为makefile输入文件-f
dir读取makefile之前改变工作路径至dir目录-n只打印要执行的命令但不执行-s执行但不显示执行的命令
若使用make命令时没有指定目标则make工具默认会实现makefile文件内的第一个目标然后退出
指定了make工具要实现的目标目标可以是一个或多个多个目标间用空格隔开。
make默认在工作目录中寻找名为GNUmakefile、makefile、Makefile的文件作为makefile输入文件-f
dir读取makefile之前改变工作路径至dir目录-n只打印要执行的命令但不执行-s执行但不显示执行的命令
若使用make命令时没有指定目标则make工具默认会实现makefile文件内的第一个目标然后退出指定了make工具要实现的目标目标可以是一个或多个多个目标间用空格隔开。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xPzkbE1X-1684633973971)(D:\Typora笔记\c\Linux系统编程.assets\image-20230506122603448.png)]
检查规则中的目标是否需要更新必须先检查它的所有依赖,依赖中有任一个被更新,则目标必须更新
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sd1cZ68U-1684633973972)(D:\Typora笔记\c\Linux系统编程.assets\clip_image002-1527647235911.jpg)]
在Makefile中使用变量有点类似于C语言中的宏定义使用该变量相当于内容替换使用变量可以使Makefile易于维护,修改内容变得简单变量定义及使用。
makefile变量名可以以数字开头变量是大小写敏感的变量一般都在makefile的头部定义变量几乎可在makefile的任何地方使用
除了使用用户自定义变量makefile中也提供了一些变量变量名大写供用户直接使用我们可以直接对其进行赋值。
如果当前目录下有同名clean文件则不执行clean对应的命令解决方案
声明目标为伪目标之后makefile将不会该判断目标是否存在或者该目标是否需要更新
“-”此条命令出错make也会继续执行后续的命令。
如:“-rm
打开现存文件或新建文件时系统内核会返回一个文件描述符文件描述符用来指定已打开的文件。
这个文件描述符相当于这个已打开文件的标号文件描述符是非负整数是文件的标识操作这个文件描述符相当于操作这个描述符所指定的文件。
程序运行起来后每个进程都有一张文件描述符的表标准输入、标准输出、标准错误输出设备文件被打开对应的文件描述符
//标准错误的文件描述符在程序运行起来后打开其他文件时系统会返回文件描述符表中最小可用的文件描述符并将此文件描述符记录在表中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KENJPXJv-1684633973972)(D:\Typora笔记\c\Linux系统编程.assets\1527651181126.png)]
参数pathname文件的路径及文件名flags打开文件的行为标志必选项
O_RDWRmode这个参数只有在文件不存在时有效指新建文件时指定文件的权限
取值含义O_RDONLY以只读的方式打开O_WRONLY以只写的方式打开O_RDWR以可读、可写的方式打开
取值含义O_CREAT文件不存在则创建文件使用此选项时需使用mode说明文件的权限O_EXCL如果同时指定了O_CREAT且文件已经存在则出错O_TRUNC如果文件存在则清空文件内容O_APPEND写文件时数据添加到文件末尾O_NONBLOCK对于设备文件,
~umaskshell进程的umask掩码可以用umask命令查看
取值八进制含义S_IRWXU00700文件所有者的读、写、可执行权限S_IRUSR00400文件所有者的读权限S_IWUSR00200文件所有者的写权限S_IXUSR00100文件所有者的可执行权限S_IRWXG00070文件所有者同组用户的读、写、可执行权限S_IRGRP00040文件所有者同组用户的读权限S_IWGRP00020文件所有者同组用户的写权限S_IXGRP00010文件所有者同组用户的可执行权限S_IRWXO00007其他组用户的读、写、可执行权限S_IROTH00004其他组用户的读权限S_IWOTH00002其他组用户的写权限S_IXOTH00001其他组用户的可执行权限
需要说明的是当一个进程终止时内核对该进程所有尚未关闭的文件描述符调用close关闭所以即使用户程序不调用close在终止时内核也会自动关闭它打开的所有文件。
但是对于一个长年累月运行的程序(比如网络服务器)打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多会占用大量文件描述符和系统资源。
是两个非常有用的系统调用都是用来复制一个文件的描述符使新的文件描述符也标识旧的文件描述符所标识的文件。
这个过程类似于现实生活中的配钥匙钥匙相当于文件描述符锁相当于文件本来一个钥匙开一把锁相当于一个文件描述符对应一个文件现在我们去配钥匙通过旧的钥匙复制了一把新的钥匙这样的话旧的钥匙和新的钥匙都能开启这把锁。
也一样通过原来的文件描述符复制出一个新的文件描述符这样的话原来的文件描述符和新的文件描述符都指向同一个文件我们操作这两个文件描述符的任何一个都能操作它所对应的文件。
复制出一个新的文件描述符新的文件描述符是调用进程文件描述符表中最小可用的文件描述符最终
1023如果指定的数字已经被占用和某个文件有关联此函数会自动关闭
world));//调用lseek函数移动文件指针到开始处lseek(fd,
buf);//关闭文件close(fd);close(newfd);return
同一个剧本可以在多个舞台同时上演。
同样同一个程序也可以加载为不同的进程(彼此之间互不影响)
语言代码通过编译器编译最终它会成为一个可执行程序当这个可执行程序运行起来后没有结束之前它就成为了一个进程。
程序是存放在存储介质上的一个可执行文件而进程是程序执行的过程。
进程的状态是变化的其包括进程的创建、调度和消亡。
程序是静态的进程是动态的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ch28ZeJ-1684633973973)(D:\Typora笔记\c\Linux系统编程.assets\1527992375886.png)]
程序就类似于剧本(纸)进程类似于戏(舞台、演员、灯光、道具…)同一个剧本可以在多个舞台同时上演。
同样同一个程序也可以加载为不同的进程(彼此之间互不影响)。
系统中操作系统是通过进程去完成一个一个的任务进程是管理事务的基本单元。
进程拥有自己独立的处理环境如当前需要用到哪些环境变量程序运行的目录在哪当前是哪个用户在运行此程序等和系统资源如处理器
我们可以这么理解公司相当于操作系统部门相当于进程公司通过部门来管理系统通过进程管理对于各个部门每个部门有各自的资源如人员、电脑设备、打印机等。
如若将CPU的1S的时间分成1000个时间片每个进程执行完一个时间片必须无条件让出CPU的使用权这样1S中就可以执行1000个进程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOaPb69Z-1684633973973)(D:\Typora笔记\c\Linux系统编程.assets\wps1.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iYksEmr4-1684633973974)(D:\Typora笔记\c\Linux系统编程.assets\wps2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-58DBIrz6-1684633973974)(D:\Typora笔记\c\Linux系统编程.assets\wps3.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcY3h0X5-1684633973975)(D:\Typora笔记\c\Linux系统编程.assets\wps4.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2PbPndq0-1684633973975)(D:\Typora笔记\c\Linux系统编程.assets\wps5.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5JuQdi5M-1684633973975)(D:\Typora笔记\c\Linux系统编程.assets\wps6.jpg)]
并行是两个队列同时使用两台咖啡机并发是两个队列交替使用一台咖啡机
每个进程在内核中都有一个进程控制块PCB来维护进程相关的信息Linux内核的进程控制块是task_struct结构体。
/usr/src/linux-headers-4.4.0-96/include/linux/sched.h文件的1390行处可以查看struct
]进程id。
系统中每个进程有唯一的id在C语言中用pid_t类型表示其实就是一个非负整数。
进程的状态有就绪、运行、挂起、停止等状态。
进程切换时需要保存和恢复的一些CPU寄存器。
描述虚拟地址空间的信息。
描述控制终端的信息。
当前工作目录Current
--pwdumask掩码。
文件描述符表包含很多指向file结构体的指针。
和信号相关的信息。
用户id和组id。
会话Session和进程组。
进程可以使用的资源上限Resource
进程基本的状态有5种。
分别为**初始态就绪态运行态挂起态与终止态。
**其中初始态为进程准备阶段常与就绪态结合来看。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zq5ft5Lu-1684633973975)(D:\Typora笔记\c\Linux系统编程.assets\wps7.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bSZunqj1-1684633973976)(D:\Typora笔记\c\Linux系统编程.assets\1527994562159.png)]
IOR正在运行或在队列中的进程S(大写)处于休眠状态T停止或被追踪Z僵尸进程W进入内存交换从内核2.6开始无效X死掉的进程高优先级N低优先级s包含子进程位于前台的进程组
进程是一个具有一定独立功能的程序它是操作系统动态执行的基本单元。
ps命令可以查看进程的详细状况常用选项(选项可以不加“-”)如下
选项含义-a显示终端上的所有进程包括其他用户的进程-u显示进程的详细状态-x显示没有控制终端的进程-w显示加宽以便显示更多的信息-r只显示正在运行的进程
top命令用来动态显示运行中的进程。
top命令能够在运行后在指定的时间间隔更新显示信息。
可以在使用top命令时加上-d
按键含义M根据内存使用量来排序P根据CPU占有率来排序T根据进程运行时间的长短来排序U可以根据后面输入的用户名来筛选进程K可以根据后面输入的PID来杀死进程。
q退出h获得帮助
信号值从0到15其中9为绝对终止可以处理一般信号无法终止的进程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1XfZrc5-1684633973976)(D:\Typora笔记\c\Linux系统编程.assets\image-20221117094111216.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bo2nSRtr-1684633973977)(D:\Typora笔记\c\Linux系统编程.assets\wps8.jpg)]
fork.pid:[%d]\n,getpid());pid_t
getpid(),getppid());//sleep(1);}else
getpid(),getppid());}printf(after
fork.pid:[%d]\n,getpid());return
getpid(),getppid());//sleep(1);}else
getpid(),getppid());}}sleep(10);return
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQchz8BB-1684633973977)(D:\Typora笔记\c\Linux系统编程.assets\image-20221118160236878.png)]
getpid(),getppid());g_var;//sleep(1);}else
0)//子进程{sleep(1);//为了避免父进程还没有执行子进程就结束了printf(child:pid
getpid(),getppid());printf(child:g_var
0;}[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zsiInzdT-1684633973977)(D:\Typora笔记\c\Linux系统编程.assets\image-20221118165319619.png)]
有的时候需要在一个进程里面执行其他的命令或者是用户自定义的应用程序此时就用到了exec函数族当中的函数。
使用方法一般都是在父进程里面调用fork创建处子进程然后在子进程里面调用exec函数。
返回值若是成功则不返回不会再执行exec函数后面的代码若是失败会执行execl后面的代码可以用perror打印错误原因。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpW3H6PV-1684633973978)(D:\Typora笔记\c\Linux系统编程.assets\image-20221119105126533.png)]
exce函数是用一个新程序替换了当前进程的代码段数据段堆和栈原有进程空间没有发生变化并没有创建新的进程进程PID没有发生改变。
当一个进程退出之后进程能够回收自己的用户区的资源但是不能回收内核空间的PCB资源必须由它的父进程调用wait或者waitpid函数完成对子进程的回收避免造成系统资源的浪费。
若子进程的父进程已经死掉而子进程还存活着这个进程就成了孤儿进程。
为了保证每个进程都有一个父进程孤儿进程会被init进程领养init进程成为了孤儿进程的养父进程当孤儿进程退出之后由init进程完成对孤儿进程的回收。
编写模拟孤儿进程的代码讲解孤儿进程验证孤儿进程的父进程是否由原来的父进程变成了init进程。
if(pid0)//父进程{sleep(5);printf(fa***r:
getppid());sleep(20);printf(child:
}[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nJnQZZI8-1684633973978)(D:\Typora笔记\c\Linux系统编程.assets\image-20221119110651517.png)]
但是父进程没有调用wait或waitpid函数完成对子进程的回收则该子进程就成了僵尸进程。
if(pid0)//父进程{sleep(100);printf(fa***r:
由于僵尸进程是一个已经死亡的进程所以不能使用kill命令将其杀死通过杀死其父进程的方法可以消除僵尸进程。
杀死其父进程后这个僵尸进程会被init进程领养由init进程完成对僵尸进程的回收。
wait(status);printf(wpid[%d]\n,
wpid);if(WIFEXITED(status)){printf(child
原因是杀死其父进程可以让init进程领养僵尸进程最后init进程回收僵尸进程。
阻塞并等待子进程退出回收子进程残留资源获取子进程结束状态(退出原因)。
Linux环境下进程地址空间相互独立每个进程各自有不同的用户地址空间。
任何一个进程的全局变量在另一个进程中都看不到所以进程和进程之间不能相互访问要交换数据必须通过内核在内核中开辟一块缓冲区进程1把数据从用户空间拷到内核缓冲区进程2再从内核缓冲区把数据读走内核提供的这种机制称为进程间通信IPCInterProcess
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NODDdI8-1684633973978)(D:\Typora笔记\c\Linux系统编程.assets\wps9.jpg)]
在进程间完成数据传递需要借助操作系统提供特殊的方法如文件、管道、信号、共享内存、消息队列、套接字、命名管道等。
随着计算机的蓬勃发展一些方法由于自身设计缺陷被淘汰或者弃用。
现今常用的进程间通信方式有
管道是一种最基本的IPC机制也称匿名管道应用于有血缘关系的进程之间完成数据传递。
调用pipe函数即可创建一个管道。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgxFmaIl-1684633973979)(D:\Typora笔记\c\Linux系统编程.assets\wps10.jpg)]
管道的本质是一块内核缓冲区由两个文件描述符引用一个表示读端一个表示写端。
规定数据从管道的写端流入管道从读端流出。
当两个进程都终结的时候管道也自动消失。
管道的读端和写端默认都是阻塞的。
管道的实质是内核缓冲区内部使用环形队列实现。
缓刑管道效率高默认缓冲区大小为4K可以使用ulimit
-a命令获取大小。
实际操作过程中缓冲区会根据数据压力做适当调整。
数据一旦被读走便不在管道中存在不可反复读取。
数据只能在一个方向上流动若要实现双向流动必须使用两个管道只能在有血缘关系的进程间使用管道。
若函数调用成功fd[0]存放管道的读端fd[1]存放管道的写端
函数调用成功返回读端和写端的文件描述符其中****fd[0]是读端
fd[1]是写端**向管道读写数据是通过使用这两个文件描述符进行的读写管道的实质是操作内核缓冲区。
****
管道创建成功以后创建该管道的进程父进程同时掌握着管道的读端和写端。
如何实现父子进程间通信呢
一个进程在由pipe()创建管道后一般再fork一个子进程然后通过管道实现父子进程间的通信因此也不难推出只要两个进程中存在血缘关系这里的血缘关系指的是具有共同的祖先都可以采用管道方式来进行通信。
*父子进程间具有相同的文件描述符且指向同一个管道pipe*其他没有关系的进程不能获得pipe产生的两个文件描述符也就不能利用同一个管道进行通信。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zJwTaX18-1684633973979)(D:\Typora笔记\c\Linux系统编程.assets\wps11.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pH8FVZWa-1684633973979)(D:\Typora笔记\c\Linux系统编程.assets\wps12.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wS7i3J2O-1684633973980)(D:\Typora笔记\c\Linux系统编程.assets\wps13.jpg)]
父进程调用pipe函数创建管道得到两个文件描述符fd[0]和fd[1]分别指向管道的读端和写端。
父进程调用fork创建子进程那么子进程也有两个文件描述符指向同一管。
父进程关闭管道读端子进程关闭管道写端。
父进程可以向管道中写入数据子进程将管道中的数据读出这样就实现了父子进程间通信。
if(pid0){//关闭读端close(fd[0]);write(fd[1],hello
world));wait(NULL);}else{//关闭写段close(fd[1]);char
buf[64];memset(buf,0x00,sizeof(buf));int
read(fd[0],buf,sizeof(buf));printf(read
默认情况下管道的读写两端都是阻塞的若要设置读或者写端为非阻塞则可参
FIFO常被称为命名管道以区分管道(pipe)。
管道(pipe)只能用于“有血缘关系”的进程间通信。
但通过FIFO不相关的进程也能交换数据。
FIFO是Linux基础文件类型中的一种文件类型为p可通过ls
-l查看文件类型。
但FIFO文件在磁盘上没有数据块文件大小为0仅仅用来标识内核中一条通道。
进程可以打开这个文件进行read/write实际上是在读写内核缓冲区这样就实现了进程间通信。
当创建了一个FIFO就可以使用open函数打开它常见的文件I/O函数都可用于FIFO。
如close、read、write、unlink等。
out对FIFO的读总是从开始处返回数据对它们的写则把数据添加到末尾。
*它们不支持诸如*lseek*()等文件定位操作。
*
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kt0wmEbQ-1684633973980)(D:\Typora笔记\c\Linux系统编程.assets\wps1-1670200792026-1.jpg)]
使一个磁盘文件与存储空间中的一个缓冲区相映射。
从缓冲区中取数据就相当于读文件中的相应字节将数据写入缓冲区则会将数据写入文件。
这样就可在不使用read和write函数的情况下使用地址指针完成I/O操作。
使用存储映射这种方法首先应通知内核将一个指定文件映射到存储区域中。
这个映射工作可以通过mmap函数来实现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7rJJ0zM-1684633973981)(D:\Typora笔记\c\Linux系统编程.assets\wps2-1670200963607-3.jpg)]
对映射区的写入操作会产生一个映射区的复制(copy-on-write),
创建映射区的过程中隐含着一次对映射文件得读操作将文件内容读取到映射区
当MAP_SHARED时要求映射区的权限文件打开的权限出于对映射区的保护。
而MAP_PRIVATE则无所谓因为mmap中的权限是对内存的限制
特别注意当映射文件大小为0时不能创建映射区。
所以用于映射的文件必须要有实际大小mmap使用时常常会出现总线错误。
通常是由于共享文件储存空间大小引起的。
munmap传入的地址一定是mmap的返回地址。
坚决杜绝指针操作。
mmap创建映射区出现错误概率非常高一定要检查返回值确保映射区建立成功再进行后续操作。
第一个参数写成NULL第二个参数要映射的大小0第三个参数PROT_READ
MAP_PRIVATE第五个参数打开的文件对应的文件描述符第六个参数4k的整数倍
进程A给进程B发送信号进程B收到信号之前执行自己的代码收到信号后不管执行到程序的什么位置都要暂停运行去处理信号处理完毕后再继续执行。
与硬件中断类似——异步模式。
但信号是软件层面上实现的中断早期常被称为“软中断”。
每个进程收到的所有信号都是由内核负责发送的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nRm9DyRr-1684633973981)(D:\Typora笔记\c\Linux系统编程.assets\wps1-1670315130080-1.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cc3y4Svo-1684633973981)(D:\Typora笔记\c\Linux系统编程.assets\1527928967909.png)]
在标准信号中有一些信号是有三个“Value”第一个值通常对alpha和sparc架构有效中间值针对x86、arm和其他架构最后一个应用于mips架构。
一个‘-’表示在对应架构上尚未定义该信号。
不同的操作系统定义了不同的系统信号。
因此有些信号出现在Unix系统内也出现在Linux中而有的信号出现在FreeBSD或Mac
OS中却没有出现在Linux下。
这里我们只研究Linux系统中的信号。
(默认即时对该种信号忽略操作)Core终止进程生成Core文件。
(查验死亡原因用于gdb调试)Stop停止暂停进程Cont继续运行进程
SIGSTOP信号不允许忽略和捕捉只能执行默认动作。
甚至不能将其设置为阻塞。
另外需清楚只有每个信号所对应的事件发生了该信号才会被递送(但不一定递达)不应乱发信号
0无效的内存访问等。
这些情况通常由硬件检测到并通知内核然后内核产生适当的信号发送给相应的进程。
当检测到某种软件条件已发生(如定时器alarm)并将其通知有关进程时产生信号。
调用系统函数(如kill、raise、abort)将发送信号。
注意接收信号进程和发送信号进程的所有者必须相同或发送信号进程的所有者必须是超级用户。
信号的实现手段导致信号有很强的延时性但对于用户来说时间非常短不易察觉。
Linux内核的进程控制块PCB是一个结构体task_struct,
除了包含进程id状态工作目录用户id组id文件描述符表还包含了信号相关的信息主要指阻塞信号集和未决信号集。
将某些信号加入集合对他们设置屏蔽当屏蔽x信号后再收到该信号该信号的处理将推后(处理发生在解除屏蔽后)。
信号产生未决信号集中描述该信号的位立刻翻转为1表示信号处于未决状态。
当信号被处理对应位翻转回为0。
这一时刻往往非常短暂。
信号产生后由于某些原因(主要是阻塞)不能抵达。
这类信号的集合称之为未决信号集。
在屏蔽解除前信号一直处于未决状态。
在PCB中有两个非常重要的信号集。
一个称之为“阻塞信号集”另一个称之为“未决信号集”。
这两个信号集都是内核使用位图机制来实现的。
但操作系统不允许我们直接对其进行位操作。
而需自定义另外一个集合借助信号集操作函数来对PCB中的这两个信号集进行修改。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7fYLX78-1684633973981)(D:\Typora笔记\c\Linux系统编程.assets\1527930344052.png)]
不能更改信号的处理方式因为它们向用户提供了一种使进程终止的可靠方法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Kqhf6Ym-1684633973981)(D:\Typora笔记\c\Linux系统编程.assets\1527931072795.png)]
等函数等待子进程结束但是这会导致父进程挂起。
如果父进程要处理的事情很多不能够挂起通过
只要子进程退出触发SIGCHLD自动调用sig_child()signal(SIGCHLD,
3如果父进程不关心子进程什么时候结束那么可以用signalSIGCHLD,
SIG_IGN通知内核自己对子进程的结束不感兴趣父进程忽略此信号那么子进程结束后内核会回收
在UNIX系统中用户通过终端登录系统后得到一个Shell进程这个终端成为Shell进程的控制终端Controlling
Terminal进程中控制终端是保存在PCB中的信息而fork会复制PCB中的信息因此由Shell进程启动的其它进程的控制终端也是这个终端。
默认情况下没有重定向每个进程的标准输入、标准输出和标准错误输出都指向控制终端进程从标准输入读也就是读用户的键盘输入进程往标准输出或标准错误输出写也就是输出到显示器上。
信号中还讲过在控制终端输入一些特殊的控制键可以给前台进程发信号例如CtrlC表示SIGINTCtrl\表示SIGQUIT。
返回值成功终端名失败NULL下面我们借助ttyname函数通过实验看一下各种不同的终端所对应的设备文件名
进程组也称之为作业。
BSD于1980年前后向Unix中增加的一个新特性。
代表一个或多个进程的集合。
每个进程都属于一个进程组。
在waitpid函数和kill函数的参数中都曾使用到。
操作系统设计的进程组的概念是为了简化对多个进程的管理。
当父进程创建子进程的时候默认子进程与父进程属于同一进程组。
进程组ID为第一个进程ID(组长进程)。
所以组长进程标识其进程组ID为其进程ID
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpZN54Zg-1684633973982)(D:\Typora笔记\c\Linux系统编程.assets\1528119946594.png)]
组长进程可以创建一个进程组创建该进程组中的进程然后终止。
只要进程组中有一个进程存在进程组就存在与组长进程是否终止无关。
进程组生存期进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
中的后台服务进程。
它是一个生存期较长的进程通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
一般采用以d结尾的名字。
守护进程是个特殊的孤儿进程这种进程脱离终端为什么要脱离终端呢之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断其在执行过程中的信息也不在任何终端上显示。
由于在
中每一个系统与用户进行交流的界面称为终端每一个从此终端开始运行的进程都会依附于这个终端这个终端就称为这些进程的控制终端当控制终端被关闭时相应的进程都会自动关闭。
umask()函数防止继承的文件创建屏蔽字拒绝某些权限增加守护进程灵活性
关闭文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);//
0;sigemptyset(sigact.sa_mask);sigact.sa_handler
设置第一次触发定时器时间act.it_value.tv_sec
在许多经典的操作系统教科书中总是把进程定义为程序的执行实例它并不执行什么,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZG7bVU24-1684633973982)(D:\Typora笔记\c\Linux系统编程.assets\1528121100232.png)]
进程直观点说保存在硬盘上的程序运行以后会在内存空间里形成一个独立的内存体这个内存体有自己的地址空间有自己的堆上级挂靠单位是操作系统。
操作系统会以进程为单位分配系统资源所以我们也说进程是CPU分配资源的最小单位。
线程存在与进程当中(进程可以认为是线程的容器)是操作系统调度执行的最小单位。
说通俗点线程就是干活的。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动进程是系统进行资源分配和调度的一个独立单位。
调度和分派的基本单位它是比进程更小的能独立运行的基本单位。
线程自己基本上不拥有系统资源只拥有一点在运行中必不可少的资源如程序计数器一组寄存器和栈但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
如果说进程是一个资源管家负责从主人那里要资源的话那么线程就是干活的苦力。
一个管家必须完成一项工作就需要最少一个苦力也就是说一个进程最少包含一个线程也可以包含多个线程。
苦力要干活就需要依托于管家所以说一个线程必须属于某一个进程。
进程有自己的地址空间线程使用进程的地址空间也就是说进程里的资源线程都是有权访问的比如说堆啊栈啊静态存储区什么的。
类Unix系统中早期是没有“线程”概念的80年代才引入借助进程机制实现出了线程的概念。
process)也有PCB创建线程使用的底层函数和进程一样都是clone从内核里看进程和线程是一样的都有各自不同的PCB.进程可以蜕变成线程在linux下线程最是小的执行单位进程是最小的分配资源单位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KqOcyvoO-1684633973982)(D:\Typora笔记\c\Linux系统编程.assets\1528121496711.png)]
实际上无论是创建进程的fork还是创建线程的pthread_create底层实现都是调用同一个内核函数
优点相对突出缺点均不是硬伤。
Linux下由于实现方法导致进程、线程差别不是很大。
就像每个进程都有一个进程号一样每个线程也有一个线程号。
进程号在整个系统中是唯一的但线程号不同线程号只在它所属的进程环境中有效。
的时候用一个结构体来表示所以在可移植的操作系统实现不能把它做为整数处理。
参数thread线程标识符地址。
attr线程属性结构体地址通常设置为
NULL。
start_routine线程函数的入口地址。
arg传给线程函数的参数。
0在一个线程中调用pthread_create()创建新的线程后当前线程从pthread_create()返回继续往下执行而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。
由于pthread_create的错误码不保存在errno中因此不能直接用perror()打印错误信息可以先用strerror()把错误码转换成错误信息再打印。
参数thread被等待的线程号。
retval用来存储线程退出状态的指针的地址。
value保存线程退出的返回值pthread_join(tid,
}调用该函数的线程将挂起等待直到id为thread的线程终止。
thread线程以不同的方法终止通过pthread_join得到的终止状态是不同的总结如下
如果thread线程通过return返回retval所指向的单元里存放的是thread线程函数的返回值。
如果thread线程被别的线程调用pthread_cancel异常终止掉retval所指向的单元里存放的是常数PTHREAD_CANCELED。
如果thread线程是自己调用pthread_exit终止的retval所指向的单元存放的是传给pthread_exit的参数。
一般情况下线程终止后其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。
但是线程也可以被置为detach状态这样的线程一旦终止就立刻回收它占用的所有资源而不保留终止状态。
不能对一个已经处于detach状态的线程调用pthread_join这样的调用将返回EINVAL错误。
也就是说如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。
功能使调用线程与当前进程分离分离后不代表此线程不依赖与当前进程线程分离的目的是将线程资源的回收工作交由系统自动来完成也就是说当被分离的线程结束之后系统会自动回收它的资源。
所以此函数不会阻塞。
在进程中我们可以调用exit函数或_exit函数来结束进程在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。
线程从执行函数中返回。
线程调用pthread_exit退出线程。
线程可以被同一进程中的其它线程取消。
功能退出调用线程。
一个进程中的多个线程是共享该进程的数据段因此通常线程退出后所占用的资源并不会释放。
注意线程的取消并不是实时的而又一定的延时。
需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档必须到达指定的场所(存档点如客栈、仓库、城里等)才能存储进度。
取消点是线程检查是否被取消并按请求进行动作的一个位置。
通常是一些系统调用creatopenpauseclosereadwrite…
注意线程的取消并不是实时的而又一定的延时。
需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档必须到达指定的场所(存档点如客栈、仓库、城里等)才能存储进度。
取消点是线程检查是否被取消并按请求进行动作的一个位置。
通常是一些系统调用creatopenpauseclosereadwrite…
如果thread线程是自己调用pthread_exit终止的retval所指向的单元存放的是传给pthread_exit的参数。
一般情况下线程终止后其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。
但是线程也可以被置为detach状态这样的线程一旦终止就立刻回收它占用的所有资源而不保留终止状态。
不能对一个已经处于detach状态的线程调用pthread_join这样的调用将返回EINVAL错误。
也就是说如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。
功能使调用线程与当前进程分离分离后不代表此线程不依赖与当前进程线程分离的目的是将线程资源的回收工作交由系统自动来完成也就是说当被分离的线程结束之后系统会自动回收它的资源。
所以此函数不会阻塞。
在进程中我们可以调用exit函数或_exit函数来结束进程在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。
线程从执行函数中返回。
线程调用pthread_exit退出线程。
线程可以被同一进程中的其它线程取消。
功能退出调用线程。
一个进程中的多个线程是共享该进程的数据段因此通常线程退出后所占用的资源并不会释放。
注意线程的取消并不是实时的而又一定的延时。
需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档必须到达指定的场所(存档点如客栈、仓库、城里等)才能存储进度。
取消点是线程检查是否被取消并按请求进行动作的一个位置。
通常是一些系统调用creatopenpauseclosereadwrite…
注意线程的取消并不是实时的而又一定的延时。
需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档必须到达指定的场所(存档点如客栈、仓库、城里等)才能存储进度。
取消点是线程检查是否被取消并按请求进行动作的一个位置。
通常是一些系统调用creatopenpauseclosereadwrite…
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback