[Linux]进程 / PID
一、认识进程 --- PCB
写一个死循环程序执行起来,观察进程
ps ajx 显示所有进程
用分号可以在命令行的一行中执行多条指令,也可以用 && :
ps ajx | head -1 && ps ajx | grep proc
终止掉进程后再查看:
所以 ./proc 就是死循环程序运行的进程
grep --color=auto proc 之所以一直都在,是因为命令行指令执行时也是一个进程
ps ajx | grep proc 执行时,grep 指令也变成了一个进程,查询结果自然就会包含在内
可以用 grep 的 -v 选项反向搜索来不显示这个进程:
进程可以分为两种:一是执行完就退出的,例如指令,二是用户不关就不退的,被称为常驻进程,例如杀毒软件。
二、进程属性 --- task_struct 内容分类
1. PID
PID 是进程的标示符,用于唯一标识一个进程。
可以用 getpid() 来获取进程的 PID:(需要 sys/types.h 头文件)
2.kill
kill 是一条进程相关的指令,有许多选项:
通过 -9 或 -SIGKILL 可以关闭一个进程:
kill -9 <PID>
3. /proc
Linux 中一切皆文件,所以进程的属性也以文件的形式可供用户查看
这里以数字命名的每个目录都代表着一个进程,里面的文件都是这个进程相关的属性
其中:
(1)exe
exe 是一个链接文件,标识了这个进程来源于哪个可执行程序
如果我们删掉 proc 文件,就会变成:
注意此时进程之所以还在进行,还有这个进程的目录,是因为我们的删除操作删除掉的是磁盘中的 proc 文件,而进程是内存级的,只要不停止运行就会一直在。
(2)cwd
current work directory “当前工作目录”的缩写
进程刚刚创建时,会用自己的 cwd 属性记录下程序所在的目录作为默认的“当前路径”
如果我们在程序中用 fopen 打开一个新 log.txt 文件,运行后就会在当前路径下创建出一个 log.txt 文件。这个“当前路径”,就来自于进程的 cwd 属性。
而且这个文件被新建时,创建的路径使用的是绝对路径,是用进程的 cwd 和文件名拼接成的:/home/mmr/linux-c/par02/log.txt
想要改变进程的 cwd,可以用 chdir()
就可以把进程的 cwd 更改为根目录,log.txt 也就可以新建在根目录下了(注意普通用户没有根目录的写权限,所以普通用户会创建失败)
(3)/proc是内存级文件
/proc 并不存储在硬盘当中,关机时整个文件被释放掉,不会存储。
4.PPID
在 Linux 系统中,系统启动之后,新创建的任何进程,都是由自己的父进程创建的。
PPID 就是父进程的 PID 。
可以用 getppid() 来获取进程的 ppid
可以看到 ppid 一直是相同的,我们查看一下:
可以看到这个进程是 bash,叫做命令行解释器,是 Linux 系统的shell 外壳
命令行中,执行指令、执行程序,本质都是 bash 的进程,创建的子进程,由子进程来执行代码
用户每一次登录,系统都会为用户创建一个 bash 进程,这里 bash 前有一个 "-",代表当前这个用户是使用命令行终端进行登录的。
三、使用系统调用,创建进程 --- fork
我们运行一下:
可以看到 child proc 打印了两遍。
这是因为 fork 创建出子进程后,原先的进程和子进程都要运行。原先的进程的父进程是 bash,子进程的父进程是原先的进程。
这两个进程先后连续创建的,所以 pid 也是连续的。
由 fork 的返回值可以知道,如果子进程创建成功,那么在原先的进程中,获得的返回值是子进程的 pid,而子进程中获得的 fork 返回值是 0
fork 创建的子进程,与其父进程共享同一份代码,但是数据是各自私有一份的,互不干扰。
父进程的代码,是由硬盘加载进内存后运行的,而子进程的代码,是直接共享自父进程的,而非从硬盘中加载得来。
所以说,进程具有很强的独立性。
多个进程之间,运行时,是互不影响的。
四、创建多个进程
1. C++
Linux 中 C++ 可以使用 .cpp .cc .cxx 作为后缀
使用 g++ -o <程序名> <源文件名> 生成可执行程序
如果使用了C++11中的语法:
2.创建多个进程
为什么这里子进程的 PPID 是 1 ?
因为我截这个图时父进程已经执行完毕挂掉了,此时子进程的 PPID 就不会是原先的父进程,会被 init 进程,托管给 1 号进程。
五、再理解创建子进程
1. fork 函数为什么有两个返回值
在 fork 函数体内部,先是父进程在走,在走的时候,子进程被创建出来,因为父子进程是共享同一份代码的,所以这时候就是父子进程一起在走 fork 函数。
当 fork 函数运行到最后的 return 语句时,父子进程各自执行一次 return,父进程返回父进程的值,子进程返回子进程的值。又因为父子进程的数据是各自私有一份的,所以即使返回值不同也互不干预,不会有任何后果。
因此虽然一个函数有两个返回值,但其实并不冲突。
2. fork之后,父子谁先运行
fork之后,父子进程谁先运行是不确定的,是由OS的调度器自主决定的。