zoukankan      html  css  js  c++  java
  • Linux IPC实践(12) --System V信号量(2)

    实践1:信号量实现进程互斥

    父子进程执行流程如下:

    父进程

    子进程

    P

    P

    O(print)

    X(print)

    sleep

    sleep

    O(print)

    X(print)

    V

    V

    sleep

    sleep

    从图中可以看出, O或X总是成对出现的, 要么两个O, 要么两个X;

    /**P,V原语实现父子进程互斥使用终端**/
    // 程序代码
    int main(int argc,char *argv[])
    {
        int semid = sem_create(IPC_PRIVATE);
        sem_setval(semid, 1);
        int count = 10;
    
        pid_t pid = fork();
        if (pid == -1)
            err_exit("fork error");
        else if (pid > 0)   //子进程
        {
            srand(getpid());
            while (count --)
            {
                sem_P(semid);
                //临界区开始
                cout << 'X';
                fflush(stdout); //一定要加上ffflush, 因为中断是行缓冲的
                sleep(rand()%3);
                cout << 'X';
                fflush(stdout);
                //临界区结束
                sem_V(semid);
                sleep(rand()%3);
            }
        }
        else                //父进程
        {
            srand(getpid());
            while (count --)
            {
                sem_P(semid);
                //临界区开始
                cout << 'O';
                fflush(stdout);
                sleep(rand()%3);
                cout << 'O';
                fflush(stdout);
                //临界区结束
                sem_V(semid);
                sleep(rand()%3);
            }
            wait(NULL);
            sem_delete(semid);
        }
    
        return 0;
    }

    实践2: 信号量集解决哲学家进餐问题

       假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃饭,所以假设哲学家必须用两只餐叉吃东西, 而且他们只能使用自己左右手边的那两只餐叉。

    /** 
    解决的方法采用的是: 只有左右两个刀叉都能够使用时,才拿起两个刀叉
    实现了有死锁和无死锁的两种形式的wait_2fork(见下)
    **/
    
    int semid;
    //没有死锁的wait
    void wait_2fork(unsigned short no)
    {
        unsigned short left = no;
        unsigned short right = (no+1)%5;
        struct sembuf sops[2] = {{left, -1, 0}, {right, -1, 0}};
        //同时获取左右两把刀叉
        if (semop(semid, sops, 2) == -1)
            err_exit("wait_2fork error");
    }
    /*
    //有死锁的wait
    void wait_2fork(unsigned short no)
    {
        unsigned short left = no;
        unsigned short right = (no+1)%5;
        struct sembuf sops = {left, -1, 0};
        //获取左边的刀叉
        if (semop(semid, &sops, 1) == -1)
            err_exit("wait_2fork error");
        sleep(4);   //沉睡几秒, 加速死锁的产生
        sops.sem_num = right;
        //获取右边的刀叉
        if (semop(semid, &sops, 1) == -1)
            err_exit("wait_2fork error");
    }
    */
    //释放两把刀叉
    void signal_2fork(unsigned short no)
    {
        unsigned short left = no;
        unsigned short right = (no+1)%5;
        struct sembuf sops[2] = {{left, 1, 0}, {right, 1, 0}};
        if (semop(semid, sops, 2) == -1)
            err_exit("signal_2fork error");
    }
    //哲学家
    void philosopher(unsigned short no)
    {
        srand(time(NULL));
        while (true)
        {
            cout << no << " is thinking" << endl;
            sleep(rand()%5+1);
            cout << no << " is hunger" << endl;
            wait_2fork(no); //获取两把刀叉
            //进餐
            cout << "++ " << no << " is eating" << endl;
            sleep(rand()%5+1);
            signal_2fork(no);//释放两把刀叉
        }
    }
    int main()
    {
        // 创建一个信号量集: 里面包含5个信号量
        semid = semget(IPC_PRIVATE, 5, IPC_CREAT|0666);
        if (semid == -1)
            err_exit("semget error");
    
        //将每个信号量都设初值为1
        union semun su;
        su.val = 1;
        for (int i = 0; i < 5; ++i)
            if (semctl(semid, i, SETVAL, su) == -1)
                err_exit("semctl SETVAL error");
    
        //创建四个子进程, 将每个进程的编号设定为no
        pid_t pid;
        unsigned short no = 0;
        for (unsigned short i = 0; i < 4; ++i)
        {
            pid = fork();
            if (pid == -1)
                err_exit("fork error");
            else if (pid == 0)
            {
                no = i+1;
                break;
            }
        }
    
        // 最后五个进程(4个子进程+1个父进程)都会汇集到此处,
        // 每个进程代表着一个哲学家,编号no: 0~4
        philosopher(no);
    
        return 0;
    }

  • 相关阅读:
    java 面向对象(六):类结构 方法(三) java的值传递机制
    java 面向对象(五):类结构 方法(二) 关键字:return;方法的重载;可变个数形参的方法
    java 面向对象(四):类结构 方法(一)
    java 面向对象(三):类结构 属性
    java 面向对象(二):JVM内存结构
    java 面向对象(一):类与对象
    java 基本语法(十三) 数组(六)数组的常见异常
    java 基本语法(十二) 数组(五)Arrays工具类的使用
    java 基本语法(十一) 数组(四)数组的常见算法
    java 基本语法(十) 数组(三) 二维数组
  • 原文地址:https://www.cnblogs.com/itrena/p/5926955.html
Copyright © 2011-2022 走看看