1. 进程的概念
概念:加载到内存的程序/正在运行的程序称为内存。
我们在玩电脑的时候是可以启动多个程序的,比如边听歌边写博客,根据上篇文章我们知道肯定要将多个.exe文件加载到内存中,作为操作系统肯定是要管理这多个加载到内存的程序。如何管理呢?六个大字,先描述再组织。
2. 进程的描述
进程的信息被放在一个叫进程控制块(process control block)PCB,可以理解成一个进程属性的集合。
linux描述一个进程的结构体叫task_struct(PCB的一种)。该结构体有非常多的字段,我们挑几个常见的字段。
struct task_struct
{标识符:唯一标识符,用于区别其他进程状态:任务状态、退出代码、退出信号优先级:相对于其他进程的优先级程序计算器:程序中即将被执行的指令的地址上下文数据:进程执行时处理器的寄存器中的数据I/O状态:包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等....
}
3. 进程的组织
将多个进程描述成多个PCB结构体,再将各个PCB链接起来,所以对进程的管理就转化成对PCB结构体的管理。
所以一个进程等于:可执行程序(.exe)+内核数据结构(PCB)
4. 查看一个进程
使用ps指令来查看一个指令,下面是一个程序的代码,用于测试
int main()
{while(1){printf("I am a process!!!\n");sleep(1);}return 0;
}
ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep
PID就是上面说的标识符,可以使用系统函数getpid()获得,PPID则是父进程的标识符,可以使用getppid()。
int main()
{while(1){printf("I am a process!!! pid:%d ppid:%d\n",getpid(),getppid()");sleep(1);}return 0;
}
除了用ps指令查看一个进程,linux还将进程的信息存放在了一个叫proc的文件夹中。
我们来单独查看一个进程(PID为4630)是什么样的
5. 创建一个进程
运行一个程序就相当于创建一个进程,那么能用代码的形式创建一个进程吗?fork()函数就可以解决这个问题。
我们先来看一段代码:
1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 5 int main()6 {7 printf("before fork:pid: %d ppid: %d\n",getpid(),getppid());8 fork();9 printf("after fork:pid: %d ppid: %d\n",getpid(),getppid()); 10 return 0;11 }
可以看到fork之后有两个进程了,且它们是父子关系,fork()之后的代码共享。我们在来看看这个函数的原型。
再来看一段代码
1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 5 int main()6 {7 printf("before fork:pid: %d ppid: %d\n",getpid(),getppid());8 pid_t id = fork();9 printf("after fork:pid: %d ppid: %d\n returnid: %d",getpid(),getppid(),id); 10 11 sleep(2); 12 return 0; 13 }
从结果可以看出,如果是父进程fork的返回值是子进程的pid,而子进程的fork返回值是0,我们可以通过这个特性很好的分流执行代码。
1 #include <stdio.h>2 #include <sys/types.h>3 #include <unistd.h>4 5 int main()6 {7 printf("before fork:pid: %d ppid: %d\n",getpid(),getppid());8 pid_t id = fork();9 if(id < 0)10 return -1;11 if(id == 0)12 {13 while(1)14 {15 printf("after fork, 我是子进程:pid: %d ppid: %d returnid: %d\n",getpid(),getppid(),id);16 sleep(2);17 }18 }19 else20 {21 while(1)22 {23 printf("after fork,我是父进程:pid: %d ppid: %d returnid: %d\n",getpid(),getppid(),id);24 sleep(2); 25 }26 }27 28 sleep(2);29 return 0;30 }
看完简单的使用之后,我们再来思考三个问题?
fork为什么给父进程返回子进程的pid,而给子进程返回0?因为父进程有很多子进程,难以找到某个子进程,所以返回子进程的pid,但是子进程只有一个父进程,使用getppid就能得到父进程的pid了。
为什么一个函数会返回两个值?因为在fork这个函数内部,return语句之前的代码执行完之后,已经创建了子进程,所以会共享代码,return语句就执行了两遍。
为什么一个变量会有两个值?这个问题与进程地址空间有关,后面才会理解。