#include "../head.h"

int main(void)
{
    char word[32] = {0};        //存终端输入的单词
    char tmpbuff[1096] = {0};   //存文件里的每行内容
    char *pret = NULL;
    FILE *fp = NULL;
    char first_word[32] = {0};  //存第一个单词
    char meaning[1024] = {0};   //存释义

    //使用strtok时:
    char *ptmpword = NULL;
    char *pmean = NULL;


    //从终端输入单词
    printf("请输入单词:\n");
    fgets(word, sizeof(word), stdin);
    word[strlen(word)-1] = '\0';
    //打开目的文件
    fp = fopen("dict.txt", "r");
    if(NULL == fp)
    {
        perror("fail to fopen");
        return -1;
    }
    //fgets逐行读目的文件里的内容,找输入的单词
    while(1)
    {
        pret = fgets(tmpbuff, sizeof(tmpbuff), fp);
        if(NULL == pret)
        {
            break;
        }
        /*
        //使用sscanf提取第一个单词和释义
        if(2 == sscanf(tmpbuff, "%31s %[^\n]", first_word, meaning))
        {
            if(strcmp(first_word, word) == 0)
            {
                printf("%s\n", meaning);
                return 1;
            }   
        }
        */

       //使用strtok分割单词
        ptmpword = strtok(tmpbuff, " ");
        if(0 == strcmp(ptmpword, word))
        {
            pmean = strtok(NULL, "\r");
            if(pmean != NULL)
            {
                while(*pmean == ' ' || *pmean == '\t')
                {
                    pmean++;
                }
                printf("单词含义:%s\n", pmean);
                fclose(fp);
                return 1;
            }
        }
    }
    printf("未找到该单词\n");

    fclose(fp);

    return 0;
}

链表是在内存中的,文件是在外存中存储的,内存中加载快

进程

概念

  • 进程:是程序动态执行的过程,包括创建、调度、消亡
  • 程序:存放在外存中的一段数据的集合

进程的创建

进程空间分布

  • 每个进程运行起来后,操作系统开辟0 - 4G虚拟内存空间
  • 进程空间:用户空间 + 内核空间(不允许用户访问)
  • 用户空间:文本段(文本区) + 数据段(数据区) + 系统数据段(堆区、栈区)
  • 文本段:存放代码和指令
  • 数据段:字符串常量、已初始化全局变量/静态变量、未初始化全局变量/静态变量
  • 系统数据段:堆区(malloc空间:申请大空间)、栈区(局部变量、函数运行:小变量)
    • 栈区存放数据特点:
      • 未经初始化值为随机值
      • 执行到变量定义开辟内存空间
      • 超过变量作用域回收变量空间
    • 堆区存放数据特点:
      • malloc申请堆区空间
      • free释放堆区空间
      • 避免内存泄漏
    • 数据区特点:
      • 未经初始化值为0值
      • 编译时开辟空间
      • 程序结束时回收空间

虚拟地址和物理地址

虚拟地址

所有能够被用户看到的地址均为虚拟地址,表示用户可以寻址的范围

物理地址

内存存放数据对应的实际硬件物理地址

MMU:内存映射单元

实际地址和物理地址之间的映射 由内存映射单元(MMU)来完成

多进程存储

存储方式
  • 多个进程空间在操作系统中存储时,空间是独立的(物理空间地址是独立的)
  • 多个进程在操作系统中共用同一个虚拟内存空间(虚拟地址是共享的)

多进程的调度

  • 常见的进程调度算法
    • 先来先执行,后来后执行
    • 高优先级调度算法
    • 时间片轮转调度算法
    • 多级队列反馈调度算法
    • 负载均衡调度算法
  • 多进程调度本质
    • 宏观并行,微观串行
    • 宏观分析:一个CPU同时执行多个进程任务
    • 微观分析:一个CPU在多个任务中高速切换保障多任务

进程相关的命令

top(动态查看)

ps -ef(查看当前进程信息)

ps - aux

后台执行进程任务

jobs

fg

nice / renice

kill / killall

进程的状态

进程相关的函数接口

fork

  • 子进程拷贝父进程文本段、数据段、系统数据段
  • 进程的PID不一样
  • fork的返回值不一样,父进程中返回子进程的PID,子进程中返回0
  • PID 一定是  > 0
  • 子进程返回0
  • 父进程的返回值 > 0
  • 父子进程空间独立,同一份代码中的变量和数据都会在父子进程中各有一份,父子进程修改自己空间的数据不会影响对方的空间

