日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

IPC之信号量·即时通讯小程序(三)

發(fā)布時(shí)間:2023/12/1 综合教程 37 生活家
生活随笔 收集整理的這篇文章主要介紹了 IPC之信号量·即时通讯小程序(三) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

系統(tǒng)運(yùn)維

上次說(shuō)到解決并發(fā)的問(wèn)題,需要用到信號(hào)量。下面,簡(jiǎn)單復(fù)習(xí)一下。

信號(hào)量

信號(hào)量是一種變量,它只能取正整數(shù)值,對(duì)這些正整數(shù)只能進(jìn)行兩種操作:等待和信號(hào)。(在我的理解,信號(hào)量就是用來(lái)訪問(wèn)一些臨界資源而設(shè)計(jì)的)
用兩種記號(hào)來(lái)表示信號(hào)量的這兩種操作:
P(semaphore variable) 代表等待(請(qǐng)求資源)
V(semaphore variable) 代表信號(hào)(釋放資源)

信號(hào)量的分類

最簡(jiǎn)單的信號(hào)量是一個(gè)只能取“0”和“1”值的變量,也就是人們常說(shuō)的“二進(jìn)制信號(hào)量”

可以取多個(gè)正整數(shù)值的信號(hào)量叫做“通用信號(hào)量”

P、V操作

假設(shè)我們有一個(gè)信號(hào)量變量sv,則pv操作的定義如下
P(sv):如果sv的值大于零,就給它減去1;如果sv的值等于零,就掛起該進(jìn)程的執(zhí)行
V(sv): 如果有其他進(jìn)程因等待sv變量而被掛起,就讓它恢復(fù)執(zhí)行;如果沒(méi)有進(jìn)程因等待sv變量而被掛起,就給它加1

信號(hào)量函數(shù)

需要用到的函數(shù)如下:

#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/sem.h>

int semget(key_t key, int nsems, int semflg);

作用:創(chuàng)建一個(gè)新的信號(hào)量或者取得一個(gè)現(xiàn)有的信號(hào)量的關(guān)鍵字
key: 是一個(gè)整數(shù)值,不相關(guān)的進(jìn)程將通過(guò)這個(gè)值去訪問(wèn)同一個(gè) 信號(hào)量
num_sems:需要使用的信號(hào)量個(gè)數(shù),它幾乎總是取值為1
sem_flags:是一組標(biāo)志,其作用與open函數(shù)的各種標(biāo)志很相似,它低端的九個(gè)位是該信號(hào)量的權(quán)限,其作用相當(dāng)于文件的訪問(wèn)權(quán)限,可以與鍵值IPC_CREATE做按位的OR操作以創(chuàng)建一個(gè)新的信號(hào)量
成功時(shí)將返回一個(gè)正數(shù)值,它就是其他信號(hào)量函數(shù)要用到的那個(gè)標(biāo)識(shí)碼,如果失敗,將返回-1

int semop(int semid, struct sembuf *sops, unsigned nsops);

作用:改變信號(hào)量的鍵值(就是用來(lái)執(zhí)行PV操作的)
semid:是該信號(hào)量的標(biāo)識(shí)碼,也就是semget函數(shù)的返回值
sops:是個(gè)指向一個(gè)結(jié)構(gòu)數(shù)值的指針
nsop:進(jìn)行操作信號(hào)量的個(gè)數(shù),即sops結(jié)構(gòu)變量的個(gè)數(shù),需大于或等于1。最常見設(shè)置此值等于1,只完成對(duì)一個(gè)信號(hào)量的操作
Semop調(diào)用的一切動(dòng)作都是一次性完成的,這是為了避免出現(xiàn)因使用了多個(gè)信號(hào)量而可能發(fā)生的競(jìng)爭(zhēng)現(xiàn)象
其中,sembuf的結(jié)構(gòu)體如下:

