进程及fork创建子进程
LINUX学习笔记
1. 进程概念
程序:只占用磁盘空间,死的
进程:运行起来的程序。占用内存、cpu等系统资源,是活的程序
2. 并发概念
并发,在操作系统中,一个时间段中有多个进程都处于启动运行到运行完毕之间的状态,但,任一个时刻点上仍只有一个进程在运行
原因就是cpu性能太猛了,通过时钟滴答方式,在进程间切换进而实现看起来并发的效果
2.1 单道程序设计
所有进程一个一个排队执行。若A阻塞,B只能等待。(DOS系统)
2.2 多道程序设计
在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行。多道程序设计必须有硬件基础作为保证。
时钟中断(时钟滴答)! 一个时间片后强制让进程让出cpu,进而在多道程序设计模型中,多个进程轮流使用CPU(分时复用CPU资源)
本质上!!!并发是宏观的并行,微光的串行!!!!!
3. MMU(Memory Management Unit, 内存管理单元)
MMU 实现了虚拟内存与物理内存的映射!!!!
每个进程在运行时,将分配0~4G的虚拟内存,通过MMU映射到真实的内存地址上
4. PCB(进程控制块)
每个进程在内核中(3G~4G虚拟空间中)都有一个进程控制块(PCB)来维护进程相关的信息,PCB本质是 task_struct 结构体
- 进程id
- 进程状态(初始态、就绪态、运行态、挂起态、终止态)5种/4种(将初始和就绪合并)
- 进程切换需要保存和恢复的一些CPU寄存器
- 描述虚拟地址空间的信息
- 描述控制终端的信息
- 当前工作目录
- umask掩码
- 文件描述符
- 信号相关信息
- 用户id和组id
- 会话和进程组
- 进程可以使用的资源上限
5. fork 创建子进程
5.1 头文件包含及函数声明
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
5.2 pid_t fork(void)
创建子进程。父子进程各自返回。
成功:父进程返回子进程pid,子进程返回0
失败:父进程返回-1,子进程没有创建,设置errno
5.3 getpid、getppid
pid_t getpid(void); 返回当前进程id
pid_t getppid(vodi); 返回当前进程的父进程id
5.4 demo
/* fork.c */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.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(0);
}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;
}
成功创建一个子进程,并和父进程各自打印各自的进程id和父进程id
6. 循环创建n个子进程 (demo loop_fork.c)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
int main(int argc, char *argv[])
{
int i;
pid_t pid;
for(i=0; i<5; i++)
{
if((pid = fork()) == 0)
break;
}
if(i == 5)
{
//sleep(5); 防止父子进程抢占CPU
printf("I'm parent \n");
}
else if(i<5)
{
//sleep(i);
printf("I'm %dth child\n",i+1);
}
return 0;
}
结果!!! 会出现父子进程抢占CPU的情况,且bash会先一步打印进程工作目录
fork之后父进程先执行还是子进程先执行不确定。取决于内核所使用的调度算法。
出现子进程和父进程争夺cpu的情况,打印顺序会打乱
会出现子进程没有争过bash的情况
解决:
sleep( )
延迟调用线程,会被挂起设定的时间time,time后会被继续调用,但是需要看系统操作是否繁忙。
7. 父子进程异同
父子进程相同:
刚fork之后,data段,text段,堆,栈,环境变量,全局变量、宿主目录位置、进程工作目录位置、信号处理方式
父子进程不同:
进程id、返回值、各自的父进程、进程创建的时间、闹钟、未决信号集
父子进程共享:读时共享,写时复制 !!!
- 文件描述符(打开文件结构体)
- mmap建立的映射区(进程间的通信详解)
7.1 demo 父子进程间的共享(全局变量)
/* fork_shared.c */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>
int var = 100;
int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0)
{
perror("fork error");
exit(1);
}
else if(pid > 0)
{
var = 288;
printf("parent,var = %d\n", var);
printf("I'am parent pid=%d,getppid = %d\n",getpid(), getppid());
}
else if(pid == 0)
{
var=200;
printf("child,var = %d\n", var);
printf("I'am child pid = %d,ppid = %d\n", getpid(), getppid());
}
printf("--------finish------------\n");
}
读时共享,写时复制!!!,父子进程全局变量不同
8. gdb 调试
使用gdb调试的时候,gdb只能跟踪一个进程。可以在fork函数 调用之前,通过指令设置gdb调试工具跟踪父进程或者时跟踪子进程。默认跟踪父进程
set follow-fork-mode child 命令设置gdb在fork之后跟踪子进程
set follow-fork-mode parent 设置跟踪父进程
以 loop_fork.out 为例:
调试子进程: