高级进程管理

基于文件的进程间通信
目的:

  1. 回顾文件及进程相关操作
  2. 初步探索一下进程间通信
    需求描述
    1. 设置一个并发度INS,表示要开的进程数量
    2. 使用这个INS 个进程,计算从start 到 end 之间的数字累加和
    3. start 和 end 使用 getopt 解析命令行参数
      1. ./add -s 12 -e 24
        注意事项:
  3. 使用文件进行数据共享,需要考虑到数据竞争
  4. 尝试使用文件锁来模拟线程之间的互斥锁
  5. 通过文件锁来实现临界数据的访问。
    函数接口 : flock
// 单文件,以数据文件为锁
#include "head.h"
#define INS 5

char num_file[] = "./.num";

struct Msg {
    int now;
    int sum;
} m, num;



/*int read_from_file() {
    FILE *fp = fopen("./file.txt", "r");
    if (fp == NULL) return 1;
    fscanf(fp, "%d%d", &m.now, &m.sum);
    fclose(fp);
    return 0;
}*/

size_t set_num(struct Msg *msg) {
    FILE *f = fopen(num_file, "w");
    if (f == NULL) {
        fclose(f);
        perror("fopen");
        return -1;
    }
    size_t nwrite = fwrite(msg, 1, sizeof(struct Msg), f);
    fclose(f);
    return nwrite;
}

size_t get_num(struct Msg *msg) {
    FILE *f = fopen(num_file, "r");
    if (f == NULL) {
        perror("fopen");
        return -1;
    }
    size_t nread = fread(msg, 1, sizeof(struct Msg), f);
    fclose(f);
    return nread;
}

/*int write_into_file() {
    FILE *fp = fopen("./file.txt", "w");
    if (fp == NULL) return 1;
    fprintf(fp, "%d %d", m.now, m.sum);
    fclose(fp);
    return 0;
}*/

void do_add(int end, int pid_num) {
    while (1) {
        FILE *f = fopen(num_file, "r");
        flock(f->_fileno, LOCK_EX);
        fread(&num, sizeof(struct Msg), 1, f);
        if (num.now > end) {
            fclose(f);
            break;
        }
        num.sum += num.now;
        num.now++;
        printf("The <%d>th Child : now = %d, sum = %d\n", pid_num, num.now, num.sum);
        set_num(&num);
        flock(f->_fileno, LOCK_UN);
        fclose(f);
    }
}

int main(int argc, char **argv) {
    int opt, start = 0, end = 0;
    while ((opt = getopt(argc, argv, "s:e:")) != -1) {
        switch (opt) {
            case 's':
                start = atoi(optarg);
                break;
            case 'e':
                end = atoi(optarg);
                break;
            default:
                fprintf(stderr, "Usage : %s -s start -e end_num\n", argv[0]);
                exit(1);
        }
    }

    printf("start = %d\nend = %d\n", start, end);
    num.now = 0;
    num.sum = 0;
    set_num(&num);
    pid_t pid;
    int x;
    for (int i = 1; i <= INS; i++) {
        if ((pid = fork()) < 0) {
            perror("fork()");
            exit(1);
        }
        if (pid == 0) {
            x = i;
            break;
        }
    }
    if (pid == 0) {
        do_add(end, x);
    } else {
        for (int i = 0; i < INS; i++) wait(NULL);
    }
    get_num(&num);
    printf("sum : %d\n", num.sum, getpid());


    return 0;
}
// 使用锁文件
#include "head.h"
#define INS 5

char num_file[] = "./.num";
char lock_file[] = "./.lock";
struct Msg {
    int now;
    int sum;
} m, num;



/*int read_from_file() {
    FILE *fp = fopen("./file.txt", "r");
    if (fp == NULL) return 1;
    fscanf(fp, "%d%d", &m.now, &m.sum);
    fclose(fp);
    return 0;
}*/

size_t set_num(struct Msg *msg) {
    FILE *f = fopen(num_file, "w");
    if (f == NULL) {
        fclose(f);
        perror("fopen");
        return -1;
    }
    size_t nwrite = fwrite(msg, 1, sizeof(struct Msg), f);
    fclose(f);
    return nwrite;
}

size_t get_num(struct Msg *msg) {
    FILE *f = fopen(num_file, "r");
    if (f == NULL) {
        perror("fopen");
        return -1;
    }
    size_t nread = fread(msg, 1, sizeof(struct Msg), f);
    fclose(f);
    return nread;
}

/*int write_into_file() {
    FILE *fp = fopen("./file.txt", "w");
    if (fp == NULL) return 1;
    fprintf(fp, "%d %d", m.now, m.sum);
    fclose(fp);
    return 0;
}*/

void do_add(int end, int pid_num) {
    while (1) {
        FILE *lock = fopen(lock_file, "w");
        flock(lock->_fileno, LOCK_EX);
        //FILE *f = fopen(num_file, "r");
        //fread(&num, sizeof(struct Msg), 1, f);
        get_num(&num);
        if (num.now > end) {
            fclose(lock);
            break;
        }
        num.sum += num.now;
        num.now++;
        printf("The <%d>th Child : now = %d, sum = %d\n", pid_num, num.now, num.sum);
        set_num(&num);
        flock(lock->_fileno, LOCK_UN);
       // fclose(f);
        fclose(lock);
    }
}