sem_num是信號(hào)量的編號(hào),如果你的工作不需要使用一組信號(hào)量,這個(gè)值一般就取為0。
sem_op是信號(hào)量一次PV操作時(shí)加減的數(shù)值,一般只會(huì)用到兩個(gè)值,一個(gè)是“-1”,也就是P操作,等待信號(hào)量變得可用;另一個(gè)是“+1”,也就是我們的V操作,發(fā)出信號(hào)量已經(jīng)變得可用
sem_flag通常被設(shè)置為SEM_UNDO.她將使操作系統(tǒng)跟蹤當(dāng)前進(jìn)程對(duì)該信號(hào)量的修改情況

    int semctl(int semid, int semnum, int cmd, ...);

sem_id: 是由semget函數(shù)返回的一個(gè)信號(hào)量標(biāo)識(shí)碼
sem_num: 信號(hào)量的編號(hào),如果在工作中需要使用到成組的信號(hào)量,就要用到這個(gè)編號(hào);它一般取值為0,表示這是第一個(gè)也是唯一的信號(hào)量
comman:將要采取的操作動(dòng)作
如果還有第四個(gè)參數(shù),那它將是一個(gè)“union semun”復(fù)合結(jié)構(gòu).
先說(shuō)一下cmd,有以下幾個(gè):

其中,比較常用的是以下兩個(gè):

SETVAL:用來(lái)把信號(hào)量初始化為一個(gè)已知的值,這個(gè)值在semun結(jié)構(gòu)里是以val成員的面目傳遞的。

IPC_RMID:刪除一個(gè)已經(jīng)沒(méi)有人繼續(xù)使用的信號(hào)量標(biāo)識(shí)碼
再說(shuō)一下第四個(gè)參數(shù),這個(gè)函數(shù)比較特別,當(dāng)有四個(gè)參數(shù)時(shí),是一個(gè)“union semun”復(fù)合結(jié)構(gòu).(頭文件里可能沒(méi)有,最好自己重新定義一下)

       union semun {
           int              val;    /* Value for SETVAL */
           struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
           unsigned short  *array;  /* Array for GETALL, SETALL */
           struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                       (Linux-specific) */
       };

案例分析

做個(gè)小練習(xí),租賃汽車?yán)印?br />共5個(gè)廠,每個(gè)廠分別有汽車數(shù):3 7 5 0 6。考慮租車問(wèn)題(訪問(wèn)臨界資源)
factory.c:

#include < stdio.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/sem.h>

/*
租賃汽車?yán)?5個(gè)廠,每個(gè)廠分別有汽車數(shù):3 7 5 0 6
=>信號(hào)量個(gè)數(shù) = 5 , 信號(hào)量的值分別為:3 7 5 0 6 ,信號(hào)量編號(hào)從0開始
*/
union semun {
     int              val;    /* Value for SETVAL */
     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
     unsigned short  *array;  /* Array for GETALL, SETALL */
     struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                 (Linux-specific) */
};

int main()
{
    int i;
    int sem_id;
    int values[5] = {3,7,5,0,6};
    union semun value = {0};
    //創(chuàng)建信號(hào)量
    sem_id = semget(1000,5,0);
    if(sem_id != -1)
    {
        for(i = 0 ;i < 5;i++)
        {
            semctl(sem_id , i , IPC_RMID);
        }

    }
sem_id = semget(1000,5,IPC_CREAT);
printf(%d\\n,sem_id);
if(sem_id == -1)
{
    perror(producer semget);
    return -1;
}
//設(shè)置信號(hào)量
for(i = 0 ; i < 5 ; i++)
{
    printf(%d ,semctl(sem_id,i,GETVAL));
}
printf(\\n);
for(i = 0 ;i < 5; i++)
{
    value.val = values[i];
    //打印信號(hào)量
    semctl(sem_id,i,SETVAL,value.val);
}
for(i = 0 ; i < 5 ; i++)
{
    printf(%d ,semctl(sem_id,i,GETVAL));
}

return 0;
}

