目录
启动进程
查看进程
方法1:/proc
方法2:查看脚本
方法3:系统调用获取进程标示符❗❗
终止进程
创建进程(主fork)
🙂查看父子进程的pid
🙂进程创建/执行/终止
🙂多次重新启动进程查看pid和ppid
🙂系统调用函数fork创建子进程
🙂why❓fork有两个返回值
🙂父子进程执行不一样的代码
进程的当前工作路径
从本篇开始陆续介绍Linux中进程的task_struct本省内部的属性。
启动进程
- ./ 本质上就是让系统创建进程并运行。
- myprocess.c是C语言源代码
- myprocess是可执行文件(gcc/g++)
- 可执行的程序的可执行文件在磁盘上
- ./运行程序:把可执行程序加载到内存上,OS创建task_struct,进入排队,等待调度。
- 启动程序:就是让系统创建进程并运行。
- 我们自己写的代码形成的可执行程序 == 系统指令(都是可执行程序) == 可执行文件。
- 在Linux中运行的大部分指令操作,本质都是运行程序。
在Linux中启动进程一般make(shell命令行)
在windows启动进程双击(图形化界面)
【Makefile】
1 myprocess:myprocess.c
2 gcc -o $@ $^ -g
3 .PHONY:clean
4 clean:
5 rm -f myprocess
【myprocess.c】
1 #include<stdio.h>
2 #include<unistd.h>//sleep函数包含的头文件
3 int main()
4 {
5 //让这个进程持续运行不停止
6 while(1)
7 {
8 printf("I am a process!\n");
9 sleep(1);
10 }
11 return 0;
12 }
./myprocess
I am a process!
I am a process!
I am a process!
I am a process!
//之后myprocess已经是一个进程
【在/usr/bin/find目录下查看的可执行程序】
file /usr/bin/find
查看进程
怎么查看进程呢?父进程,子进程?
- 进程的信息可以通过 /proc 系统文件夹查看
- 系统调用获取进程的标识符
- 查看脚本
方法1:/proc
如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。
ls /proc/1
ls /proc/
方法2:查看脚本
大多数进程信息同样可以使用top和ps这些用户级工具来获取。
- ps查看当前系统的进程
- axj显示进程的详细信息(可以打乱顺序写)
- ps axj是用户级的工具,可以行输出消息
- grep也是一个可执行程序
- grep可以把数据按照 行过滤的方式输出
ps axj | grep myprocess :grep把得到的进程信息数据,按照行过滤的方式,凡是包含myprocess的关键字全部提取出来。
- head n查看前n行的信息
ps axj | head -1 查看进程信息的属性信息
- grep -v 关键字 就是除了显示除了关键字以外的其他行信息
- -v反向过滤
- 不需要查看grep的进程信息,举例很混乱。
ps axj | grep -v grep
- && 一起查看,两个命令同时进程
- | 管道
- 查阅出来的信息也可能含有grep的进程信息,因为grep也是一个进程。grep这个进程本省也携带了过滤myprocess的这个关键字。
- 周期性循环查看进程脚本
- while :; do ps axj | head -1 && ps axj | grep -v grep | grep process; sleep 1; done
- 清晰的看到进程的创建,执行,中止,状态这一整个连贯操作。
ps axj | grep myprocess
ps axj | head -1
ps axj | head -1 && ps axj | grep 关键字
ps axj | head -1 && ps axj | grep -v 关键字 | grep 关键字
ps axj | head -1 && ps axj | grep -v grep | grep process
//循环查看脚本
while :; do ps axj | head -1 && ps axj | grep -v grep | grep process; sleep 1; done
方法3:系统调用获取进程标示符❗❗
- 进程的pid:每一个进程都要有自己的唯一标识符,叫做进程pid
- 进程的ppid(子进程的父进程的pid)
- pid:process id 表示进程的唯一标识符
- ppid:process parent id
pid和ppid同理- pid的类型是unsigned int,无符号整数被操作系统封装成pid_t的类型
- pid是一个无符号整型的变量(unsigned int)
- 每一个进程都有task_struct,每个进程间的task_struct怎么区分,用pid❗❗
- Linux中PCB就是task_struct,进程 == 任务task
- 用户想要知道一个进程的pid,是不能够直接到操作系统的内核数据结构中去查询的PCB的pid,需要使用操作系统提供的"系统调用接口"去查询
pid的查看
- 使用man指令去查询getpid 和getppid:man pid
- ❓若查询不到,请安装man命令的一个man-pages安装包
- 安装查看:yum install man-pages(安装的时候只能用超级管理员root安装)
- 系统调用函数getpid查看子进程的pid
- 用getppid查看子进程的ppid == 父进程pid
- #include <sys/types.h>
- #include <unistd.h>
理解:当代码预处理,编译,汇编,链接形成可执行程序的文件的时候是在磁盘中存放,这个时候还不是进程,也没有运行一行代码。只有可执行程序加载到内存,OS创建进程启动进程调度进程的时候。进程启动起来,系统调用函数才会获得PCB的pid。
- 操作系统OS没有PCB,操作系统内核有定期的任务。比如刷新,内存管理之类这些有PCB,操作系统OS本身不需要PCB
yum install man-pages //记得指令提权
man pid
man 2 pid
【用系统调用函数查看进程的pid】
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 pid_t id=getpid();
8 //让这个进程持续运行不停止
9 while(1)
10 {
11 printf("I am a process!,pid=%d\n",id);
12 sleep(1);
13 }
14 return 0;
15 }
终止进程
- ctrl+c就是在用户层面终止进程
- kill -9 pid 可以用来直接杀掉进程
创建进程(主fork)
进程创建的代码方式
(这里讲解重点在操作,轻微原理。后面会重谈进程的创建以及其他方法)
- pid_t getpid(void);获得当前进程的pid
- pid_t getppid(void);获得当前进程的父进程的pid
- 父进程VS子进程
- bash是父进程,进程创建进程
- 系统调用函数创建进程
🙂查看父子进程的pid
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 pid_t id=getpid();
8 pid_t parentid=getppid();
9 //让这个进程持续运行不停止
10 while(1)
11 {
12 printf("I am a process!,pid=%d,ppid:%d\n",id,parentid);
13 sleep(1);
14 }
15 return 0;
16 }
🙂进程创建/执行/终止
while :; do ps axj | head -1 && ps axj | grep -v grep | grep process; sleep 1; done
🙂多次重新启动进程查看pid和ppid
❓我们发现每次重新启动进程,进程的pid都要改变,但是进程的父进程pid都不改变。
❓查询父子进程属性信息:发现都有bash进程
- 进程每次启动,对应的pid都不一样是正常的
- 经过验证,我们创建的进程的父进程就是bash
- bash是父进程,是实习生,是李婆,命令行解释器。
- ❗所以进程可以创建进程。
ps axj | head -1 && ps axj | grep -v grep | grep 13185//查询父进程
ps axj | head -1 && ps axj | grep -v grep | grep 12345 //查询子进程随机一个
🙂系统调用函数fork创建子进程
- 创建一个进程,OS内多了进程,多了进程的PCB,内存多了进程的代码和数据。
- 用户想要创建进程,就要创建内核数据结构,用户是没有权力对操作系统内部的内核数据结构等做增删查改的。
- 所以,用户只能使用OS提供的系统调用函数来创建子进程。
- fork:分支,叉子。创建子进程。
- man fork
- 头文件: #include <unistd.h>
- 返回值有两个类型都是pid_t
- 可以接收返回值,也可以不接收。
fork创建子进程的脑中图,理解记忆❗
- 在fork创建了子进程,是由父子进程分别都要执行后序代码,它们共享一份代码。
- 创建一个进程,本质是系统中多一个进程,那么内存也会多一份代码和数据。
- 父进程的代码和数据是从磁盘中加载过来的
- 父进程的PBC也是OS动态创建的
❓子进程的代码和数据,PBC呢?
- 子进程的代码和数据也可以从磁盘再加载一份到内存(下个博文进程控制会讲)
- 在fork这里默认子进程的代码和数据是继承父进程的
- 数据也是继承的吗❓
所以,fork()之后,父子进程代码共享。理论上,子进程是在fork之后共享了父进程的代码和数据,OS才动态创建了子进程的PCB
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 printf("process is running,only me!\n");//只有父进程
8 sleep(3);
9 fork();//创建子进程
10 printf("hello linux\n");
11 sleep(5);
20 return 0;
21 }
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 pid_t id=getpid();
8 pid_t parentid=getppid();
9 printf("process is running,id=%d,parentid=%d\n",id,parentid);//只有父进程
10 sleep(3);
11 fork();//创建子进程
12 printf("hello linux,id=%d,parentid=%d\n",id,parentid);
13 sleep(5);
22 return 0;
23 }
~
🙂why❓fork有两个返回值
🙂父子进程执行不一样的代码
进程的当前工作路径
🙂感谢大家的阅读,若有错误和不足,欢迎指正。