Linux C 中的进程

本文详细介绍了Linux操作系统中的进程概念,包括进程与程序的区别、进程空间问题、虚拟内存管理,以及进程的相关概念如PID、PCB和调度机制。讨论了进程创建函数fork()及其与父子进程的关系,还讲解了进程控制、进程状态、进程命令和相关函数如exit()、_exit()、atexit()。最后,文章涵盖了Linux下的特殊进程处理,如僵尸进程、孤儿进程和守护进程的创建与管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 进程

进程就是一段程序的执行过程。

进程与程序的区别

  1. 程序是静态的,它是保存在磁盘上的指令的有序集合,没有任何执行的概念
  2. 进程是一个动态的概念,它是程序执行的过程,包括进程的创建、调度和消亡
  3. 程序是保存在磁盘上的,而进程是在内存中运行的

2. 进程空间问题

当一个进程运行或者创建时,操作系统会自动为其分配4G的虚拟内存空间(32位操作系统),虚拟内存的使用主要解决了进程间通信问题,保证每一个进程的空间一样,这样在通信的时候,数据交换更加方便。

既然说是分配4G的虚拟内存,那么实际分配时一定不会直接给它4个G的物理内存,而是实际使用多少,就分配多少的物理内存。


2.1 虚拟内存的划分

  • 4G 的虚拟内存分为 1G 内核空间 和 3G 用户空间
  • 1G 的内核空间是 操作系统中所有进程所公有的
  • 3G 的用户空间是进程私有的

进程间通信就是学习如何在内核空间开辟区域。


2.2 虚拟内存和物理内存之间由MMU(内存管理单元)映射管理

在这里插入图片描述

MMU(Memory Management Unit)主要用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权、多任务多进程操作系统。


2.3 虚拟内存划分

虚拟内存划分


2.4 进程进行IO操作示意图

在这里插入图片描述


3. 进程相关概念


3.1 进程号 PID

在操作系统中,每一个进程都有一个编号,该编号就是进程号,进程号是进程的唯一标识
进程号是操作系统给当前进程随机分配的,非负整数。

特殊的进程号:

  • 0 → 内核进程号,系统运行起来就是内核进程
  • 1init进程,它是所有进程的祖先

3.2 Linux 下进程分类

类型 解释
交互进程 shell控制和运行,可以在前台运行,也可以在后台运行
批处理进程 不属于某一个终端,它被提交到一个队列中,以便顺序执行
守护执行 在后台运行,一般在Linux启动时就开始执行,系统关闭时才结束

3.3 进程控制块(PCB)

进程在创建和运行时,会有专门的一个结构体来保存它的信息,这个结构体为task_struct,又将其称之为进程表项或进程控制块,简称PCB

在/usr/src/linux-headers-3.2.0-29-generic-pae/include/linux下的sched.h头文件里面定义了task_struct结构体


3.4 进程的调度机制

多个进程如何运行

时间片轮转,上下文切换


3.5 进程的运行状态

符号 状态 解释
D 等待 不可中断的静止
R 运行态 正在执行中
S 睡眠态 阻塞状态
T 停止态 暂停执行,程序运行时,按下ctrl+Z,进程可进入该状态
Z 僵尸态 不存在但无法消除
W 没有足够页可分配
+ 前台进程 前台运行
< 高优先级进程 优先运行
N 低优先级进程
L 有内存分页分配并锁在内存中,多线程中出现
s 会话组组长

4. 进程相关命令


查看进程,杀掉进程

ps	   #查看当前用户运行的进程
ps aux #查看当前系统中所有的进程,并且可以查看所占内存百分百等信息
ps ajx #查看当前系统中所有的进程,并且可以查看当前进程的父进程的id
top    #动态显示系统中所有的进程
htop   #更加好看的动态显示系统中所有的进程
sudo apt-get install htop #下载htop
nice   #按用户指定的优先级运行进程
renice	#改变正在运行进程的优先级
kill    #向一个进程发送一个信号
kill -9 pid    #将进程号为pid的进程杀死
pstree  #以树形结构显示系统中所有的进程的关系

前后台切换

#将一个进程在后台运行,查看运行状态后面没有+,如果是前台运行,会有+
./a.out  & 

#将挂起的进程(停止态T)在后台执行
bg  

#把后台运行的进程放到前台运行(+),也可以直接把停止态(T)的进程直接唤醒
fg    

#将进程号为pid的进程变为停止态
kill -19 pid  

#将进程号为pid的进程从停止态变为后台进程
kill -18 pid 

暂时放着


5. 进程相关函数


5.1 fork() 创建子进程

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

功能:
	父进程创建一个子进程
	
参数:
    无
    
返回值:
    成功:
        >0   子进程的进程号,标识父进程的代码区
        0    标识子进程的代码区
        
   失败:-1

fork函数主要用于在一个进程中创建子进程,目的是为了能够在一个程序里面分别独立执行多个任务,相互之间还没有影响

只要执行一次fork(),就会在原有进程基础上再创建一个进程
如果不区分父子进程代码,fork()后所有代码,父子进程都会执行
一般使用fork()都是为了执行不同任务,所以一般都会区分父子进程代码区。

区分父子进程的方法就是判断fork()函数的返回值,在父进程中,fork()返回子进程PID,在子进程中fork()返回0

父子进程调度机制

父子进程调度机制也是时间片轮转,上下文切换
所以,他们之间执行时,根本没有先后之分。

父子进程用户空间问题

使用fork()函数创建的子进程,会将父进程 (虚拟内存中) 的用户空间完整复制一份,作为子进程 (虚拟内存中) 的用户空间。

父子进程之间的用户空间是完全独立的,他们对各自用户空间的操作,都不会对对方产生任何影响。

这里注意,如果在父进程中已经定义了一个变量aa的地址为0x123,当子进程创建完后,子进程中也会有变量a,且地址也是0x123,但是他们并不是同一个a,因为他们分别属于不同的用户空间,互不影响。


父子进程共享资源问题 / 父子进程同时访问同一文件

先看一段代码

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
   
   
    int fd;
    if((fd = open(argv[1], O_RDWR)) == -1)
    {
   
   
        perror("open error");
        return -1;
    }
    
    pid_t pid = fork();
    if(pid == -1)
    {
   
   
        perror("fork error");
        return -1;
    }
    else if(pid > 0) //父进程的代码区
    {
   
   
        //父进程向文件写入数据
        write(fd, "hello world", 12);
        printf("父: [%d]的offset = %ld\n"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值