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;
}

时间: 2024-08-07 08:15:30

Linux IPC实践(12) --System V信号量(2)的相关文章

Linux IPC实践(11) --System V信号量(1)

信号量API #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg); int semctl(int semid, int semnum, int cmd, ...); int semop(int semid, struct sembuf *sops, unsigned nsops); semget int

Linux IPC实践(9) --System V共享内存

共享内存API #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); void *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr); int shmctl(int shmid, int cmd, struct shmid_ds *buf); //

Linux IPC实践(6) --System V消息队列(3)

消息队列综合案例 消息队列实现回射客户/服务器 server进程接收时, 指定msgtyp为0, 从队首不断接收消息 server进程发送时, 将mtype指定为接收到的client进程的pid client进程发送的时候, mtype指定为自己进程的pid client进程接收时, 需要将msgtyp指定为自己进程的pid, 只接收消息类型为自己pid的消息; // client/server进程接收/发送的数据结构 const int MSGMAX = 8192; struct msgBuf

Linux IPC实践(13) --System V IPC综合实践

实践:实现一个先进先出的共享内存shmfifo 使用消息队列即可实现消息的先进先出(FIFO), 但是使用共享内存实现消息的先进先出则更加快速; 我们首先完成C语言版本的shmfifo(基于过程调用), 然后在此基础上实现C++版本的ShmFifo, 将1块共享内存与3个信号量(1个mutext信号量, 1个full信号量, 1个empty信号量)封装成一个类ShmFifo, 然后编写各自的测试代码; shmfifo说明: 将申请到的共享内存作为一块缓冲区, 将该内存的首部(p_shm到p_pa

Linux IPC实践(4) --System V消息队列(1)

消息队列概述 消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法(仅局限于本机); 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值. 消息队列也有管道一样的不足: (1)每个消息的最长字节数的上限(MSGMAX); (2)系统中消息队列的总条数也有一个上限(MSGMNI); (3)每个消息队列所能够保存的总字节数是有上限的(MSGMNB) . 查看系统限制 cat /proc/sys/kernel/msgmax  #最大消息长度限制 cat /proc/sys

Linux IPC实践(5) --System V消息队列(2)

消息发送/接收API msgsnd函数 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 参数 msgid: 由msgget函数返回的消息队列标识码, 也可以是通过ipcs命令查询出来的一个已经存在的消息队列的ID号 msgp:是一个指针,指针指向准备发送的消息, msgsz:是msgp指向的消息长度, 注意:这个长度不含保存消息类型的那个long int长整型 msgflg:控制着当前消息队列满或到达系统上限时

linux进程通信之SYSTEM V信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有.信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒. 一.信号量的分类: 在学习信号量之前,我们必须先知道--Linux提供两种信号量: (1) 内核信号量,由内核控制路径使用. (2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量. POSIX信号量又分为有名信号量和无名信号量.有名信号量,其值保存在文件

linux进程间通讯-System V IPC 信号量

进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信--使用信号.下面就进入信号量的讲解. 一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更新的代码需要独占式地执行.而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在

Linux程序设计学习笔记----System V进程间通信(信号量)

关于System V Unix System V,是Unix操作系统众多版本中的一支.它最初由AT&T开发,在1983年第一次发布,因此也被称为AT&T System V.一共发行了4个System V的主要版本:版本1.2.3和4.System V Release 4,或者称为SVR4,是最成功的版本,成为一些UNIX共同特性的源头,例如"SysV 初始化脚本"(/etc/init.d),用来控制系统启动和关闭,System V Interface Definitio