最后的话
最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!
资料预览
给大家整理的视频资料:
给大家整理的电子书资料:
如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
- ctrl+c,信号终止
1.return
return是一种常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数,会将main的返回值当做 exit的参数。
2.exit
使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作:
- 执行用户通过atexit或on_exit定义的清理函数。
- 关闭所有打开的流,所有的缓存数据均被写入。
- 调用_exit函数终止进程。
举个栗子:
#include <stdio.h>
#include <stdlib.h>
void show()
{
printf("hello linux\n");
exit(1);
}
int main()
{
show();
return 0;
}
运行结果:
结果分析:
exit终止进程前会将缓冲区当中的数据输出。
3._exit函数
使用_exit函数退出进程的方法我们并不经常使用,_exit函数也可以在代码中的任何地方退出进程,但是_exit函数会直接终止进程,并不会在退出进程前会做任何收尾工作。
举个栗子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void show()
{
printf("hello linux");
_exit(2);
}
int main()
{
show();
return 0;
}
运行结果:
结果分析:
使用_exit终止进程,则缓冲区当中的数据将不会被输出。
💫进程异常退出
情况一:向进程发生信号导致进程异常退出。
例如,在进程运行过程中向进程发生kill -9信号使得进程异常退出,或是使用Ctrl+C使得进程异常退出等。
情况二:代码错误导致进程运行时异常退出。
例如,代码当中存在野指针问题使得进程运行时异常退出,或是出现除0的情况使得进程运行时异常退出等。
🌙进程等待
💫进程等待的必要性
- 子进程退出,父进程如果不读取子进程的退出信息,子进程就会变成僵尸进程,进而造成内存泄漏。
- 进程一旦变成僵尸进程,那么就算是kill -9命令也无法将其杀死,因为谁也无法杀死一个已经死去的进程。
- 对于一个进程来说,最关心自己的就是其父进程,因为父进程需要知道自己派给子进程的任务完成的如何。
- 父进程需要通过进程等待的方式,回收子进程资源,获取子进程的退出信息。
💫获取子进程状态status
下面会介绍有关进程等待的两个函数wait和waitpid,它们都有一个status参数,改参数是一个输出型参数,由操作系统进行填充。如果对该参数传入NULL,表示不关心子进程的退出状态信息。反之,操作系统会通过该参数,将子进程的退出状态信息反馈给父进程。
如何理解参数status?
status实际上是一个整型变量,但是status不能当作整型里看待,因为status的不同的比特位所代表的信息不同(只研究低16位比特位)。在status的低16位比特位中,高8位表示进程的退出状态,即退出码。进程若是被信号所杀,低7位表示终止信号,而第8位是core dump标志。需要注意的是,当一个进程非正常退出时,说明该进程是被信号所杀,那么该进程的退出码也就没有意义了。
我们通过一系列位操作,就可以根据status得到进程的退出码和退出信号。
exitCode = (status >> 8) & 0xFF; //退出码
exitSignal = status & 0x7F; //退出信号
对于此,系统当中提供了两个宏来获取退出码和退出信号。
- WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号。
- WEXITSTATUS(status):用于获取进程的退出码。
exitNormal = WIFEXITED(status); //是否正常退出
exitCode = WEXITSTATUS(status); //获取退出码
需要注意的是,当一个进程非正常退出时,说明该进程是被信号所杀,那么该进程的退出码也就没有意义了。
💫进程等待的方法
1.wait方法
- 函数原型:
pid_t wait(int* status);
- 作用:等待任意子进程。
- 返回值:等待成功返回被等待进程的pid,等待失败返回-1。
- 参数:输出型参数,获取子进程的退出状态,不关心可设置为NULL。
举个栗子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
int count = 10;
while (count--)
{
printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());
sleep(1);
}
exit(0);
}
int status = 0;
pid_t ret = wait(&status);
if (ret > 0)
{
printf("wait child success...\n");
if (WIFEXITED(status))
{
printf("exit code:%d\n", WEXITSTATUS(status));
}
}
sleep(3);
return 0;
}
利用指令对进程进行实时监控:
while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done
运行结果:
结果分析:
- 创建子进程后,父进程可使用wait函数一直等待子进程,直到子进程退出后读取子进程的退出信息。
- 这时我们可以看到,当子进程退出后,父进程读取了子进程的退出信息,子进程也就不会变成僵尸进程了。
1.waitpid方法
- 函数原型:pid_t waitpid(pid_t pid, int* status, int options);
- 作用:等待指定子进程或任意子进程。
- 返回值:
- 等待成功返回被等待进程的pid。
- 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
- 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。
- 参数:
- 等待成功返回被等待进程的pid。
- 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
- 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。
举个栗子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if (id == 0){
//child
int count = 10;
while (count--){
printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());
sleep(1);
}
exit(0);
}
//father
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if (ret >= 0){
//wait success
printf("wait child success...\n");
if (WIFEXITED(status)){
//exit normal
printf("exit code:%d\n", WEXITSTATUS(status));
}
else{
//signal killed
printf("killed by siganl %d\n", status & 0x7F);
}
}
sleep(3);
return 0;
}
运行结果:
结果分析:
- 创建子进程后,父进程可使用waitpid函数一直等待子进程(此时将waitpid的第三个参数设置为0),直到子进程退出后读取子进程的退出信息。
- 被信号杀死而退出的进程,其退出码将没有意义。
🌙进程程序替换
💫替换原理
用fork
创建子进程后执行的是和父进程相同的程序
(
但有可能执行不同的代码分支
),
子进程往往要调用一种
exec
函数 以执行另一个程序。当进程调用一种exec
函数时
,
该进程的用户空间代码和数据完全被新程序替换
,
从新程序的启动 例程开始执行。调用exec
并不创建新进程
,
所以调用
exec
前后该进程的
id
并未改变。
1.创建子进程的目的?
执行父进程磁盘代码的一部分;
让子进程加载磁盘上指定的程序到内存中,执行新程序的的代码和数据。
2.程序替换的本质
将指定程序的代码和数据加载到指定的位置
3.进程替换的时候,有没有创建新的进程?
我们知道一个进程被创建出来,OS会给它分配进程PCB,mm_struct,页表等信息,同时会将程序的代码和数据加载到物理内存。要知道进程程序替换之后,该进程的PCB,进程地址空间,页表等信息都不会发生改变,仅仅是把一个新的程序的数据和代码替换了原来进程的代码和数据,只是物理内存当中的数据和代码发生了改变,所以并没有创建新的进程,而且进程程序替换前后该进程的pid也没有改变。
4.子进程进行进程程序替换后,会影响父进程的代码和数据吗?
子进程刚被创建时,与父进程共享代码和数据,但当子进程需要进行进程程序替换(调用exec函数)时,也就意味着子进程需要对其数据和代码进行写入操作,这时便需要将父子进程共享的代码和数据进行写时拷贝,此后父子进程的代码和数据分离,因此子进程进行进程程序替换后不会影响父进程的代码和数据。
💫替换函数
一、int execl(const char *path, const char *arg, ...);
第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾。
举例:
execl(“/usr/bin/ls”, “ls”, “-a”, “-i”, “-l”, NULL);
二、int execlp(const char *file, const char *arg, ...);
第一个参数是要执行程序的名字,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾。
举例:
execlp(“ls”, “ls”, “-a”, “-i”, “-l”, NULL);
三、int execle(const char *path, const char *arg, ..., char *const envp[]);
第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾,第三个参数是你自己设置的环境变量。
举例:
char* myenvp[] = { “MYVAL=2021”, NULL };
execle(“./mycmd”, “mycmd”, NULL, myenvp);
四、int execv(const char *path, char *const argv[]);
第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾。
char* myargv[] = { “ls”, “-a”, “-i”, “-l”, NULL };
execv(“/usr/bin/ls”, myargv);
五、int execvp(const char *file, char *const argv[]);
第一个参数是要执行程序的名字,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾。
举例:
char* myargv[] = { “ls”, “-a”, “-i”, “-l”, NULL };
execvp(“ls”, myargv);
六、int execve(const char *path, char *const argv[], char *const envp[]);
第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾,第三个参数是你自己设置的环境变量。
举例:
char* myargv[] = { “mycmd”, NULL };
char* myenvp[] = { “MYVAL=2021”, NULL };
execve(“./mycmd”, myargv, myenvp);
💫函数解释
- 这些函数如果调用成功,则加载指定的程序并从启动代码开始执行,不再返回。
- 如果调用出错,则返回-1。
- 所以
exec
函数只有出错的返回值而没有成功的返回值。
💫命名理解
最全的Linux教程,Linux从入门到精通
======================
-
linux从入门到精通(第2版)
-
Linux系统移植
-
Linux驱动开发入门与实战
-
LINUX 系统移植 第2版
-
Linux开源网络全栈详解 从DPDK到OpenFlow
第一份《Linux从入门到精通》466页
====================
内容简介
====
本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。
本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。
需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。
本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。
需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!