client.c:

#include < stdio.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/sem.h>

/*
 struct sembuf
 {
     unsigned short sem_num;        // semaphore number 
     short          sem_op;             // semaphore operation 
     short          sem_flg;            // operation flags 

     Flags recognized  in  sem_flg  are  
     IPC_NOWAIT  and   SEM_UNDO. 
*/

int main()
{
    int sem_id;
    struct sembuf buf = {0};
    //打開信號(hào)量
    sem_id = semget(1000,5,0);
    if(sem_id == -1)
    {
        perror(consumer semget);
        return -1;
    }
    printf(waiting...\\n);
    //借車
    buf.sem_num = 0;    //信號(hào)量編號(hào)
    buf.sem_op = -1;    //請(qǐng)求資源
    buf.sem_flg = SEM_UNDO; //自動(dòng)釋放
    semop(sem_id,&buf,1); //第三個(gè)參數(shù)必須是1
    printf(get a car.\\n);

//正在借
sleep(20);

//還車
buf.sem_op = +1;    //釋放資源
semop(sem_id,&buf,1);
printf(back a car.\\n);

return 0;
}

運(yùn)行結(jié)果:
當(dāng)執(zhí)行第三個(gè)客戶端的時(shí)候,會(huì)一直等待,知道第一輛車還了之后。

生產(chǎn)者、消費(fèi)者問(wèn)題

學(xué)習(xí)信號(hào)量最經(jīng)典的幾個(gè)問(wèn)題,都可以拿來(lái)練練。生產(chǎn)者消費(fèi)者問(wèn)題、讀者寫者問(wèn)題、哲學(xué)家進(jìn)餐問(wèn)題等都是一樣的。
要求:產(chǎn)品數(shù)量初始為0,生產(chǎn)者負(fù)責(zé)生產(chǎn),即+1,消費(fèi)者負(fù)責(zé)消費(fèi),即-1。倉(cāng)庫(kù)最多只能存放10個(gè)產(chǎn)品
下面給出生產(chǎn)者消費(fèi)者問(wèn)題的代碼:
(我使用的是兩個(gè)信號(hào)量,通常課本上會(huì)使用一個(gè)信號(hào)量,可以自己試試)
public.c:

#ifndef  _PUBLIC_H_
#define  _PUBLIC_H_

#include < stdio.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/sem.h>

unsigned short values[2] = {10,0};

union semun {
     int              val;    /* Value for SETVAL */
     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
     unsigned short  *array;  /* Array for GETALL, SETALL */
     struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                 (Linux-specific) */
};

#endif

生產(chǎn)者

#include  public.h

//信號(hào)量:控制進(jìn)程間同步問(wèn)題,共同的資源,資源的數(shù)量(信號(hào)量的值)>=0。
//P等待/請(qǐng)求 -1,V信號(hào)/釋放,+1
//semget(創(chuàng)建、打開)\\semctl(刪除、初始化)、semop(PV)
int main()
{

int semid;
//兩個(gè)信號(hào)量,一個(gè)生產(chǎn),初始化10,一個(gè)消費(fèi),初始化為0
//生產(chǎn)了一件商品后,生產(chǎn)者(第零個(gè)信號(hào)量-1),消費(fèi)者(第一個(gè)信號(hào)量+1)
//unsigned short values[2] = {10,0};

union semun sem = {0};
struct sembuf buf1 = {0};
struct sembuf buf2 = {0};

//1.創(chuàng)建信號(hào)量
semid = semget(1000,0,0);
if(semid == -1)
{
    semid = semget(1000,2,IPC_CREAT);
    if(semid == -1)
    {
        perror(semget open.);
        return 1;
    }
}

//2.信號(hào)量初始化
sem.array = values;
semctl(semid,0,SETALL,sem);

//3.pv操作
while(1)
{
    //生產(chǎn),信號(hào)量[0]減1 buf1
    buf1.sem_num = 0;
    buf1.sem_op = -1;
    buf1.sem_flg = SEM_UNDO;
//      printf(can produce?\\n);
    semop(semid,&buf1,1);//最后一個(gè)參數(shù),只要>0就行
//      printf(yes.\\n);

    sleep(1);
    //生產(chǎn)完一件產(chǎn)品,信號(hào)量[1]加1 buf2
    buf2.sem_num = 1;
    buf2.sem_op = +1;
    buf2.sem_flg = SEM_UNDO;
    semop(semid,&buf2,1);
    printf(finish produce.now: %d\\n,values[1]);
//      printf(all product: %d\\n,sem.array[1]);
}
return 0;
}

