C语言超详细分析多进程的概念与使用

目录
  • 1.多进程相关概念
    • 1.1什么是进程
    • 1.2进程和程序有什么区别
    • 1.3进程的组成
    • 1.4进程的种类
    • 1.5什么是进程的PID
    • 1.6特殊PID的进程
    • 1.7进程的状态
  • 二.进程实例
    • 2.1进程的拷贝
    • 2.2进程创建的实例
    • 2.3执行没有先后循序
    • 2.4进程创建的实例(父子进程内存空间问题)
    • 2.5使用多进程创建三个进程
    • 2.6使用多进程进行拷贝文件
  • 三.什么是孤儿进程与僵尸进程
    • 3.1僵尸进程
    • 3.2孤儿进程
  • 四,守护进程的创建
    • 4.1守护进程的创建
    • 4.2步骤图
    • 4.3代码实现

1.多进程相关概念

1.1什么是进程

进程:程序的一次执行过程就会产生一个进程。进程是分配资源的最小单位(0-3G)。

进程就是一个正在执行的任务。进程是一个动态的过程,它有生命周期随着程序的运行

开始,随着程序结束而消亡。每个进程都有自己的独立的运行的空间,比如每个进程都有

自己的文件描述符,每个进程都拥有自己的缓冲区。只要用户执行了一个程序,在内核空间

就会创建一个task_struct的结构体,这个结构体就代表当前的进程。进程运行产生的所有

的信息都被放到这个结构体中保存着。

1.2进程和程序有什么区别

程序:程序是经过编译器编译生成的二进制文件,程序在硬盘上存储。程序是静态的,没有生命

​ 周期的概念程序本身不会分配内存。

进程:程序的一次执行过程就会创建一个进程,进程是动态的,有生命周期。进程运行的时候会

​ 分配0-3G的内存空间。进程在内存上存储。

1.3进程的组成

进程是由三个部分组成的:进程的PCB(task_struct),(程序段)文本段,数据段。

1.4进程的种类

交互进程:这种进程维护以一个终端,通过这个终端用户可以和进程进程交互。

​ 例如:文本编辑器

批处理进程:这种进程优先级比较低,运行的时候会被放到一个运行的队列中。

​ 随着队列的执行,而逐渐执行。

​ 例如gcc编译程序的时候这个进程就是批处理进程。

守护进程:守护进程就是后台运行的服务,随着系统的启动而启动,随着系统的终止而终止。

​ 例如:windows上的各种服务

1.5什么是进程的PID

PID就是操作系统给进程分配的编号,它是识别进程的唯一的标识。

在linux系统中PID是一个大于等于0的值。

1.6特殊PID的进程

0:idle:在linux系统启动的时候运行的第一个进程就是0号进程。

​ 如果没有其他的进程执行就运行这个idle进程。

1:init:1号进程是由0号进程调用内核kernel_thread函数产生的第一个进程,

​ 它会初始化所有的系统的硬件。当初始化完之后会一直执行,比如会为

​ 孤儿进程回收资源。

2:kthreadd:调度器进程,主要负责进程的调度工作。

1.7进程的状态

1.进程运行的状态

D// 不可中断的休眠态(信号)

R//运行态

S//可中断的休眠态(信号)

T//停止状态

X//死亡状态

Z//僵尸态(进程结束后没有被父进程回收资源)

2.进程的附加状态

<//高优先级

N//低优先级的进程

L//在内存区锁定

s//会话组组长

l//进中包含多线程

+//前台进程

二.进程实例

2.1进程的拷贝

#include <head.h>
int main(int argc,const char * argv[])
{
    for(int i=0;i<2;i++){
        fork();
        printf("-");
    }
    return 0;
}

原理图:

分析:上述的程序会打印8个-,上述程序一共是4个进程,因为在进程创建的时候会拷贝父进程的

缓冲区,由于2214和2215的缓冲区没有刷新,所以2216和2217缓冲区中有两个-。

2.2进程创建的实例

#include <head.h>
int main(int argc,const char * argv[])
{
    pid_t pid;
    pid = fork();
    if(pid == -1){
        PRINT_ERR("fork error");
    }else if(pid == 0){
        //子进程
        printf("这个就是子进程\n");
    }else{
        //父进程
        printf("这个就是父进程\n");
    }
    return 0;
}

分析图:

2.3执行没有先后循序

#include <head.h>
int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        //子进程
        while (1) {
            sleep(1);
            printf("这个就是子进程\n");
        }
    } else {
        //父进程
        while (1) {
            sleep(1);
            printf("这个就是父进程\n");
        }
    }
    return 0;
}

结果图:

2.4进程创建的实例(父子进程内存空间问题)

父进程fork产生子进程的时候利用了写时拷贝的原则

2.5使用多进程创建三个进程

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    pid_t pid;
    if((pid = fork()) ==-1){
        printf("创建进程失败\n");
        exit(-1);
    }else if(pid == 0){
        pid_t tid;
        if((tid = fork())==-1){
            printf("%d,%d\n",getpid(),getppid());
        }else if(pid == 0){
            printf("%d,%d\n",getpid(),getppid());
        }else{
            printf("%d\n",getpid());
        }
    }
    while(1);
    return 0;
}

