进程篇
一、进程相关概念
1.什么是程序,什么是进程,有什么区别?
1)程序是静态的概念,gcc xxx.c -o pro,磁盘中生成的pro文件,叫做程序。
2)进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程。
2.如何查看系统中有哪些进程?
1)使用ps指令查看,实际工作当中,配合grep来查找程序中是否存在某个进程。比如:ps -aux|grep init 过滤掉其他进程,只显示init进程。
2)使用top指令查看,类似windows任务管理器。
3.什么是进程标识符?
1)每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证。
pid = 0:称为交换进程(swapper)
作用——进程调度
pid = 1: init进程
作用——系统初始化
2)编程调用getpid函数获取自身的进程标识符。
getppid获取父进程的进程标识符。
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
int main()
{
//pid_t getpid(void);获取进程id号函数原型。
pid_t pid;
pid = getpid();
printf("此程序进程id号为:%d\n",pid);
while(1);
return 0;
}
运行结果:
4.什么叫父进程,什么叫子进程?
进程A创建了进程B,那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。
5.C程序的储存空间是如何分配的?
1)当一个程序运行的时候,内存空间会给该运行程序画出一块地址,上诉就是内存给程序画出的地址。
2)那么这块地址是怎么分配的呢?
正文段
这是CPU执行的技巧指令部分。通常,正文段是可共享的,所以即使是频繁执行的程序(如文本编辑器,C编译器和shell等)在存储器中也只需要一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其指令。
初始化数据段
通常将此段数据成为数据段,它包含了程序中需明确地赋初值的变量,例如。C程序中任何函数之外的声明: int maxcount = 99;
未初始化数据段
通常将此段称为bss段,这一名称来源于早期汇编程序一个操作符,意思是“由符号开始的块”(block started by symbol),在程序开始执行前,内核将此段终端数据初始化为0或空指针。函数外的生命:
long sum[1000];
使此变量存放在非初始化数据段中。
栈
自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址以及调用者的环境信息(如某些技巧寄存器的值)都存放在栈中。然后,最近调用的函数站上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C递归函数可以工作。递归函数每次调用自身时,就用一个新的栈帧,因此一次函数调用实例中的变量集不会影响另一次函数调用实例中的变量。
堆
通常在队中进行动态村粗分配。由于历史上形成的惯例,堆位于未初始化数据段和栈之间。
二、创建进程函数fork的使用
1.进程创建实战
使用fork函数创建一个进程,函数原型:
pid_t fokr(void);
fork函数调用成功,返回两次
返回值是 0,代表当前进程是子进程
返回值是非负数,代表当前进程是父进程
调用失败,返回 -1
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid = getpid();
printf("fork之前是父进程为:%d\n",pid);
retpid = fork();
pid2 = getpid();
printf("fork之后进程为:%d\n",pid2);
if(retpid > 0 && pid == pid2){
printf("父进程走的,fork的返回值是:%d 当前进程的pid:%d\n",retpid,getpid());
}else if(retpid == 0 && pid != pid2){
printf("子进程走的,fork的返回值是:%d 当前进程的pid:%d\n",retpid,getpid());
}
return 0;
}
运行结果:
注意:
- 第一段红色框,是父进程走的,并且走完了整个代码段,然后根据父进程跟子进程不同的性质返回了不一样的值,父进程对于fork的返回值是子进程的进程ID号
- 第二段绿色框,是子进程走的,只是走了fork之后包括fork的代码,然后根据子进程和父进程的性质不同返回了不一样的值,子进程对于fork的返回值是0
三、进程创建发生了什么事情
1.来看一段代码
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
int main()
{
pid_t pid;
int data = 10;
printf("此程序进程id号为:%d\n",getpid());
pid = fork();
if(pid > 0 ){
printf("这里是父进程:%d\n",getpid());
}else if(pid == 0){
printf("这里是子进程:%d\n",getpid());
data = data + 100;
}
printf("data的值为:%d\n",data);
return 0;
}
运行结果:
1)父进程里面的data = 10始终都没有变,而子进程里面的data = 10变成了110。
2)所以是发生了这样的事情:
- 在早期的linux都是这样做的,fork创建新进程后是完全拷贝,新进程会把旧进程的所有内存数据都拷贝一份给自己使用。
- 后来linux内核更新,为了提高效率,for创建新进程后是写时不完全拷贝,对旧进程和新进程始终不改变的值采用共享内存的方式。
四、创建新进程的实际应用场景及fork总结
1.fork创建一个子进程的一般目的:
1)一个父进程希望复制自己,是父,子进程同时执行不同的代码段。这在网络服务进程中是常见的———父进程等待客户端的服务请求。但这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等下下一位服