目录
- 进程创建的方式
- 查看进程pid
- 调用系统调用创建子进程
- fock函数做了的工作
- 子进程刚开始创建的状态
- 一个变量,两个不同的值
- 创建子进程的作用
进程创建的方式
1.在操作系统上输入的指令。
2.已经启动的软件。
3.程序员在代码层面上调用系统调用创建进程。
linux中第一个创建的进程是init,操作系统以后的进程创建,就可以依靠该init进程为父进程创建子进程,创建的子进程也可以做为之后进程创建的父进程。所以进程的创建是依靠父进程创建出来的。在使用linux命令行解释器执行指令创建进程中,命令行解释器bash就是父进程。
查看进程pid
top指令查看进程id
也可以ps axj | grep name查看某个名字的进程id
也可以使用系统调用getpid()获取当前运行进程的pid
ls /proc/id -l 查看进程当前目录等信息
每个进程都有自己的id编号pid,所以操作系统管理进程时,就可以找该进程对应的pid编号。
调用系统调用创建子进程
fork()是linux创建子进程的系统调用,返回值是整形,如果返回值为0,就是子进程,如果返回值大于0(父进程创建子进程的子进程pid),就是父进程,如果返回值小于0,创建子进程失败,返回值为-1,错误码被设置。
查看父进程可以调用getppid(),当程序被运行的时候,我们发现两个循环体尽然都有在执行,而且他们的进程pid都不一样,但是子进程的ppid和父进程的pid一样,说明子进程是由父进程创建的。这段代码看着好像只有一个执行流,但是有两个进程。他们在并行地运行,执行。进程的概念给上层用户提高了一个很好的假象,每个程序独自占用处理器的资源,独自占用一个内存资源。
fock函数做了的工作
- fork函数在已有的进程中创建了新的进程,即父进程创建了子进程。所以有两个返回值。
- fork创建成功返回的两个返回值,一个是父进程的调用fork时返回的,一个是子进程的创建的时候返回的。
- fork返回值给父进程为子进程pid,因为一个父进程可以创建多个子进程,父进程实际上要对子进程管理。fork给子进程返回值为0,因为子进程只有一个父进程。
子进程刚开始创建的状态
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{int pid = fork();printf("I am father process,my pid is: %d, my praent ppid is: %d#########\n", getpid(), getppid());printf("I am running: pid :%d ppid:%d\n",getpid(),getppid());if (pid == 0){while (1){printf("I am child process,my pid is: %d, my praent ppid is: %d\n", getpid(), getppid());sleep(1);}}if (pid > 0){while (1){printf("I am father process,my pid is: %d, my Ppraent ppid is: %d\n", getpid(), getppid());sleep(1);}}return 0;
}
子进程创建的时候,是根据父进程为模板创建的,和父进程的代码和数据,内存共享的。创建的子进程连接到运行队列里。因为子进程是根据父进程为模板创建的,继承了父进程的代码和数据。可以看出在fork成功后,子进程也是可以看到fork之前的代码的。
一个变量,两个不同的值
父进程创建子进程后。代码和数据共享指向的内存空间也是一样的。如果有一个全局的变量,父子进程对这个进程操作不同。数据发送了改变,哪个进程先对这个数据改变,操作系统就会申请新的内存给该进程,这是为了保证进程的独立性,互补干扰,让进程有独自的存放空间。代码的全局变量num在不同的进程中,指向不同的指令,一个自增,一个自减,互补干扰。观察到对同一变量num取地址,发现一样,其实这个地址是虚拟地址,并不是真实的物理地址。父子进程对数据改变的话,真实物理地址就会不一样。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
static num = 100;
int main()
{int pid = fork();printf("I am father process,my pid is: %d, my praent ppid is: %d#########\n", getpid(), getppid());printf("I am running: pid :%d ppid:%d\n",getpid(),getppid());if (pid == 0){while (true){printf("I am child process,my pid is: %d, my praent ppid is: %d\n", getpid(), getppid());printf("num = %d,num address: %p\n",num++,&num);printf("#######################################\n");sleep(1);}}if (pid > 0){while (true){printf("I am father process,my pid is: %d, my Ppraent ppid is: %d\n", getpid(), getppid());printf("num = %d,num address: %p\n",num--,&num);printf("#######################################\n");sleep(1);}}return 0;
}
创建子进程的作用
创建子进程本质上是为了和父进程执行不同的任务。所以创建子进程后,一般就会指向新重新加载到内存的代码和数据。