消費(fèi)者

    #include  public.h

    int main()
    {
    int semid;
    struct sembuf buf1 = {0};
    struct sembuf buf2 = {0};

    semid = semget(1000,0,0);
    if(semid == -1)
    {
        perror(semget open);
        return 1;
    }

    //負(fù)責(zé)消費(fèi)
    while(1)
    {
        //能否消費(fèi) 信號(hào)量[1] buf2
        buf2.sem_num = 1;
        buf2.sem_op = -1;
        buf2.sem_flg = SEM_UNDO;
        semop(semid,&buf2,1);

        //消費(fèi) 
        sleep(3);

        //信號(hào)量[0] buf1
        buf1.sem_num = 0;
        buf1.sem_op = +1;
        buf1.sem_flg = SEM_UNDO;
        semop(semid,&buf1,1);
        printf(consume one product.\\n);
    }
}

運(yùn)行結(jié)果:
先運(yùn)行./producer。 當(dāng)生產(chǎn)到10個(gè)產(chǎn)品之后,就會(huì)阻塞。此時(shí)運(yùn)行./consumer,每3s取走一個(gè)產(chǎn)品,1s后生產(chǎn)者會(huì)生產(chǎn)一個(gè)產(chǎn)品。

即時(shí)通訊小程序

現(xiàn)在,可以繼續(xù)我們的小程序了。有了以上兩個(gè)練習(xí)之后,就更容易理解了。跟生產(chǎn)者消費(fèi)者問(wèn)題很類似。
我們?yōu)樽x寫設(shè)置兩個(gè)信號(hào)量,一個(gè)控制讀,一個(gè)控制寫。寫信號(hào)量初始化為1,寫完后-1,變成0。讀信號(hào)量初始化為0,寫操作后,變成在線用戶數(shù)-1。如果所有用戶都讀完了,讀信號(hào)量為0,寫信號(hào)量為1。
public.h:

#ifndef _PUBLIC_H_
#define _PUBLIC_H_

#include < stdio.h>
#include < string.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/msg.h>
#include < sys/shm.h>
#include < signal.h>
#include < sys/sem.h>
#include < string>
#include < map>
#include < iostream>
using namespace std;

//用戶信息結(jié)構(gòu)體
typedef struct user_t
{
    pid_t pid;
    char uname[10]; //后面加上用戶名不重名、密碼驗(yàn)證
}USER_T;

//登錄消息隊(duì)列結(jié)構(gòu)體
typedef struct login_msg_t
{
    long type;
    USER_T user;
}LMSG_T;

//聊天消息結(jié)構(gòu)體
typedef struct msg_t
{
    USER_T user;
    char acMsg[100];
}MSG_T;

//消息隊(duì)列:用戶登錄
#define LOGIN_TYPE          1
#define EXIT_TYPE           2
#define MSG_KEY             1000
#define MSG_SIZE            sizeof(LMSG_T)-sizeof(long)

//共享內(nèi)存:用戶列表(空閑塊:0-空閑,1-占用)
#define SHM_USER_KEY        1001
#define MAX_USER            100
#define SHM_USER_SIZE       MAX_USER + MAX_USER * sizeof(USER_T)

//共享內(nèi)存:聊天內(nèi)容
#define SHM_MSG_KEY         1002
#define SHM_MSG_SIZE        sizeof(MSG_T)