int main(int argc, char **argv) {
    int opt, start = 0, end = 0;
    while ((opt = getopt(argc, argv, "s:e:")) != -1) {
        switch (opt) {
            case 's':
                start = atoi(optarg);
                break;
            case 'e':
                end = atoi(optarg);
                break;
            default:
                fprintf(stderr, "Usage : %s -s start -e end_num\n", argv[0]);
                exit(1);
        }
    }

    printf("start = %d\nend = %d\n", start, end);
    num.now = 0;
    num.sum = 0;
    set_num(&num);
    pid_t pid;
    int x;
    for (int i = 1; i <= INS; i++) {
        if ((pid = fork()) < 0) {
            perror("fork()");
            exit(1);
        }
        if (pid == 0) {
            x = i;
            break;
        }
    }
    if (pid == 0) {
        do_add(end, x);
    } else {
        for (int i = 0; i < INS; i++) wait(NULL);
    }
    get_num(&num);
    printf("sum : %d\n", num.sum);


    return 0;
}

锁:进行数据保护,当多个对象使用一个资源
计算机中的同步,事件发生的顺序是确定的。
出现竞争的情况,需要互斥锁来解决。
互斥锁中,当一个进程发现访问对象被上锁了,会等待,直到锁被释放。使用完资源需要释放锁,如果不解锁,会发生阻塞。

这里使用文件来存储数据,作为不同进程之间交流的媒介,数据共享。
但是考虑到如果存在两个文件,假设两个进程各拿一个文件,那么会因为无法进行下一步而导致死锁。
所以,这里引入了原子操作。
文件怎么存储?
1. 第一行存当前的值,第二行存累加的值。这样做的话,需要用标准IO, 因为标准IO 使用行缓冲的机制。
2. 存int * 2 八个字节,每4个字节一个变量。

进程调度:

  • 进程调度是一个内核子系统
  • 进程调度的主要任务是决定哪一个“就绪”状态的进程来执行
  • 就绪进程就是非阻塞进程
  • 阻塞进程就是正在睡眠的进程,需要内核唤醒的进程

进程的三态模型
在这里插入图片描述
最开始只有一个CPU : 单任务操作系统,运行完运行下一个,当我们想同时运行多个任务,操作系统只能交替运行多个应用,从用户的角度来看,制造同时运行多个任务的错觉。
在多处理器的操作系统中,在不同的处理器上可以同时运行不同的任务,做到真正的并行运行。

什么是并发?什么是并行?
并发:在一段时间内,同时运行多个任务,这些任务并发。
并行:在同一时刻,同时运行多个任务,这些任务并行。需要有多个CPU
调度就是运行哪个进程,运行多久。

协同式和抢占式
协同:
进程会一直运行直到它自己结束
操作系统不做干预
程序自己决定,什么时候让出CPU ,什么时候让其他进程运行。
现在有,但是不多,实用性不高。
抢占:
调度器决定进程合适结束并执行另一个进程,这叫做抢占
进程被抢占前运行的时间称为该进程的时间片
调度器给每个进程分配一个处理器时间片
时间片:
时间片的长短会对于系统的全局行为和性能来说是至关重要的
时间片过长:-> 提升系统通吐率和全局性能
进程执行前需要等待很长时间,降低了并发运行
用户会感到明显的延迟
时间片过短: -> 提升交互性能
大量的时间将花费在调度上
时间局部性带来的性能提升将大打折扣(缓存)
空间局部性也有提升
什么是时间局部性?随着时间,CPU 会保留一些可能需要用到的数据,当用的时候,直接交付数据。就没有必要从内存上拿,当调度过快的时候,CPU 无法预测下一次可能使用的数据。
什么是空间局部性?当前一个数据是需要的,后面的数据可能也需要,CPU 会提前多拿几个。每次多拿的数据,对性能有提升。
解决时间片长短问题:不用时间片
高优先级,可能会在运行队列的前面,也有更大时间片的可能。

多核与多CPU 的区别:
现在认为,一个物理CPU 有多个核心,一个核心,认为是一个小CPU

确定就绪态(就绪队列)
根据就绪队列以及优先级分配时间和顺序

IO约束型和处理器约束型

一直消耗完可用时间片的进程为处理器约束型进程
需要获取大量的CPU资源
消耗掉调度器分配全部的CPU
多数时间处于阻塞状态或者等待资源的进程为IO约束型进程
经常阻塞在文件IO操作上:文件,网络,键盘,鼠标
也可能除了请求内核执行IO操作之外什么也不做

IO约束及处理器约束对时间片的要求
处理器约束:
期待获得更长的时间片,从而最大化缓冲命中,尽快完成任务(时间局部性)
IO约束:
进程只会运行极短的时间,然后阻塞在内核资源
期待自己阻塞后,更快的唤醒,好进行下次IO操作,调度更多的IO,对资源有更高效的使用
一个等待用户输入的程序,被调度的越快,越有无缝衔接的感觉,听音乐也是如此
简而言之:需要更短的时间片。

在这里插入图片描述
在这里插入图片描述
根据预期划分时间,每个人有不同的时间片,根据优先级和权值调整时间片。
目标延迟:两次执行之间的间隔,调度的固定周期。
目标延迟过小,运行时间短。
最小粒度。
有了目标延迟,有了动态时间片,有了最小粒度,就有了一定的公平性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值