📝 进程?
在 Linux 中主要有两种类型的进程:
- 前台进程(也称为交互式进程) - 这些进程由终端会话初始化和控制。换句话说,需要有一个连接到系统中的用户来启动这样的进程;它们不是作为系统功能/服务的一部分自动启动。
- 后台进程(也称为非交互式/自动进程) - 这些进程没有连接到终端;它们不需要任何用户输入。
Linux 是一个多道程序设计系统,因此系统中存在彼此相互独立的进程同时运行。此外,每个用户都会同时有几个活动的进程。因为如果是一个大型系统,可能有数百上千的进程在同时运行。
在某些用户空间中,即使用户退出登录,仍然会有一些后台进程在运行,这些进程被称为 守护进程(daemon)。
Linux 中有一种特殊的守护进程被称为 计划守护进程(Cron daemon) ,计划守护进程可以每分钟醒来一次检查是否有工作要做,做完会继续回到睡眠状态等待下一次唤醒。
📝进程查看命令ps
创建一个示例C程序:
#include <stdio.h>
#include <unistd.h>
int main(){
while(1){
printf("process!\n");
sleep(1);
}
return 0;
}
make
./myprocess
这里程序在无限循环下去,方便我们做接下来的查看工作
进一步得到我们想要的myprocess进程的信息:
ps axj | head -1 && ps axj | grep myprocess
ps axj | head -1 && ps axj | grep myprocess | grep -v grep
一个可执行程序多次加载到内存并运行,那么就可能同时存在多个进程,例如我们把myprocess进行运行多个,再用ps命令进行查看
可以发现PID不同。
📝详细查看进程
Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。
为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程的相关信息,其中N为正在运行的进程(可以想象得到,在某进程结束后其相关目录则会消失)。
大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。不过,这些可读性较差的文件在使用一些命令如apm、free、lspci或top查看时却可以有着不错的表现。
ls /proc/
这里我们以PID为14157的myprocess进程示例:
cd /proc/14157
[vimer@hcss-ecs-c1cc 14157]$ ll
这里就能看到进程的各种属性。
注意:当进程终止后,上述命令将无法找到原有进程,原进程已被销毁,/proc/目录下的对应文件夹也会被删除
📝getpid();getppid()
getpid(取得进程识别码)
相关函数: fork,kill,getpid
表头文件: #include<unistd.h>
定义函数: pid_t getpid(void);
函数说明:
getpid()用来取得目前进程的进程识别码,许多程序利用取到的
此值来建立临时文件,以避免临时文件相同带来的问题。
返回值: 目前进程的进程识别码
getppid 函数:获取当前进程的父进程 ID
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
while(1){
printf("process!process ID:%d\n",getpid());
sleep(1);
}
return 0;
}
每一次进程启动时,其进程PID都会不同。
📝知识点
1.bash命令行解释器,本质上也是一个进程
2.命令行启动的所有程序,最终都会变成进程,而该进程对应的父进程都是bash
📝父子进程&创建子进程
man fork
并对返回值进行检索,/return val
RETURN VALUE
On success, the PID of the child process is returned in the parent, and
0 is returned in the child. On failure, -1 is returned in the parent,
no child process is created, and errno is set appropriately.
如果成功,则在父进程中返回子进程的PID,并且在子对象中返回0。如果失败,在父进程中返回-1,没有创建子进程,并且errno被适当地设置。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
//while(1){
// printf("process!process ID:%d,ppid:%d\n",getpid(),getppid());
// sleep(1);
//}
printf("AAA;pid:%d;ppid:%d\n",getpid(),getppid());
fork(); //创建子进程
printf("BBB;pid:%d;ppid:%d\n",getpid(),getppid());
sleep(1);
return 0;
}
这里我们发现,在AB行B行被重复打印,原因就在于fork()创建了;观察B的pid和ppid可以发现,这两行其实是父子关系,下面一行B其实是上一行B的子进程。
🌟验证返回值
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
//while(1){
// printf("process!process ID:%d,ppid:%d\n",getpid(),getppid());
// sleep(1);
//}
printf("AAA;pid:%d;ppid:%d\n",getpid(),getppid());
pid_t ret = fork(); //创建子进程
printf("BBB;pid:%d;ppid:%d,ret = %d,&ret:%p\n",getpid(),getppid(),ret,&ret);
sleep(1);
return 0;
}
可以发现,给父进程返回子进程PID,给子进程返回0;那么问题来了:
一个函数为何会有两个返回值?
怎么可能一个变量(相同地址)其读取的数据内容不同?
示例:
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
int main(){
//while(1){
// printf("process!process ID:%d,ppid:%d\n",getpid(),getppid());
// sleep(1);
//}
//printf("AAA;pid:%d;ppid:%d\n",getpid(),getppid());
pid_t ret = fork(); //创建子进程
//printf("BBB;pid:%d;ppid:%d,ret = %d,&ret:%p\n",getpid(),getppid(),ret,&ret);
assert(ret != -1);
if(ret == 0){
//子进程
while(1){
printf("我是子进程;pid:%d;ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
else if(ret >0){
while(1){
printf("我是父进程;pid:%d;ppid:%d\n",getpid(),getppid());
sleep(2);
}
}else{}
sleep(1);
return 0;
}
这都没有问题,但是仔细想想,这并不符合我们的认知:
if
语句与else if
是不能同时成立的- 一个程序里怎么会有两个while在同时执行呢?
1. fork
之后,执行流会变成2个执行流
2. fork
之后,谁先运行由调度器决定
3. fork
之后,fork之后的代码共享,通常我们通过if
和else if
来进行执行流分流
📝原理解析
进程在运行时,是具有独立性的!
进程 = 内核数据结构 + 进程的代码和数据
父子进程在运行时,也是一样的,具有独立性
代码:只读
数据:当有一个执行流尝试修改数据的时候,OS自动给我们的进程触发 写时拷贝
当父进程退出时,子进程会自动被OS领养(通过让1号进程成为新的父进程)————被领养的进程————孤儿进程
杀死进程:
kill -9 进程PID