訊號量、訊息佇列、共享記憶體複習

FreeeLinux發表於2017-02-08

訊號量程式碼

使用訊號量實現父子程式間同步:

#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

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) */
};

void pv(int sem_id, int op) 
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = op; 
    sem_b.sem_flg = SEM_UNDO;
    semop(sem_id, &sem_b, 1); 
}

int main(int argc, char** argv)
{
    int sem_id = semget(IPC_PRIVATE, 1, 0666);

    union semun sem_un;
    sem_un.val = 1;
    semctl(sem_id, 0, SETVAL, sem_un);

    pid_t pid = fork();
    if(pid < 0)
        return -1;
    else if(pid == 0){
        printf("child try to get binary sem\n");
        pv(sem_id, -1);
        printf("child get the sem and would release it after 5 seconds\n");
        sleep(5);
        pv(sem_id, 1);
        exit(0);
    }
   else{
        printf("parent try to get binary sem\n");
        pv(sem_id, -1);
        printf("parent get the sem and would release it agter 5 seocnds\n");
        sleep(5);
        pv(sem_id, 1);
    }

    waitpid(pid, NULL, 0);
    semctl(sem_id, 0, IPC_RMID, sem_un);
    return 0;
}

訊息佇列程式碼

使用訊息佇列實現簡單的程式間交談。

utili.h標頭檔案:

#pragma once

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

const char* const PATH_NAME = "pathname";
const int PRO_ID = 0xff;
const int BUFFER_SIZE = 256;

struct message {
    long msg_type;
    char msg_text[BUFFER_SIZE];
};

server.cpp:

#include "utili.h"

const int SERVER_MSG_SND_TYPE = 10000;
const int SERVER_MSG_RCV_TYPE = 2;

int main()
{
    int msg_id;

    if(msg_id = msgget(ftok(PATH_NAME, PRO_ID), IPC_CREAT | IPC_EXCL | 0755) < 0){ 
        perror("what");
        exit(-1);
    }   

    printf("msg_id = %d\n", msg_id);

    message msg;
    for(; ;){ 
        printf("server:>");
        scanf("%s", msg.msg_text);
         if(strncasecmp(msg.msg_text, "quit", 4) == 0)
            break;
        msg.msg_type = SERVER_MSG_SND_TYPE;
        msgsnd(msg_id, &msg, strlen(msg.msg_text)+1, 0); 

        msgrcv(msg_id, &msg, BUFFER_SIZE, SERVER_MSG_RCV_TYPE, 0); 
        printf("client:>%s\n", msg.msg_text);
    }   
    msgctl(msg_id, IPC_RMID, 0); 

    while(1);
    exit(0);

客戶端:

#include "utili.h"

const int CLIENT_MSG_SND_TYPE = 2;
const int CLIENT_MSG_RCV_TYPE = 10000;

int main()
{
    int msg_id;
    key_t msg_key = ftok(PATH_NAME, PRO_ID);
    if(msg_key < 0){ 
        perror("what1");
        exit(-1);
    }   
    printf("msg_key = %d\n", msg_key);
    if((msg_id = msgget(msg_key, 0)) < 0){ 
        perror("what2");
        exit(-1);
    }   

    printf("mid_id = %d\n", msg_id);

     message msg;
    for(; ;){ 
        msgrcv(msg_id, &msg, BUFFER_SIZE, CLIENT_MSG_RCV_TYPE, 0); 
        printf("serverhehf:>%s\n", msg.msg_text);

        printf("clientwowo:>");
        scanf("%s", msg.msg_text);
        if(strncasecmp(msg.msg_text, "quit", 4) == 0)
            break;

        msg.msg_type = CLIENT_MSG_SND_TYPE;
        msgsnd(msg_id, &msg, strlen(msg.msg_text)+1, 0);
    }
    msgctl(msg_id, IPC_RMID, 0);
    exit(0);
}

雙反接收和傳送的訊息型別不同,可以利用這點從訊息佇列中取自己的訊息。

昨天晚上寫訊息佇列這個程式坑了我好長時間,沒有使用perror判斷錯誤外加沒有注意到ftok函式針對的path必須是真實存在的檔案或目錄,然後找BUG找了一個半多小時。

共享記憶體程式碼

這裡使用共享記憶體來實現伺服器和客戶端通訊,並且由於共享了讀寫,使用訊號量來同步。

utili.h:

#include <stdio.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>

#define PATHNAME "newfile"
#define PRO_ID   0xff

#define err_exit(m) \
        do{ \
            perror(m); \
            exit(1); \
        }while(0)

#define USE_SEM

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

server.cpp:

#include "utili.h"

int main(int argc, char** argv)
{
    int shm_id;
    if( (shm_id = shmget(ftok(PATHNAME, PRO_ID), 1024, 
                                    IPC_CREAT|IPC_EXCL|0755)) < 0)
        err_exit("shmget err.");

    char *ch;
    if( (ch = (char *)shmat(shm_id, NULL, 0)) == (void *)-1)
        err_exit("shmat err");

#ifdef USE_SEM
    int sem_id;
    if( (sem_id = semget(ftok(PATHNAME, PRO_ID), 2, IPC_CREAT|IPC_EXCL|0755)) < 0){
        shmdt(ch);
        shmctl(shm_id, IPC_RMID, 0);
        err_exit("semget err.");
    }

    union semun init;
    init.val = 0;
    semctl(sem_id, 0, SETVAL, init);  //0
    semctl(sem_id, 1, SETVAL, init);  //1
    struct sembuf p = {0, -1, 0}, v = {1, 1, 0};
#endif

    for(; ;){
        printf("server:>");
        scanf("%s", ch);
        if(strncasecmp(ch, "quit", 4) == 0){
            shmdt(ch);
            break;
        }
        semop(sem_id, &v, 1);

        semop(sem_id, &p, 1);
        printf("client:>%s\n", ch);
    }

    int res;
    if( (res = shmctl(shm_id, IPC_RMID, 0)) < 0)
        err_exit("remove err.");

    return 0;
}

client.cpp:

#include "utili.h"

int main(int argc, char** argv)
{
    int shm_id;
    if( (shm_id = shmget(ftok(PATHNAME, PRO_ID), 0, 0)) < 0)
        err_exit("shmget err.");

    char *ch;
    if( (ch = (char *)shmat(shm_id, NULL, 0)) == (void *)-1)
        err_exit("shmat err");

#ifdef USE_SEM
    int sem_id;
    if( (sem_id = semget(ftok(PATHNAME, PRO_ID), 0, 0)) < 0){
        shmdt(ch);
        shmctl(shm_id, IPC_RMID, 0);
        err_exit("semget err.");
    }

    union semun init;
    init.val = 0;
    semctl(sem_id, 0, SETVAL, init);  //0
    semctl(sem_id, 1, SETVAL, init);  //1
    struct sembuf p = {1, -1, 0}, v = {0, 1, 0};
#endif

    for(; ;){
        semop(sem_id, &p, 1);   
        printf("server:>%s\n", ch);

        printf("cli:>");
        scanf("%s", ch);
        if(strncasecmp(ch, "quit", 4) == 0){
            shmdt(ch);
            break;
        }   
        semop(sem_id, &v, 1);
    }

    int res;
    if( (res = shmctl(shm_id, IPC_RMID, 0)) < 0)
        err_exit("remove err.");

    return 0;
}

相關文章