//信號(hào):更新用戶列表,讀消息
#define SIGNAL_USERS        34
#define SIGNAL_CHAT         35

//讀寫信號(hào)量
#define SEM_KEY             1003

union semun {
   int              val;    /* Value for SETVAL */
   struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
   unsigned short  *array;  /* Array for GETALL, SETALL */
   struct seminfo  *__buf;  /* Buffer for IPC_INFO
                               (Linux-specific) */
};
//兩個(gè)信號(hào)量,一個(gè)控制讀,一個(gè)控制寫
//寫信號(hào)量初始化為1,寫完后-1,變成0
//讀信號(hào)量初始化為0,寫操作后,變成在線用戶數(shù)-1
//如果所有用戶都讀完了,讀信號(hào)量為0,寫信號(hào)量為1
union semun sem = {0};
struct sembuf buf1 = {0};   //寫
struct sembuf buf2 = {0};   //讀
#endif

server.cpp:

#include  public.h

int main()
{
    int msg_id;
    int shm_id;
    LMSG_T loginMsg = {0};
    char *userAddr;
    USER_T *pUser;  //用戶真正寫入的地址
    map<int,string> userMap;    //用戶列表
    map<int,string>::iterator it;
    int i;

    /*1、創(chuàng)建消息隊(duì)列:用戶登錄*/
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        msg_id = msgget(MSG_KEY,IPC_CREAT);
        if (msg_id == -1)
        {
            perror(server msgget);
            return -1;
        }
    }

    /*2、創(chuàng)建共享內(nèi)存:用戶列表*/
    shm_id = shmget(SHM_USER_KEY,0,0);
    if (shm_id != -1)
    {//已經(jīng)存在,刪除
        shmctl(shm_id,IPC_RMID,NULL);
    }
    shm_id = shmget(SHM_USER_KEY,SHM_USER_SIZE,IPC_CREAT);
    userAddr = (char *)shmat(shm_id,NULL,0);//映射
    pUser = (USER_T *)(userAddr + MAX_USER);
    memset(userAddr,0,SHM_USER_SIZE);//初始化

    /*3、創(chuàng)建共享內(nèi)存:聊天信息*/
    int shm_msg_id = shmget(SHM_MSG_KEY,0,0);
    if (shm_msg_id != -1)
    {
        shmctl(shm_msg_id,IPC_RMID,NULL);
    }
    shm_msg_id = shmget(SHM_MSG_KEY,SHM_MSG_SIZE,IPC_CREAT);
    char *msgAddr = (char *)shmat(shm_msg_id,NULL,0);
    memset(msgAddr,0,SHM_MSG_SIZE);

    /*4、創(chuàng)建信號(hào)量*/
    int sem_id;
    sem_id = semget(SEM_KEY,0,0);
    if (sem_id != -1)
    {
        semctl(sem_id,0,IPC_RMID);
        semctl(sem_id,1,IPC_RMID);
    }
    sem_id = semget(SEM_KEY,2,IPC_CREAT);
    //初始化信號(hào)量的值
    sem.val = 1;
    semctl(sem_id,0,SETVAL,sem);    //寫
    sem.val = 0;
    semctl(sem_id,1,SETVAL,sem);    //讀

    //一直監(jiān)聽,是否有用戶上線
    while (1)
    {
        memset(&loginMsg,0,sizeof(LMSG_T));
        msgrcv(msg_id,&loginMsg,MSG_SIZE,0,0);  //任何消息都接收
        switch(loginMsg.type)
        {
        case LOGIN_TYPE:
            //登錄
            cout<<client <<loginMsg.user.uname<<:<<loginMsg.user.pid<< is logining...<<endl;
            //2.1 將登錄信息寫入共享內(nèi)存(先找到空閑塊)
            for (i = 0 ; i < MAX_USER ; i++)
            {
                if (*(userAddr + i) == 0)
                {
                    //空閑
                    break;
                }
            }
            if (i < MAX_USER)
            {
                *(userAddr + i) = 1;
                *(pUser + i) = loginMsg.user;
                userMap.insert( pair<int,string>(loginMsg.user.pid,loginMsg.user.uname) );
            }
            else
            {
                cout<<online users are full.\\n<<endl;
                return 1;
            }
            //2.2 發(fā)消息通知所有在線用戶
            for (it = userMap.begin();it != userMap.end();it++)
            {
                kill((*it).first,SIGNAL_USERS);
            }

            break;
        case EXIT_TYPE:
            //退出
            cout<<client <<loginMsg.user.uname<<:<<loginMsg.user.pid<< is exiting...<<endl;
            for (i = 0 ; i < MAX_USER ; i++)
            {
                if ((pUser+i)->pid == loginMsg.user.pid)
                {
                    *(userAddr+i) = 0;
                    break;
                }
            }
            for (it = userMap.begin();it != userMap.end();it++)
            {
                if ((*it).first == loginMsg.user.pid)
                {
                    continue;   //自己退出,不用再通知自己
                }
                kill((*it).first,SIGNAL_USERS);
            }
            break;
        }

    }

    return 0;
}