2.6使用多进程进行拷贝文件

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
char destfile[32]={0};
int get_length(char const *arcfile)
{
    int fd,cd,length;
    if((fd=open(arcfile,O_RDONLY))==-1){
        printf("open file error");
        exit(-1);
    }
    snprintf(destfile,sizeof(destfile),"new_%s",arcfile);
    if((cd==open(destfile,O_WRONLY|O_TRUNC|O_CREAT,0666)==-1)){
    }
    length = lseek(fd,0,SEEK_END);
    return length;
}
int copy_file(const char* srcfile,const char* destfile,int start,int length)
{
    int sfd,dfd,ret,count = 0;
    char buff[128] = {0};
    int counts=0;
    if((sfd=open(srcfile,O_RDONLY))==-1){
        printf("open error");
    }
    if((dfd=open(destfile,O_WRONLY))==-1){
        printf("open file error");
    }
    lseek(sfd,start,SEEK_SET);
    lseek(dfd,start,SEEK_SET);
    while (1){
       ret = read(sfd,buff,sizeof(buff));
       if(ret == 0){
            break;
       }
        count+=ret;
       if(count >= length){
            write(dfd,buff,length-(count-ret));
            break;
       }
       write(dfd,buff,ret);
    }
    return 0;
}
int main(int argc, char const *argv[])
{
    pid_t pid;
    if(argc!=2){
        printf("输入格式错误,请重新输入\n");
        exit(-1);
    }
    int length = get_length(argv[1]);
    if(length < 0){
        printf("srcfile error\n");
        return -1;
    }
    if((pid=fork())==-1){
        printf("fork error");
        exit(-1);
    }else if(pid == 0){
        copy_file(argv[1],destfile,length/2,length-length/2);
    }else{
        copy_file(argv[1],destfile,0,length/2);
    }
    return 0;
}

三.什么是孤儿进程与僵尸进程

3.1僵尸进程

子进程结束之后,父进程没有为它回收资源,此时子进程就是僵尸进程

#include <head.h>
int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        //子进程
    } else {
        //父进程
        while(1);
    }
    return 0;
}

3.2孤儿进程

孤儿进程:父进程死掉之后,子进程就是孤儿进程,孤儿进程被init进程收养。

#include <head.h>
int main(int argc, const char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        PRINT_ERR("fork error");
    } else if (pid == 0) {
        //子进程
        printf("pid = %d\n",getpid());
        while (1);
    } else {
        //父进程
    }
    return 0;
}

四,守护进程的创建

4.1守护进程的创建

守护进程:相当于系统的服务,随着系统的启动而运行,随着系统的终止而终止。脱离了某个终端.

4.2步骤图

4.3代码实现

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define ERROR(msg) do{\
    printf("%s %s %d\n",__FILE__,__func__,__LINE__);\
    printf(msg);\
    exit(-1); \
}while(0)
int main(int argc, char const *argv[])
{
    pid_t pid;
    if ((pid == fork()) == -1){
        ERROR("fork error");
    }else if(pid == 0){
        int fd;
        setsid();
        chdir("/");
        umask(0);
        for(int i = 3; i < getdtablesize(); i++){
            close(i);
        }
        if((fd = open("my.log", O_RDWR, O_APPEND, O_CREAT, 0666)) == -1){
            ERROR("open error");
        }
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        while(1){
            sleep(1);
            printf("我是子进程\n");
            fflush(stdout);
        }
    }else{
        printf("父亲进程已经结束....\n");
        exit(-1);
    }
    return 0;
}

