1.进程的启动
1.把程序运行起来,双击或 ./exe(可执行程序),本质就是再系统中启动了一个进程
2.在Linux下命令行输入的大多数指令本质上就是启动一个进程,但这个进程很快被cpu运行完,如:ll , pwd whoami等指令
2.pid标识符
pid相当于是一个进程的编号,每个进程都有一个属于自己的pid,但是进程每次启动关闭后,进程pid也会发生变化。
上面是一段死循环程序,启动后可以演示一直运行的进程,
使用指令:ps axj | head -1 ; ps axj | grep myproc 。可以查看我们想看见的进程
ps axj 表示查看所有正在执行的进程,head -1 表示显示第一行 ,grep myproc表示想要看到的进程。
关闭这个死循环进程后使用该指令,会发现还有一个进程,是因为grep自己也是一个进程,默认把自己也过滤出来,如果不想看见就使用指令:ps axj | head -1 ; ps axj | grep myproc | grep -v grep
反向匹配grep,就不会显示
3.如何获取pid
使用getpid()系统调用,获得进程的标识符
这里返回值pid_t其实系统给我们封装的一个类型,其实就是一个long int的封装。
演示:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 pid_t id = getpid();
8 while(1)
9 {
10 printf("hello bit,I am a process ,pid : %d\n",id);
11 sleep(1);
12 }
13 return 0;
14 }
关闭进程:ctrl+c 或者 kill+信号+pid
kill对指定进程发送信号,信号如下:
在这里我们只需要认识9,意思是杀死一个进程,如果要杀死上面代码进程:kill -1 15325
Linux下万物皆文件,在根目录下有个文件proc,但proc不是磁盘级别的文件,是把内存中数据以文件形式存储下来。
在proc目录中存放了以数字命名的文件,这个数字其实就是指定进程的pid
然后每个文件里面包含的就是每个进程的所有属性,当进程启动的时候,proc文件夹中就会迅速创建一个以该进程pid命名的文件,并且把该进程属性存入该文件中
关闭进程后proc就会把该文件删除
4.理解cwd(当前工作目录)
cwd 是current work dir 缩写
进入文件查看属性
这里要认识两个属性:cwd exe
exe就是该可执行程序在磁盘中的位置,如果该可执行程序正在进行时,把该可执行程序删除,此时该程序还在运行,因为该程序已经加载进内存,删除只是删除该程序在磁盘中的位置,所有该程序还在运行,但此时这个exe属性就会变
cwd就是当前工作目录
当前路径就叫做进程的cwd
如果想主动更改当前工作路径,用函数chdir(路径);
不能更改到根目录,因为没写的权限
使用chdir更改当前工作路径
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 chdir("/home/xiazelin/112");
8 FILE* fp = fopen("log.txt","w");
9 if(fp==NULL)
10 {
11 perror("fopen");
12 return 1;
13 }
14 pid_t id = getpid();
15 while(1)
16 {
17 printf("hello bit,I am a process ,pid : %d\n",id);
18 sleep(1);
19 }
20 return 0;
21 }
如上代码,更改默认路径
5.认识ppid
一个进程启动时,除了要有自己的pid,也要有自己的ppid(即父进程pid)。
在Linux系统中,启动之后,新创建的任何进程的时候,都是由自己的父进程创建的!
下面代码获取pid与ppid
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 //chdir("/home/xiazelin/112");
8 //FILE* fp = fopen("log.txt","w");
9 //if(fp==NULL)
10 //{
11 // perror("fopen");
12 // return 1;
13 //}
14 pid_t pid = getppid();
15 pid_t id = getpid();
16 while(1)
17 {
18 printf("hello bit,I am a process ,pid: %d , ppid: %d\n",id,pid);
19 sleep(1);
20 }
21 return 0;
22 }
我们启动进程关闭进程又启动发现我们父进程(ppid)不变,pid会改变
我们再查看 11424是谁?下面可以看到11424叫做-bash,bash就是命令行解释器---shell
可以得出结论:在命令行中,执行命令/执行程序,本质就是-bash进程,创建的子进程,由子进程执行我们的代码,与指令。
6.使用系统调用,创建进程
返回值:成功,在父进程中返回子进程的pid,子进程返回0;失败,返回-1给父进程,没有子进程创建。
1.见一见子进程创建
运行结果:
经过fork函数后,会多产生一条打印,根据打印结过发现,第三行的ppid就是第二行的pid,第二行就是第三行的父进程,所有,经过fork,两条分支,一个为父进程,一个为子进程,父进程就是这串代码进程自己,子进程就是父进程创建出来新的执行流。
Linux下所有的进程也是树形结构!
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int main()
6 {
7 printf("我已经是一个进程! pid: %d,ppid: %d\n",getpid(),getppid());
8 pid_t id = fork();
9
10 if(id>0)
11 {
12 while(1)
13 {
14 printf("我是父进程,pid: %d,ppid: %d,ret id: %d\n",getpid(),getppid(),id);
15 sleep(1);
16 }
17 }
18 else if(id==0)
19 {
20 while(1)
21 {
22 printf("我是子进程,pid: %d,ppid: %d,ret id: %d\n",getpid(),getppid(),id);
23 sleep(1);
24 }
25 }
44 return 0;
45 }
从fork()往后就有两个进程,这两个进程各自执行各自代码,给父进程返回子进程pid,因为父进程要拿到每一个孩子的pid方便管理。给子进程返回0,因为一个孩子都只有一个父亲,所有不需要拿到很明显的标识符,只要保证自己被创建就可以,(父子比例:1:n,需要给父进程返回每个子进程的pid值,方便管理子进程)
2.快速解释函数特点,返回值上,不做理解的介绍
父子:1:n
如何理解一个函数有两个函数?
fork()->两个进程-->父子关系-->一般而言,代码是共享的,但是数据是各自私有一份的。
1.怎么理解代码是共享的呢?如下图:
、
可执行程序从磁盘中加载到内存中,就相当于代码和数据加载到内存,同时操作系统为了管理·这个进程,也会为它创建对应PCB(父),对应PCB指向代码,后来CPU调度进程当他执行到函数fork()时,所有在系统层面上创建一个新的进程(子),新创建的子进程并没有从磁盘中加载新的代码和可执行程序让子进程跑,也就是这个子进程创建出来,只有内核数据结构没有代码和数据,可是这个子进程创建出来不就是让他帮忙办事的吗?所以系统的设定上就直接要求子进程在创建时指向父进程的代码,也就是让子进程与父进程共享代码,因为子进程并没有做加载。子进程的代码是从父进程继承下来的。
fork下来后printf打两次就是因为子进程继承父进程的代码。
2.为什么数据是各自私有一份的
进程具有很强的独立性!多个进程之间,运行时,互不影响,即便是父子(比如说:手机上多个进程启动时,其中一个挂了,剩余进程依旧可以正常使用)。
当成结论:
证明:设置一个全局变量gval,看结果:父子进程数据互不影响,相互独立!
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4
5 int gval = 0;
6
7 int main()
8 {
9 printf("我已经是一个进程! pid: %d,ppid: %d\n",getpid(),getppid());
10 pid_t id = fork();
11
12 if(id>0)
13 {
14 while(1)
15 {
16 printf("我是父进程,pid: %d,ppid: %d,ret id: %d,gval: %d\n",getpid(),getppid(),id,gval);
17 sleep(1);
18 }
19 }
20 else if(id==0)
21 {
22 while(1)
23 {
24 printf("我是子进程,pid: %d,ppid: %d,ret id: %d,gval: %d\n",getpid(),getppid(),id,gval);
25 gval++;
26 sleep(1);
27 }
28 }
47 return 0;
48 }
1.id是不是变量? 是变量!
2.返回的本质,不就是像指定变量写入返回数据吗?全局变量的值打印出来,父子进程都互不影响,所以父进程返回值与子进程返回值互不影响。
3.打印的本质,就是读取
所以为什么if与else if同时成立?
因为父子进程共享代码,fork之后,父进程改自己的id,子进程改自己的id,互不影响,各自运行。
3.创建多进程--demo
1 #include<iostream>
2 #include<vector>
3 #include<sys/types.h>
4 #include<unistd.h>
5 using namespace std;
6
7 const int num = 10;
8
9 void SubprocessRum()
10 {
11 while(true)
12 {
13 cout<<"I am sub process,pid: "<<getpid()<<",ppid: "<<getppid()<<endl;
14 sleep(1);
15 }
16 }
17 int main()
18 {
19 vector<pid_t> allchild;
20 for(int i = 0;i<num;i++)
21 {
22 pid_t id = fork();
23 if(id==0)
24 {
25 //子进程;
26 SubprocessRum();
27 }
28 //父进程
29 allchild.push_back(id);
30 }
31 cout<<"我的所有的孩子是:";
32 for(auto child: allchild)
33 {
34 cout<<child<<" ";
35 }
36 cout<<endl;
37
38 while(true)
39 {
40 cout<<"我是父进程,pid:"<<getpid()<<endl;
41 sleep(1);
42 }
43 return 0;
44 }
4.理解创建子进程---结合系统接口上
一个函数fork,怎么会有两个返回值?
一个变量为什么会有不同值呢?因为进程之间需要独立性(后续说怎么做到的)
fork之后,父子进程谁先运行,由OS的调度器自主决定