client.cpp:

#include  public.h

char *userAddr;
USER_T *pUser;

char *msgAddr;
MSG_T *pMsg;

map<int,string> userMap;    //用戶列表
map<int,string>::iterator it;

int sem_id;

void PrtUserList(int sig_no)
{
    //讀取共享內(nèi)存里的用戶列表數(shù)據(jù)
    userMap.clear();
    cout<<==== online users ====<<endl;
    for (int i = 0 ;i < MAX_USER ; i++)
    {
        if(*(userAddr + i) == 1)
        {
            cout<<(pUser + i)->uname<<endl;
            userMap.insert(pair<int,string>( (pUser+i)->pid, (pUser+i)->uname ));
        }
    }
    cout<<========= <<userMap.size()<< =========<<endl;
}

void GetChatMsg(int sig_no)
{
    //讀取共享內(nèi)存里的聊天內(nèi)容
    sleep(10);
    MSG_T msg = {0};
    msg = *pMsg;
    cout<<receive msg from <<msg.user.uname<< : <<msg.acMsg<<endl;

    // 4.3 讀完之后:讀信號(hào)量-1,讀到最后一個(gè)用戶,讀信號(hào)量為0,寫信號(hào)量再設(shè)為1
    buf2.sem_num = 1;
    buf2.sem_op = -1;
    buf2.sem_flg = SEM_UNDO;
    semop(sem_id,&buf2,1);

    if (semctl(sem_id,1,GETVAL) == 0) //讀信號(hào)量為0
    {
//      printf(done.\\n);
        sem.val = 1;
        semctl(sem_id,0,SETVAL,sem);
    }
}