getpid和getppid

获得自己的PID和获得父进程的PID

#include "../head.h"

int main(void)
{
    pid_t pid;

    pid = fork();
    if(-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if(0 == pid)
    {
        printf("我是子进程,PID:%d PPID:%d\n", getpid(), getppid());
    }
    else if(pid > 0)
    {
        printf("我是父进程,PID:%d childPID:%d\n", getpid(), pid);
    }
    printf("hello world\n");
    while(1)
    {
        
    }

    return 0;
}

父进程创建两个子进程

#include "../head.h"

int main(void)
{
    pid_t pid;
    pid_t pid2;

    pid = fork();
    if(-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if(0 == pid)
    {
        printf("我是子进程1,PID:%d PPID:%d\n", getpid(), getppid());
    }
    else if(pid > 0)
    {
        pid2 = fork();
        if(-1 == pid2)
        {
            perror("fail to fork");
            return -1;
        }
        if(0 == pid2)
        {
            printf("我是子进程2,PID:%d PPID:%d\n", getpid(), getppid());
        }
        else if(pid2 > 0)
        {
            printf("我是父进程,PID:%d 子进程1PID:%d 子进程2PID:%d\n", getpid(), pid, pid2);
        }
    }
    printf("hello world\n");
    while(1)
    {
        
    }

    return 0;
}

exit

  • 在主函数中调用exit和return功能一样
  • return 在函数内部将结束该函数
  • exit 在函数内部将结束进程
  • exit 会在结束前刷新缓存区

_exit

进程回收

wait(回收子进程空间)

  • wait具有阻塞等待功能,等到有子进程结束才会回收子进程继续向下执行

  • wait 可以实现多父子进程同步

#include "../head.h"

int main(void)
{
    pid_t pid;
    pid_t ret;
    int status;

    pid = fork();
    if(-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if(0 == pid)
    {
        sleep(5);
        printf("子进程即将退出\n");
        exit(0);
    }
    else if(pid > 0)
    {
        ret = wait(NULL);
        ret = wait(&status);    //传参数知道子进程是什么原因死的
        printf("回收到子进程(PID:%d)空间\n", ret);
        while (1)
        {
            
        }
    }

    return 0;
}
#include "../head.h"

int main(void)
{
    pid_t pid;
    pid_t ret;
    int status;

    pid = fork();
    if(-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if(0 == pid)
    {
        sleep(5);
        printf("子进程即将退出\n");
        exit(10);
    }
    else if(pid > 0)
    {
        ret = wait(&status);    //传参时一般不使用指针,避免操作野指针
        printf("回收到子进程(PID:%d)空间\n", ret);
        if(WIFEXITED(status))
        {
            printf("子进程正常结束,退出值为:%d\n", WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status))
        {
            printf("子进程被 %d 号信号杀死\n", WTERMSIG(status));
        }

    }

    return 0;
}

waitpid

#include "../head.h"

int main(void)
{
    pid_t pid;
    pid_t ret;
    int status;

    pid = fork();
    if (-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if (0 == pid)
    {
        sleep(10);
        printf("子进程即将退出!\n");
        exit(20);
    }
    else if (pid > 0)
    {
//        ret = wait(&status);
        while (1)
        {
            ret = waitpid(pid, &status, WNOHANG); 
            if (ret > 0)
            {
                break;
            }

            printf("子进程咋还不退出啊,再等等吧\n");
            sleep(1);
        }
        
        printf("回收到子进程(PID:%d)空间了\n", ret);
        if (WIFEXITED(status))
        {
            printf("子进程正常结束,退出值为:%d\n", WEXITSTATUS(status));
        }
        else if (WIFSIGNALED(status))
        {
            printf("子进程被 %d 号信号杀死\n", WTERMSIG(status));
        }
    }

    return 0;
}
  • waitpid 可以用非阻塞回收子进程空间
  • waitpid 可以回收指定子进程空间

进程消亡

孤儿进程

父进程先结束,子进程会称为孤儿进程,被Init进程收养

僵尸进程
  • 是每个进程结束必然会经历的阶段
  • 产生原因:子进程结束后,父进程没有回收 子进程的空间,导致进程执行结束,空间仍然被占用的状态,称为僵尸进程
  • 避免:
    • 父进程先结束,子进程会成为孤儿进程,孤儿进程被init进程收养,子进程再结束,init进程回收进程空间
    • 子进程结束,父进程回收子进程空间,避免产生僵尸进程

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