进程

进程

进程

进程的含义

进程是一个程序执行的过程(也可以说是正在运行的程序),会去分配内存资源,cpu的调度,它是并发的

PCB块

PCB是一个结构体,全名是process control block/print circuit board;

一个程序运行就有一块pcb块,它记录着运行程序的所有信息和状态
pcb里包含着:

PID---进程标识符

chdir---当前工作路径

umask 0002

fds---进程打开的文件列表

signal---信号相关设置 处理异步io:

用户id,组id:

进程资源的上限(ulimit -a,显示资源上限);

进程和程序的区别

程序是静态

存储在硬盘中代码,数据的集合;

进程是动态

程序执行的过程,包括进程的创建、调度、消亡;

(1)程序是永存,进程是暂时的

(2)进程有程序状态的变化,程序没有

(3)进程可以并发,程序无并发

(4)进程与进程会存在竞争计算机的资源

(5)一个程序可以运行多次,变成多个进程;一个进程可以运行一个或多个程序

内存空间

32位内存空间分布图:

虚拟地址空间共4G:0~ 3G,是进程的空间,3G~4G是内核的空间

虚拟地址 :

映射表将虚拟内存的地址转换为物理内存的地址;

映射表通常由页表(Page Table)组成,它记录了虚拟地址到物理地址的映射关系;

一个页面大小通常为4k(4036字节);

进程分类:

1、交互式进程

2、批处理进程 (shell脚本)

3、 守护进程

进程的作用

并发性:允许多个进程同时运行,提高了CPU利用率和系统响应速度;

稳定性:如果一个进程发生错误,通常不会影响到其他进程,这提高了系统的稳定性;

进程的状态进程已经准备好执行,所有的资源都已分配,只等待CPU时间

基本操作系统三个操作状态:就绪→执行态→阻塞(等待,睡眠);
就绪状态 :进程已经准备好执行,所有的资源都已分配,只等待CPU时间

执行

linux中的状态:运行态,睡眠态,僵尸,暂停态;

进程的调度

调度器的通过调度策略来决定哪个进程先运行;
进程上下文切换 :就是值指调度器要切换CPU给另一个进程的时候,要保存当前进程的状态,然后加载打开一个新的进程这样的一个过程。

宏观并行:在一个时间段多个任务和进程是同时进行的

微观串行:在一个时间点只能一条一条指令执行

进程相关命6.查询进程相关命令

1.ps aux

查看进程相关信息:

就绪态、运行态 ------ R

睡眠态、等待态

可唤醒等待态 ------S

不可唤醒等待态 ------D

停止态 ------T

僵尸态 ------Z

2.top

根据CPU占用率查看进程相关信息

PR NI 表示优先级

数字越小代表优先级越高

3.kill和killall发送一个信号

kill -2 PID 15

发送信号+PID对应的进程,默认接收者关闭

killall -9 进程名

发送信号 进程名对应的所有进程

killall a.out

函数

1.fork();

函数原型

c 复制代码
pid_t fork();

一次调用,会返回两次。

子进程先运行和是父进程先进程,顺序不确定。

变量不共享。

子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。
功能

通过该函数可以从当前进程中克隆一个同名新进程。

克隆的进程称为子进程,原有的进程称为 父进程。

子进程是父进程的完全拷贝。

子进程的执行过程是从fork函数之后执行。

子进程与父进程具有相同的代码逻辑。
返回值

int 类型的数字。

在父进程中:成功 返回值是子进程的pid号 >0

失败 返回-1;

在子进程中:成功 返回值 0

失败 无

举例问题:

一次fork生成几个进程?他们之间的关系是什么样的?

一次fork生成两个进程,父子关系。

如果两次fork同时前后执行,会生成几个进程?他们之间的关系如何表示,有多少个子进程,有没有孙进程?

会生成三个进程,父进程,子进程和孙进程。

2.getpid

函数原型

c 复制代码
pid_t getpid(void);

功能 :

获得调用该函数进程的pid
参数 :

缺省
返回值 :

进程的pid

3.getppid

函数原型

c 复制代码
pid_t getppid(void);

功能 :

获得调用该函数进程的父进程pid号
参数 :

缺省
返回值 :

返回父进程id号

示例代码

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
int a = 20;
int main(int argc, char *argv[])
{
    pid_t ret = fork();
    if(ret>0)
    {
        //father
            sleep(3);
            printf("father is %d   pid %d ,ppid:%d  \n",a,getpid(),getppid());
    }
    else if(0 == ret)
    {
        //child
            printf("child a is %d\n",a);
            a+=10;
            printf("child a is %d pid:%d ppid:%d\n",a,getpid(),getppid());
    }
    else 
    {
        perror("fork error\n");
        return 1;
    }
    printf("a is %d pid:%d\n",a,getpid());
    return 0;
}

应用场合:

1)一个进程希望复制自己,使父子进程同时执行同的代码段。网络服务中会比较多见。

2)一个进程需要执行一个不同的程序。fork+exec

父子进程关系

子进程是父进程的副本,子进程获得父进程数据段,堆、栈正文段共享;

区别:

fork的返回值 父的是大于0,子是等于0;

pid不同 子相对于父至少要加个1;

进程的终止

8种情况

1)main 中return

2)exit(), c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。已经清理函数(atexit)。

3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。

  1. 主线程退出

5)主线程调用pthread_exit 异常终止

6)abort() 越界报错

7)signal kill pid

8)最后一个线程被pthread_cancle

进程的退出

僵尸进程和孤儿进程

僵尸进程:进程执行结束但空间未被回收变成僵尸进程(pcb不释放会导致内存越来越小导致内存崩溃);

孤儿进程:父进程先消亡

1.exit

属于库函数