int main()
{
    char acOrder[20] = ;
    int msg_id;
    LMSG_T loginMsg = {0};
    char uname[10] = ;
    int shm_id;
    char toWho[10] = ;    //聊天對(duì)象
    MSG_T msg = {0};    //聊天消息結(jié)構(gòu)體
    char acMsg[100] = ;   //聊天內(nèi)容

    cout<<------------onlineChat-------------<<endl;
    cout<<username:;
    cin>>uname;

    //2.3 注冊(cè)消息(放在最前面)
    signal(SIGNAL_USERS,PrtUserList);
    signal(SIGNAL_CHAT,GetChatMsg);

    /*2、打開用戶列表共享內(nèi)存(要比寫消息隊(duì)列早)*/
    shm_id = shmget(SHM_USER_KEY,0,0);
    if (shm_id == -1)
    {
        perror(client userlist shmget);
        return -1;
    }
    userAddr = (char*)shmat(shm_id,NULL,0);
    pUser = (USER_T*)(userAddr + MAX_USER);

    /*3、打開聊天信息共享內(nèi)存*/
    int shm_msg_id = shmget(SHM_MSG_KEY,0,0);
    if (shm_msg_id == -1)
    {
        perror(client chatmsg shmget);
        return -1;
    }
    msgAddr = (char *)shmat(shm_msg_id,NULL,0);
    pMsg = (MSG_T *)msgAddr;

    /*4、打開信號(hào)量*/
    sem_id = semget(SEM_KEY,0,0);
    if (sem_id == -1)
    {
        perror(client semget);
        return -1;
    }

    /*1、打開消息隊(duì)列*/
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        perror(client msgget);
        return -1;
    }
    //登錄,寫消息隊(duì)列
    loginMsg.type = LOGIN_TYPE;     //設(shè)置登錄的消息類型為1
    loginMsg.user.pid = getpid();
    memcpy(loginMsg.user.uname,uname,strlen(uname));
    cout<<loginMsg.user.uname<< is logining...<<endl;
    msgsnd(msg_id,&loginMsg,MSG_SIZE,0);

    //等待寫
    while(1)
    {
        putchar(\'#\');
        fflush(stdout);
        scanf(%s,acOrder);
        if (strcmp(acOrder,exit) == 0)    //退出
        {
            cout<<loginMsg.user.uname<< is exiting...<<endl;
            loginMsg.type = EXIT_TYPE;      //設(shè)置退出的消息類型為2
            msgsnd(msg_id,&loginMsg,MSG_SIZE,0);
            break;
        }
        else if (strcmp(acOrder,users) == 0)  //查看在線用戶列表
        {
            kill(getpid(),SIGNAL_USERS);
        }
        else if (strcmp(acOrder,chat) == 0)   //進(jìn)入聊天模式
        {
            cout<<to who: ;
            cin>>toWho;
            cout<<say: ;
            memset(acMsg,0,100);
            cin>>acMsg;

            // 4.1 寫之前:P(等待/請(qǐng)求)操作,寫信號(hào)量-1
            buf1.sem_num = 0;
            buf1.sem_op = -1;
            buf1.sem_flg = SEM_UNDO;
            semop(sem_id,&buf1,1);

            // 3.1 把聊天內(nèi)容寫進(jìn)共享內(nèi)存
            memcpy(msg.acMsg,acMsg,strlen(acMsg));
            msg.user = loginMsg.user;
            memcpy(msgAddr,&msg,SHM_MSG_SIZE);

            if (strcmp(toWho,all) == 0)   //群聊
            {

                // 4.2 寫之后:設(shè)置讀信號(hào)量為在線用戶數(shù)-1
                sem.val = userMap.size() - 1;
                semctl(sem_id,1,SETVAL,sem);

                //通知所有人去讀
                for (it = userMap.begin();it != userMap.end();it++)
                {
                    if ((*it).first != getpid())
                    {
                        kill((*it).first,SIGNAL_CHAT);
                    }
                }
            }
            else    //私聊
            {
                for (it = userMap.begin();it != userMap.end();it++)
                {
                    if (strcmp((*it).second.c_str() , toWho) == 0)
                    {
                        kill((*it).first,SIGNAL_CHAT);
                        break;
                    }
                }
            }
        }
        memset(acOrder,0,sizeof(acOrder));
    }

    //解除映射
    shmdt(&userAddr);
    shmdt(&msgAddr);

    return 0;
}

運(yùn)行結(jié)果:
運(yùn)行結(jié)果就不演示了,此時(shí),再sleep(),模擬并發(fā)的情況,就不會(huì)出現(xiàn)上次的問(wèn)題了,畢竟此時(shí)的聊天內(nèi)容的共享內(nèi)存已經(jīng)變成臨界資源了,用新號(hào)良控制之后,就不會(huì)有同時(shí)讀寫的問(wèn)題了。
未完待續(xù)....

總結(jié)

以上是生活随笔為你收集整理的IPC之信号量·即时通讯小程序(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。