一、进程的相关概念
进程和程序:
我们平时写好的代码,通过编译后生成的可执行文件就是一个程序,不占用系统资源(cpu、内存、打开的文件、设备、锁....),当这个程序被运行起来后(没结束之前) 它就成为了一个进程,进程是活跃的程序,占用系统资源,在内存中执行
程序:静态的,即死的。只占用磁盘空间。 ——剧本
进程:动态的,即活的。运行起来的程序。占用内存、cpu等系统资源。 ——戏
进程的状态:
在三态模型中,进程状态分为三个基本状态,即:运行态、就绪态、阻塞态;
在五态模型中,进程状态分为五个基本状态,即:初始态、就绪态、运行态、挂起态、终止态;
其中初始态为进程准备阶段,常与就绪态结合来看。
PCB进程控制块:
进程id
文件描述符表
进程状态: 初始态、就绪态、运行态、挂起态、终止态。
进程工作目录位置
*umask掩码 (进程的概念)
信号相关信息资源。
用户id和组id
ps aux 返回结果里,第二列是进程id
环境变量:
echo $PATH 查看环境变量
path环境变量里记录了一系列的值,当运行一个可执行文件时,系统会去环境变量记录的位置里查找这个文件并执行。
echo $TERM 查看终端
echo $LANG 查看语言
env 查看所有环境变量
二、进程控制
1. fork创建子进程
系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。
fork函数原理:
fork之前的代码,父子进程都有,但是只有父进程执行了,子进程没有执行,fork之后的代码,父子进程都有机会执行。
pid_t fork(void);
功能:
用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。子进程是父进程的一个几乎完全独立的副本,包括内存空间、文件描述符、环境变量等,子进程获得的是这些资源的复制品,所以原进程和新进程的资源是相互独立的。
参数:
无
返回值:
成功:子进程中返回0,父进程中返回子进程ID(PID)
失败:返回-1
失败原因:
通常是因为系统级别错误(如可用进程数量达到限制)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{printf("before fork-1-\n");printf("before fork-2-\n");printf("before fork-3-\n");printf("before fork-4-\n");pid_t pid = fork();if (pid == -1){perror("fork error");exit(1);}else if(pid == 0){printf("---child is created\n");}else if(pid > 0){printf("---parent process:my child is %d\n",pid);}printf("============end of file\n");return 0;
}
输出如下:
before fork-1-
before fork-2-
before fork-3-
before fork-4-
---parent process:my child is 2332517
============end of file
---child is created
============end of file
循环创建多个子进程:
通过命令行参数指定创建进程的个数,每个进程休眠1S打印自己是第几个被创建的进程。如:第1个子进程休眠0秒打印:“我是第1个子进程”;第2个进程休眠1秒打印:“我是第2个子进程”;第3个进程休眠2秒打印:“我是第3个子进程”。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{int i;pid_t pid;for (i = 0;i < 5;i++){ if (fork() == 0) //循环期间,子进程不forkbreak;}if (5 == i){ sleep(5);printf("I'm parent\n");}else{ sleep(i);printf("I'm %dth child\n",i+1);}return 0;
}
输出如下:
I'm 1th child
I'm 2th child
I'm 3th child
I'm 4th child
I'm 5th child
I'm parent
2. getpid和getppid
每个进程都由一个进程号来标识,其类型为pid,进程号的范围:0~32767,进程号总是唯一的,但进程号可以重用,当一个进程终止后其进程号就可以再次使用;
进程号(PID):标识进程的一个非负整形数
父进程号(PPID):任何进程都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID),如 A进程创建了B进程,A的进程号就是B进程的父进程号
进程组号(PGID):进程组是一个或多个进程的集合,它们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID),默认情况下 当前的进程号会当作当前的进程组号
pid_t getpid(void);
功能:
获取当前进程号(PID)参数:
无返回值:
本进程号pid_t getppid(void); 获取当前进程的父进程ID
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{printf("before fork-1-\n");//在fork之前打印,父进程执行,只执行一次printf("before fork-2-\n");printf("before fork-3-\n");printf("before fork-4-\n");pid_t pid = fork();//创建子进程if (pid == -1){perror("fork error");exit(1);}else if(pid == 0){ //子进程printf("---child is created,pid=%d,parent-pid:%d\n",getpid(),getppid());}else if(pid > 0){ //父进程printf("---parent process:my child is %d,my pid:%d,my parent pid:%d\n",pid,getpid(),getppid());}printf("============end of file\n");//父子进程各执行一次return 0;
}
输出如下:
before fork-1-
before fork-2-
before fork-3-
before fork-4-
---parent process:my child is 2328447,my pid:2328446,my parent pid:2272084
============end of file
---child is created,pid=2328447,parent-pid:2328446
============end of file
父进程先结束,导致子进程成孤儿,于是回收到孤儿院,看起来合情合理。
修改一下代码,给父进程增加一个等待命令,这样能保证子进程完成时,父进程处于执行状态,子进程就不会成孤儿。同时,这里也解决了终端提示符和输出混在一起的问题。代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>int main(int argc,char *argv[])
{printf("before fork-1-\n");//在fork之前打印,父进程执行,只执行一次printf("before fork-2-\n");printf("before fork-3-\n");printf("before fork-4-\n");pid_t pid = fork();//创建子进程if (pid == -1){perror("fork error");exit(1);}else if(pid == 0){ //子进程printf("---child is created,pid=%d,parent-pid:%d\n",getpid(),getppid());}else if(pid > 0){ //父进程sleep(1);printf("---parent process:my child is %d,my pid:%d,my parent pid:%d\n",pid,getpid(),getppid());}printf("============end of file\n");//父子进程各执行一次return 0;
}
输出如下:
before fork-1-
before fork-2-
before fork-3-
before fork-4-
---child is created,pid=2338015,parent-pid:2338014
============end of file
---parent process:my child is 2338015,my pid:2338014,my parent pid:2272084
============end of file
三、进程共享
父子进程之间在fork后共享哪些内容:
父子进程相同:
全局变量、data段、text段、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式
父子进程不同:
进程ID、fork返回值、父进程ID、进程运行时间、闹钟(定时器)、未决信号集
父子进程共享:
读时共享、写时复制 ----------------- 全局变量
1.文件描述符(打开文件的结构体) 2. mmap映射区(进程间通信)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int var = 100;int main(int argc,char* argv[])
{pid_t pid;pid = fork();if (pid == -1){perror("fork error");exit(1);}else if (pid > 0){var = 288;printf("parent, var = %d\n",var);printf("I'm parent pid = %d,getpid = %d\n",getpid(),getppid());}else if(pid == 0){var = 200;printf("I'm child pid = %d,ppid = %d\n",getpid(),getppid());printf("child,var = %d\n",var);}printf("------------finish-------------\n");return 0;
}
输出如下:写时复制
parent, var = 288
I'm parent pid = 2348490,getpid = 2272084
------------finish-------------
I'm child pid = 2348491,ppid = 2348490
child,var = 200
------------finish-------------
将var = 200进行注释,输出如下: 读时共享
parent, var = 288
I'm parent pid = 2350816,getpid = 2272084
------------finish-------------
I'm child pid = 2350817,ppid = 2350816
child,var = 100
------------finish-------------