退出状态,终止的进程会通知父进程,自己使如何终止的。如果是正常结束(终止),则由exit传入的参数。如果是异常终止,则有内核通知异常终止原因的状态。任何情况下,负进程都能使用wait,waitpid获得这个状态,以及资源的回收。

函数原型

c 复制代码
void exit(int status)
调用: 
exit(1);

功能 :

让进程退出,并刷新缓存区
参数
status :进程退出的状态
返回值 :

缺省

EXIT_SUCCESS 0

EXIT_FAILURE 1
相当于return

但当该关键字出现在main函数中时候可以结束进程

如果在其他函数中则表示结束该函函数;
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{    
    FILE* fp = fopen("1.txt","w");
    char buf[512]="hello,123";
    fputs(buf,fp);
    exit(0);
    printf("aaaaaaaaaaa\n");
    return 0;
}

2._exit

属于系统调用

函数原型

c 复制代码
void _exit(int status);

功能 :

让进程退出,不刷新缓存区
参数 :

status:进程退出状态
返回值 :

缺省;
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    FILE* fp = fopen("1.txt","w");
    char buf[512]="hello,123";
    fputs(buf,fp);
    _exit(0);// 不会刷新缓冲区  
    printf("aaaaaaaaaaa\n");
    return 0;
}

3.atexit

回调函数
函数原型

c 复制代码
int atexit(void (*function)(void));

功能 :

注册进程退出前执行的函数
参数 :

function:函数指针

指向void返回值void参数的函数指针
返回值 :

成功返回0

失败返回非0

当程序调用exit或者由main函数执行return时,所有用atexit

注册的退出函数,将会由注册时顺序倒序被调用
示例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
char * p ; 
void clean(void)
{
    printf("clean func,p %s\n",p);
    free(p);
}
int main(int argc, char *argv[])
{
    atexit(clean);
    p = (char*)malloc(50);
    strcpy(p,"hello");
    printf("main p %s\n",p);
    exit(0);
    return 0;
}

进程空间的回收

wait/waitpid

wait

函数原型

c 复制代码
pid_t wait(int *status);

功能

该函数可以阻塞等待任意子进程退出并回收该进程的状态。

一般用于父进程回收子进程状态。

参数

status 进程退出时候的状态:

如果不关心其退出状态一般用NULL表示;

如果要回收进程退出状态,则用WEXITSTATUS回收。

返回值

成功 回收的子进程pid

失败 -1;

WIFEXITED(status) 是不是正常结束

WEXITSTATUS(status) 使用这个宏去那返回值

WIFSIGNALED(status) 是不是收到了信号而终止的

WTERMSIG(status)如果是信号终止的,那么是几号信号。
注意wait

1)如果所有的子进程都在运行,在阻塞

2)如果一个子进程终止,正在等待的父进程则获得终止状态,获得子进程的状态后,立刻返回。

3)如果没有子进程,则立即出错退出。

waitpid(-1,status,0)=wait(status);

waitpid

函数原型

c 复制代码
pid_t waitpid(pid_t pid, int *status, int options);

pid

小于-1 回收指定进程组内的任意子进程;

等于-1 回收任意子进程,组内外;

等于0 回收和当前调用waitpid一个组的所有子进程,组内;

大于0 回收指定ID的子进程;

waitpid (-1,a,0) == wait(a);

status

子进程退出时候的状态,

如果不关注退出状态用NULL;

options 选项

0 表示回收过程会阻塞等待

WNOHANG 表示非阻塞模式回收资源。

返回值:成功 返回接收资源的子进程pid

失败 -1

0,

exec

execute
exec族

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建; 新进程,所以调用exec前后该进程的id并未改变。

其实有六种以exec开头的函数,统称exec函数:
函数原型

c 复制代码
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);

int execle(const char *path, const char *arg, ..., char *const envp[]);
int execle(const char *path, const char *arg, ..., char *const envp[]);

int execve(const char*path,char*const argv[],char*const evnp[]);
int execlp(const char *file, const char *arg, ...);

int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

区别

1)前4个使用路径名作为参数,后面两个使用文件名做参数

当filename中,含有/时视为路径名,否则就按PATH变量,在指定目录下查找可执行文件。

2)相关的参数表传递,l表示list,v表示vector;

execl,execlp,execle,需要将参数一个一个列出,并以NULL结尾。

execv,execvp,execve,需要构造一个参数指针数组,然后将数组的地址传入。

3)以e结尾的函数,可以传入一个指向环境字符串的指针数组的指针。其他未指定环境变量,使用父进程继承过来的。

execve 是真正的系统调用

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错;则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

system

函数原型

c 复制代码
int system(const char *command); fork+exec 

功能

使用该函数可以将shell命令直接在代码中执行。
参数

command要执行的shell命令
返回值

成功 0

失败 -1

相关推荐
weixin_4370446417 分钟前
Netbox批量添加设备——堆叠设备
linux·网络·python
hhy_smile18 分钟前
Ubuntu24.04 环境配置自动脚本
linux·ubuntu·自动化·bash
宴之敖者、1 小时前
Linux——\r,\n和缓冲区
linux·运维·服务器
LuDvei1 小时前
LINUX错误提示函数
linux·运维·服务器
未来可期LJ1 小时前
【Linux 系统】进程间的通信方式
linux·服务器
Abona1 小时前
C语言嵌入式全栈Demo
linux·c语言·面试
Lenyiin1 小时前
Linux 基础IO
java·linux·服务器
The Chosen One9851 小时前
【Linux】深入理解Linux进程(一):PCB结构、Fork创建与状态切换详解
linux·运维·服务器
Kira Skyler2 小时前
eBPF debugfs中的追踪点format实现原理
linux
2501_927773073 小时前
uboot挂载
linux·运维·服务器