到此这篇关于C语言超详细分析多进程的概念与使用的文章就介绍到这了,更多相关C语言多进程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c语言多进程tcp服务器示例

    server.h 复制代码 代码如下: #ifndef SERVER_H#define SERVER_H#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <as

  • C语言超详细分析多进程的概念与使用

    目录 1.多进程相关概念 1.1什么是进程 1.2进程和程序有什么区别 1.3进程的组成 1.4进程的种类 1.5什么是进程的PID 1.6特殊PID的进程 1.7进程的状态 二.进程实例 2.1进程的拷贝 2.2进程创建的实例 2.3执行没有先后循序 2.4进程创建的实例(父子进程内存空间问题) 2.5使用多进程创建三个进程 2.6使用多进程进行拷贝文件 三.什么是孤儿进程与僵尸进程 3.1僵尸进程 3.2孤儿进程 四,守护进程的创建 4.1守护进程的创建 4.2步骤图 4.3代码实现 1.多

  • C语言超详细讲解指针的概念与使用

    目录 一.指针与一维数组 1. 指针与数组基础 2. 指针与数组 3. 一个思考 二.指针与字符串 三.指针和二维数组 1. 指针数组与数组指针 2. 指针数组 3. 数组指针 一.指针与一维数组 1. 指针与数组基础 先说明几点干货: 1. 数组是变量的集合,并且数组中的多个变量在内存空间上是连续存储的. 2. 数组名是数组的入口地址,同时也是首元素的地址,数组名是一个地址常量,不能更改. 3. 数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的其实地址. 对于第一点数

  • 超详细分析C语言动态内存管理问题

    目录 一.为什么存在动态内存的分配 二.动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 三.常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 对同一块动态内存多次释放 3.5 动态开辟内存忘记释放(内存泄漏) 四.几个经典的笔试题 五.C/C++程序的内存开辟 六.柔性数组 6.1 柔性数组的特点 6.2 柔性数组的使用 6.3 柔性数组的优势 上期结束了[

  • C语言与C++内存管理超详细分析

    目录 一.内存 1.1 内存四区 1.2 使用代码证实内存四区的底层结构 二.malloc 和 free 2.1 malloc 和 free 的使用 2.2 内存泄漏与安全使用实例与讲解 三.new 和 delete 3.1 new 和 delete 使用 3.2 delete 与 delete[] 的区别 一.内存 在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序 A 并不能访问应用程序 B,当然一些特殊技巧可以访问,但此文并不详细进行说明.例如在计算机中,一个视频播放程序与

  • C语言 超详细总结讲解二叉树的概念与使用

    目录 1.二叉树的概念及结构 2.二叉树链式结构的实现 1.二叉树的概念及结构 ①概念:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成. ②二叉树的特点: 每个结点最多有两棵子树,即二叉树不存在度大于2的结点.(度最多为2) 二叉树的子树有左右之分,其子树的次序不能颠倒. ③现实中的二叉树: 当一名普通的人看到这样一颗树,可能会想:好标准的一棵树 当一个程序猿看到这样一棵树,可能会想:好像数据结构中的二叉树,并且还是颗满二叉树 ④数据结

  • Java超详细分析泛型与通配符

    目录 1.泛型 1.1泛型的用法 1.1.1泛型的概念 1.1.2泛型类 1.1.3类型推导 1.2裸类型 1.3擦除机制 1.3.1关于泛型数组 1.3.2泛型的编译与擦除 1.4泛型的上界 1.4.1泛型的上界 1.4.2特殊的泛型上界 1.4.3泛型方法 1.4.4类型推导 2.通配符 2.1通配符的概念 2.2通配符的上界 2.3通配符的下界 题外话: 泛型与通配符是Java语法中比较难懂的两个语法,学习泛型和通配符的主要目的是能够看懂源码,实际使用的不多. 1.泛型 1.1泛型的用法

  • C语言 超详细讲解算法的时间复杂度和空间复杂度

    目录 1.前言 1.1 什么是数据结构? 1.2 什么是算法? 2.算法效率 2.1 如何衡量一个算法的好坏 2.2 算法的复杂度 2.3 复杂度在校招中的考察 3.时间复杂度 3.1 时间复杂度的概念 3.2 大O的渐进表示法 3.3 常见时间复杂度计算举例 4.空间复杂度 5. 常见复杂度对比 1.前言 1.1 什么是数据结构? 数据结构(Data Structure)是计算机存储.组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合. 1.2 什么是算法? 算法(Algorit

  • C语言超详细文件操作基础上篇

    目录 一.为什么使用文件 二.什么是文件 1.什么是数据文件 2.什么是程序文件 3.文件名 三.文件的打开和关闭 1文件指针: 2.打开和关闭文件函数 (1)打开文件函数: (2)关闭文件函数 四.文件的顺序读写 1.写文件(fputc,操作一个字符) 2.读文件(fgetc,操作一个字符) 3.写文件(fputs,操作字符串) 4.读文件(fgets,操作字符串) 一.为什么使用文件 为了更好的把信息记录下来,对数据进行持久化的保存,这个时候我们就可以把数据写到文件里面去,使用文件我们可以将

  • C++超详细分析红黑树

    目录 红黑树 红黑树的概念 红黑树的性质 红黑树结点的定义 红黑树的插入操作 情况一 情况二 情况三 红黑树的验证 用红黑树封装map.set 红黑树的迭代器 封装map 封装set 红黑树 红黑树的概念 红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的. 红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是

  • Java数据结构超详细分析二叉搜索树

    目录 1.搜索树的概念 2.二叉搜索树的简单实现 2.1查找 2.2插入 2.3删除 2.4修改 3.二叉搜索树的性能 1.搜索树的概念 二叉搜索树是一种特殊的二叉树,又称二叉查找树,二叉排序树,它有几个特点: 如果左子树存在,则左子树每个结点的值均小于根结点的值. 如果右子树存在,则右子树每个结点的值均大于根结点的值. 中序遍历二叉搜索树,得到的序列是依次递增的. 二叉搜索树的左右子树均为二叉搜索树. 二叉搜索树的结点的值不能发生重复. 2.二叉搜索树的简单实现 我们来简单实现以下搜索树,就不

随机推荐