Linux软件编程----进程间的相互通信

目录

 一.Linux进程间通信的方式:

1.管道:

     1.无名管道

    2.有名管道 

2.信号:

    1.信号类型:

    2.信号的处理方式:

        3.函数接口:

        1.signal

    4.定时器:

    5.kill 

3.消息队列:

1.步骤:

    1.创建IPC对象名称  ftok 

                    2.创建消息队列  msgget 

    3.发送消息  msgsnd

    4.接收消息   msgrcv 

    5.消息队列销毁 msgctl 

2.函数接口:

    1.ftok 

    2.msgget 

    3.msgsnd 

    4.msgrcv 

    5.msgctl 

4.共享内存:

    1.操作步骤:

       1.创建 key 值

       2.创建共享内存 

      3.映射共享内存

       4.解除映射

       5.销毁共享内存 

   2.函数接口:

        1.ftok 

        2.shmget 

        3.shmat

        4.shmdt

        5.shmctl 

5.信号灯:

    1.创建信号灯 

    2.向信号灯发送命令 

    3.对信号量完成申请和释放操作

6.信号灯和共享内存的使用

步骤:


Linux -> unix 
GNU  GNU's Not Unix 

SYS V  
BSD 

 

一.Linux进程间通信的方式:


    管道、信号 
    消息队列、共享内存、信号灯
    套接字 



1.管道:

 
    1.无名管道


        只能用于具有亲缘关系的进程间通信(父子进程)

        无名管道创建后,进程会获得操作管道的两个文件描述符,创建子进程时,子进程会拷贝得到父进程的操作无名管道的两个文件描述符,
        两个进程任务操作同一管道实现通信

        int pipe(int pipefd[2]);
        功能:
            创建一个用来通信的管道
        参数:
            pipefd:存放管道两端(读写)的文件描述符数组

             pipefd[0]:读   

             pipefd[1]:写
        成功返回0 
        失败返回-1 

        1.如果管道中至少有一个写端:
            如果管道中有数据,直接读出
            如果管道中没有数据,会阻塞等待直到有数据写入后读出
        
        2.如果管道中没有写端:
            如果管道中有数据,直接读出 
            如果管道中没有数据,不会阻塞直接继续向下执行

        3.如果管道中至少有一个读端:
            如果管道没有写满,直接写入
            如果管道中写满(64k),则阻塞等待,等有数据读出才能继续写入

        4.如果管道中没有读端:
            向管道中写入数据会产生管道破裂信号

        管道是一段内核缓存区

        write,read已经实现同步,read会一直阻塞等待写入。

#include "../head.h"

char tmpbuff[4096] = {0};

int main(void)
{
    pid_t pid;
    int pipefd[2];
    ssize_t nsize = 0;

    pipe(pipefd);

    pid = fork();
    if (-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if (0 == pid)
    {
        close(pipefd[0]);
#if 0
        strcpy(tmpbuff, "hello world");
        write(pipefd[1], tmpbuff, strlen(tmpbuff));
        read(pipefd[0], tmpbuff, sizeof(tmpbuff));
        
#endif
        while (1)
        {
            
        }
    }
    else if (pid > 0)
    {
        int cnt = 0;
        sleep(2);
        close(pipefd[0]);

        while (1)
        {
            printf("write前!\n");
            write(pipefd[1], tmpbuff, sizeof(tmpbuff));
            printf("write后!\n");
            cnt++;
            printf("cnt = %d\n", cnt);
        }
#if 0
        nsize = read(pipefd[0], tmpbuff, sizeof(tmpbuff));
        printf("读到 %ld 个字节,内容为: %s\n", nsize, tmpbuff);
#endif
    }

    return 0;
}



    2.有名管道 


        通过管道在文件系统中的路径找到管道名,两个进程以读写的方式打开同一管道完成通信

        mkfifo:
        int mkfifo(const char *pathname, mode_t mode);
        功能:
            创建一个管道 
        参数:
            pathname:管道文件的路径 
            mode:权限
        返回值:
            成功返回0 
            失败返回-1 

        有名管道必须读写两端同时加入后,才能继续向下执行

写:

#include "../head.h"

extern int errno;

int main(void)
{
    int ret = 0;
    int fd = 0;

    ret = mkfifo("/tmp/myfifo", 0664);
    if (-1 == ret && errno != EEXIST)
    {
        perror("fail to mkfifo");
        return -1;
    }

    fd = open("/tmp/myfifo", O_WRONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    printf("打开管道成功!\n");

    write(fd, "hello world", 11);

    close(fd);
    
    return 0;
}

读:

#include "../head.h"

extern int errno;

int main(void)
{
    int ret = 0;
    int fd = 0;
    ssize_t nsize = 0;
    char tmpbuff[4096] = {0};

    ret = mkfifo("/tmp/myfifo", 0664);
    if (-1 == ret && errno != EEXIST)
    {
        perror("fail to mkfifo");
        return -1;
    }

    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    printf("打开管道成功!\n");

    nsize = read(fd, tmpbuff, sizeof(tmpbuff));
    printf("读取到 %ld 个字节, 内容: %s\n", nsize, tmpbuff);

    close(fd);
    
    return 0;
}


2.安装mplayer软件:


    1.让Ubuntu可以上网
        ping www.baidu.com 
    
    2.配置apt-get工具集的源

    3.安装mplayer 
        sudo apt-get install mplayer 

    4.将视频拷贝到虚拟机中 
        mplayer 视频文件名  

    5.播放视频
        mplayer -slave -input file=/tmp/myfifo 视频路径

#include "../head.h"

extern int errno;

int main(void)
{
    int ret = 0;
    int fd = 0;

    ret = mkfifo("/tmp/myfifo", 0664);
    if (-1 == ret && errno != EEXIST)
    {
        perror("fail to mkfifo");
        return -1;
    }

    fd = open("/tmp/myfifo", O_RDWR);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }

    write(fd, "pause\n", 6);

    close(fd);

    return 0;
}



4.编写两个进程任务clienta.c和clientb.c, clienta.c从终端接收一行字符串,通过管道发送给clientb.c 
                                       clientb.c接收到内容后打印,并从终端接收一行字符串,发回给clienta.c 
                                       循环此过程实现两个进程聊天的功能

1.管道:
    进程间通信最简单的形式

2.信号:


    内核层和用户层通信的一种方式

    1.信号类型:


    1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
    6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
    11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
    16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
    21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
    26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
    31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
    38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
    43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
    48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
    53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
    58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
    63) SIGRTMAX-1    64) SIGRTMAX    

    可以从终端输入:
        SIGINT      ctrl+c 
        SIGQUIT     ctrl+\
        SIGTSTP     ctrl+z 
    
    9号和19号信号 


        SIGKILL 进程结束
        SIGSTOP 进程挂起

    SIGSEGV:表示内核操作内存错误
    SIGUSR1:用户可以使用该信号实现通信 
    SIGCHLD:表示该进程有一个子进程结束了 
    SIGPIPE:表示管道破裂
    SIGIO:表示IO事件发生

    2.信号的处理方式:


        1.缺省:
            按照默认的方式处理信号
        2.忽略:
            不处理信号
        3.捕捉:
            按照指定的方式处理信号

        9号SIGKILL和19号信号SIGSTOP不能被忽略和捕捉


    
    3.函数接口:


        1.signal


            typedef void (*sighandler_t)(int);//类型别名,把typedef void (*)(int)定义为sighandler_t
            sighandler_t signal(int signum, sighandler_t handler);

        这一行等价于void (*signal(int signum, void(* handler)(int))(int)

                 void(* )(int)是函数返回的类型,signal是函数的名字,signum是第一个形参,类型为int型,第二个参数handler的类型为void(*)(int)型。这里的handler是一个函数的名字,这里handler是一个函数指针,也就是一个函数的名字所以,只需要传入一个函数的名字即可。

定义一个void (*pret)(int)类型来接函数的返回值,
            功能:
                切换信号的处理方式
            参数:
                signum:信号的编号
                handler:信号对应的处理方式
                    SIG_IGN     忽略信号
                    SIG_DFL     缺省信号处理方式
                    自定义的函数的函数名(指向函数的指针)
            返回值:    
                成功返回之前注册的信号的处理方式
                失败返回SIG_ERR

#include "../head.h"

void handler(int signo)//signo是传入的信号的id,这里是系统内核自动获得的,无需手动写入
{
    printf("电话来了,休息一下!\n");

    return;
}

void handler2(int signo)
{
    printf("外卖来了, 快开门\n");

    return;
}

int main(void)
{
    void (*pret)(int) = NULL;

    pret = signal(SIGINT, handler2);
    if (SIG_ERR == pret)
    {
        perror("fail to signal");
        return -1;
    }
    printf("SIGINT信号处理方式切换为:捕捉\n");

    pret = signal(SIGQUIT, handler);
    if (SIG_ERR == pret)
    {
        perror("fail to signal");
        return -1;
    }
    printf("SIGQUIT信号处理方式切换为:捕捉\n");    

    while (1)
    {
        printf("正在工作!\n");
        sleep(1);
    }

    return 0;
}
#include "../head.h"

void handler(int signo)
{
    if (SIGINT == signo)
    {
        printf("外卖来了,快开门!\n");
    }
    else if (SIGQUIT == signo)
    {
        printf("电话来了,休息一下!\n");
    }

    return;
}

int main(void)
{
    signal(SIGINT, handler);
    signal(SIGQUIT, handler);

    while (1)
    {
        printf("正在工作\n");
        sleep(1);
    }

    return 0;
}

    4.定时器:


        alarm 
        unsigned int alarm(unsigned int seconds);
        功能:
            间隔seconds秒后给进程发送SIGALRM信号
        参数:
            seconds:间隔秒数
        返回值:
            成功返回上次定时剩余的秒数
            失败返回-1

#include "../head.h"

void ontimer_handler(int signo)
{
    printf("刷新一次界面!\n");

    alarm(5);
    return;
}

int main(void)
{
    signal(SIGALRM, ontimer_handler);
    alarm(5);

    while (1)
    {
        printf("正在工作!\n");
        sleep(1);
    }

    return 0;
}

    5.kill 


        int kill(pid_t pid, int sig);
        功能:
            向进程发送信号
        参数:
            pid:进程id
            sig:信号值
        返回值:
            成功返回0
            失败返回-1

#include "../head.h"

pid_t pid;

void handler_child(int signo)
{   
    if (SIGUSR1 == signo)
    {
        printf("好的,快去做饭!\n");
    }
    else if (SIGQUIT == signo)
    {
        printf("爸,我回来了!\n");
        kill(getppid(), SIGUSR2);
    }

    return;
}

void handler_parent(int signo)
{
    if (SIGINT == signo)
    {
        printf("儿子,我回来了!\n");
        kill(pid, SIGUSR1);
    }
    else if (SIGUSR2 == signo)
    {   
        printf("今天肯定进步了吧!\n");
    }

    return;
}

int main(void)
{
    pid = fork();
    if (-1 == pid)
    {
        perror("fail to fork");
        return -1;
    }
    if (0 == pid)
    {
        signal(SIGUSR1, handler_child);
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, handler_child);
    }
    else if (pid > 0)
    {   
        signal(SIGINT, handler_parent);   
        signal(SIGQUIT, SIG_IGN);
        signal(SIGUSR2, handler_parent);
    }

    while (1)
    {

    }

    return 0;
}

管道、信号    Unix
1.消息队列、共享内存、信号灯        SYS V

IPC对象:
内存文件 

1.ipcs 
  查看所有ipc对象的信息

2.ipcrm 
  ipcrm -Q/M/S  Key
  ipcrm -q/m/s  消息队列ID/共享内存ID/信号灯ID

3.消息队列:

1.步骤:


    1.创建IPC对象名称  ftok 

 //1.生成key值
    key = ftok("/", 'a');
    if (-1 == key)
    {
        return -1;
    }

                
    2.创建消息队列  msgget 

//2.创建消息队列
    msgid = msgget(key, IPC_CREAT | 0664);
    if (-1 == msgid)
    {
        return -1;
    }

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


    3.发送消息  msgsnd

    //3.发送消息
    sendmsg.mtype = 100;
    strcpy(sendmsg.mtext, "hello world");
    ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
    if (-1 == ret)
    {
        return -1;
    }

    printf("发送成功!\n");

    sendmsg.mtype = 200;
    strcpy(sendmsg.mtext, "how are you");
    ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
    if (-1 == ret)
    {
        return -1;
    }

    printf("发送成功!\n");


    4.接收消息   msgrcv 

 nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
    if (-1 == nsize)
    {
        return -1;
    }

    printf("接收到 %ld 字节,内容为: %s\n", nsize, recvmsg.mtext);


    5.消息队列销毁 msgctl 

msgctl(msgid, IPC_RMID, NULL);

2.函数接口:


    1.ftok 


    key_t ftok(const char *pathname, int proj_id);
    功能:
        创建一个IPC对象名称
    参数:
        pathname:文件的路径
        proj_id:项目ID(8bits)
    返回值:
        成功返回IPC对象名称
        失败返回-1

    2.msgget 


    int msgget(key_t key, int msgflg);
    功能:
        创建一个消息队列 
    参数:
        key:IPC对象名称 
        msgflg:消息队列属性
            IPC_CREAT:创建一个消息队列
            IPC_EXCL: 如果消息队列存在就报错
    返回值:
        成功返回消息队列ID
        失败返回 -1 

    3.msgsnd 


      int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
      功能:
        向消息队列中发送消息
      参数:
        msqid:消息队列的ID号
        msgp:发送消息的内容
        msgsz:发送消息的大小
        msgflg:消息属性 默认为0 
      返回值:
        成功返回0
        失败返回-1 

        struct msgbuf {
            long mtype;       /* message type, must be > 0 */
            char mtext[1];    /* message data */
        };

    4.msgrcv 


      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
      功能:
        从消息队列中接收消息
      参数:
        msqid:消息队列的ID号
        msgp:存放消息的空间首地址
        msgsz:最多接收消息的大小
        msgtyp:接收消息的类型 
        msgflg:消息属性 默认为0
      返回值:
        成功返回接收到数据的字节数 
        失败返回-1 

    5.msgctl 


      int msgctl(int msqid, int cmd, struct msqid_ds *buf);
      功能:
        向消息队列发送命令
      参数:
        msqid:消息队列的ID号
        cmd:命令 
            IPC_STAT:获取消息队列的信息 
      返回值:
        成功返回0
        失败返回-1

#include "../head.h"

typedef struct msg 
{
    long mtype;                     //发送消息的类型
    char mtext[256];                //发送的消息
}message_t;

int main(void)
{
    key_t key;
    int msgid = -1;
    int ret = 0;
    message_t sendmsg;
    message_t recvmsg;
    ssize_t nsize = 0;

    //1.生成key值
    key = ftok("/", 'a');
    if (-1 == key)
    {
        return -1;
    }

    printf("%#x\n", key);

    //2.创建消息队列
    msgid = msgget(key, IPC_CREAT | 0664);
    if (-1 == msgid)
    {
        return -1;
    }

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

    //3.发送消息
    sendmsg.mtype = 100;
    strcpy(sendmsg.mtext, "hello world");
    ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
    if (-1 == ret)
    {
        return -1;
    }

    printf("发送成功!\n");

    sendmsg.mtype = 200;
    strcpy(sendmsg.mtext, "how are you");
    ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
    if (-1 == ret)
    {
        return -1;
    }

    printf("发送成功!\n");

    nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
    if (-1 == nsize)
    {
        return -1;
    }

    printf("接收到 %ld 字节,内容为: %s\n", nsize, recvmsg.mtext);

    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

练习:
1.编写两个进程,实现利用消息队列的通信
    send.c  创建消息队列 -> 发送消息
    recv.c  创建消息队列 -> 接收消息 -> 销毁消息队列

#include "../head.h"

int main(void)
{
    key_t key;
    int msgid;
    int ret = 0;
    message_t sendmsg;

    //1.创建IPC对象 Key 值
    key = ftok("/", 'b');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建消息队列  
    msgid = msgget(key, IPC_CREAT | 0664);
    if (-1 == msgid)
    {
        perror("fail to shmget");
        return -1;
    }

    sendmsg.mtype = 100;
    fgets(sendmsg.mtext, sizeof(sendmsg.mtext), stdin);
    sendmsg.mtext[strlen(sendmsg.mtext)-1] = '\0';

    //3.发送消息
    ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg.mtext), 0);
    if (-1 == ret)
    {
        perror("fail to msgsnd");
        return -1;
    }

    return 0;
}
#include "../head.h"

int main(void)
{
    key_t key;
    int msgid;
    ssize_t nsize = 0;
    message_t recvmsg;

    //1.创建IPC对象 Key 值
    key = ftok("/", 'b');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建消息队列 
    msgid = msgget(key, IPC_CREAT | 0664);
    if (-1 == msgid)
    {
        perror("fail to shmget");
        return -1;
    }

    //3.接收消息 
    nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
    if (-1 == nsize)
    {
        perror("fail to msgrcv");
        return -1;
    }

    //4.打印消息
    printf("RECV:%s\n", recvmsg.mtext);

    //5.删除消息队列
    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

4.共享内存:


    进程间通信最高效的方法


    1.操作步骤:


       1.创建 key 值

 //1.创建 key 值 
    key_t key;
    int shmid = 0;
    int ret = 0;
    char *pshmaddr = NULL;

    key = ftok("/", 'c');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }


       2.创建共享内存 

  //2.创建共享内存 
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (-1 == shmid)
    {
        perror("fail to shmget");
        return -1;
    }

      3.映射共享内存

 //3.映射到共享内存中
    pshmaddr = shmat(shmid, NULL, 0);
    if (NULL == pshmaddr)
    {
        perror("fail to shmat");
        return -1;
    }

    fgets(pshmaddr, 4096, stdin);
    pshmaddr[strlen(pshmaddr)-1] = '\0';

    printf("共享空间: %s\n", pshmaddr);


       4.解除映射

 //4.解除映射
    ret = shmdt(pshmaddr);
    if (-1 == ret)
    {
        perror("fail to shmdt");
        return -1;
    }


       5.销毁共享内存 

//5.销毁共享空间
    shmctl(shmid, IPC_RMID, NULL);

   2.函数接口:


        1.ftok 


        2.shmget 


          int shmget(key_t key, size_t size, int shmflg);
          功能:
            创建一个共享内存
          参数:
            key:IPC对象名称
            size:共享内存的大小
            shmflg:
                IPC_CREAT  创建 
                IPC_EXCL   如果存在就报错 
          返回值:
            成功返回共享内存ID号
            失败返回-1 

        3.shmat


          void *shmat(int shmid, const void *shmaddr, int shmflg);
          功能:
            将地址映射到共享内存中
          参数:
            shmid:共享内存ID号
            shmaddr:
                NULL: 让系统选择一个合适的地址映射到共享内存中
            shmflg:
                属性,默认为0 
          返回值:
            成功返回映射到共享空间的地址
            失败返回NULL

        4.shmdt


          int shmdt(const void *shmaddr);
          功能:
            解除映射空间
          参数:
            shmaddr:映射到共享内存中的地址
          返回值:
            成功返回0 
            失败返回-1 

        5.shmctl 


          int shmctl(int shmid, int cmd, struct shmid_ds *buf);
          功能:
            向共享内存发送命令
          参数:
            shmid:共享内存ID号
            cmd:命令
                IPC_RMID 删除
          返回值:
            成功返回0 
            失败返回-1 

练习:
1.编写两个进程,实现利用共享内存的通信
    write.c  创建共享内存 -> 映射 -> 从终端接收数据写入共享内存中
    read.c  创建共享内存 -> 映射 -> 从共享内存中读取数据 -> 显示到终端

#include "../head.h"

int main(void)
{
    int ret = 0;
    int shmid = 0;
    char *pshmaddr = NULL;

    //1.创建 key 值
    key_t key;

    key = ftok("/", 'a');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建共享内存
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (-1 == shmid)
    {
        perror("fail to shmget");
        return -1;
    }

    //3.映射共享内存空间
    pshmaddr = shmat(shmid, NULL, 0);
    if (NULL == pshmaddr)
    {
        perror("fail to shmat");
        return -1;
    }

    //4.从终端接收写入到共享内存中
    while (1)
    {
        gets(pshmaddr);
        if (!strcmp(pshmaddr, ".quit"))
        {
            break;
        }
    }

    //5.解除映射
    ret = shmdt(pshmaddr);
    if (-1 == ret)
    {
        perror("fail to shmdt");
        return -1;
    }

    return 0;
}

#include "../head.h"

int main(void)
{
    int ret = 0;
    int shmid = 0;
    char *pshmaddr = NULL;

    //1.创建 key 值
    key_t key;

    key = ftok("/", 'a');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建共享内存
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (-1 == shmid)
    {
        perror("fail to shmget");
        return -1;
    }

    //3.映射共享内存空间
    pshmaddr = shmat(shmid, NULL, 0);
    if (NULL == pshmaddr)
    {
        perror("fail to shmat");
        return -1;
    }

    while (1)
    {
        printf("内容:%s\n", pshmaddr);
        if (!strcmp(pshmaddr, ".quit"))
        {
            break;
        }
    }

    //4.解除映射
    ret = shmdt(pshmaddr);
    if (-1 == ret)
    {
        perror("fail to shmdt");
        return -1;
    }

    //5.删除共享内存 
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

5.信号灯:


    有名信号量数组


    1.创建信号灯 

 //2.创建信号量数组 
    semid = semget(key, 2, IPC_CREAT | 0664);
    if (-1 == semid)
    {
        perror("fail to semget");
        return -1;
    }


        int semget(key_t key, int nsems, int semflg);   
        功能:
            创建信号量数组 
        参数: 
            key:IPC对象名称
            nsems:信号量个数
            semflg:信号量属性
                IPC_CREAT:创建一个信号量数组
        返回值:    
            成功返回0 
            失败返回-1 

    2.向信号灯发送命令 

//将下标为0的读信号量设置为0 
    myun.val = 0;
    ret = semctl(semid, 0, SETVAL, myun);
    if (-1 == ret)
    {
        perror("fail to semctl");
        return -1;
    }

    //将下标为1的写信号量设置为1 
    myun.val = 1;
    ret = semctl(semid, 1, SETVAL, myun);
    if (-1 == ret)
    {
        perror("fail to semctl");
        return -1;
    }


        int semctl(int semid, int semnum, int cmd, ...);
        功能:
            向信号灯发送命令
        参数:
            IPC_RMID    删除信号灯
            SETVAL      设置第semnum-th信号量的值为arg.val 
        返回值:
            成功返回0 
            失败返回-1 

    3.对信号量完成申请和释放操作

//申请写信号量
    mybuf.sem_num = 1;
    mybuf.sem_op = -1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        perror("fail to semop");
        return -1;
    }

    printf("拿到写信号量了!\n");

    //释放读信号量
    mybuf.sem_num = 0;
    mybuf.sem_op = +1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        perror("fail to semop");
        return -1;
    }

    printf("释放读信号量了!\n");

    //申请读信号量
    mybuf.sem_num = 0;
    mybuf.sem_op = -1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        perror("fail to semop");
        return -1;
    }

    printf("申请读信号量了!\n");

    //申请读信号量
    mybuf.sem_num = 0;
    mybuf.sem_op = -1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        perror("fail to semop");
        return -1;
    }

    printf("申请读信号量了!\n");


        int semop(int semid, struct sembuf *sops, size_t nsops);
        功能:
            对信号量完成申请和释放操作
        参数:
            semid:信号灯ID号
            sops:信号灯操作数组
                unsigned short sem_num;  //操作信号量的下标
                short          sem_op;   //对信号量的操作 +1(释放信号量) -1(申请信号量)
                short          sem_flg;  //SEM_UNDO 操作结束后,信号量的值会恢复到原来的值
            nsops:数组元素个数  
        返回值:
            成功返回0 
            失败返回-1

6.信号灯和共享内存的使用

步骤:

1.创建key

2.创建共享内存

3.创建信号量组

4.信号量组初始化

5.映射共享内存

6.申请写

7.写操作

8.释放读

9.申请读

10.释放写

11.解除映射

12.删除共享内存

信号量灯:

#include "../head.h"

/* 对信号量初始化 */
int init_sem(int semid, int *parray, int len)
{
    int i = 0;
    union semun myun;
    int ret = 0;

    for (i = 0; i < len; i++)
    {   
        myun.val = parray[i];
        ret = semctl(semid, i, SETVAL, myun);
        if (-1 == ret)
        {
            return -1;
        }
    }

    return 0;
}

/* 信号量申请操作 */
int sem_p(int semid, int semnum)
{
    int ret = 0;
    struct sembuf mybuf;

    mybuf.sem_num = semnum;
    mybuf.sem_op = -1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/* 信号量释放操作 */
int sem_v(int semid, int semnum)
{
    int ret = 0;
    struct sembuf mybuf;

    mybuf.sem_num = semnum;
    mybuf.sem_op = +1;
    mybuf.sem_flg = SEM_UNDO;
    ret = semop(semid, &mybuf, 1);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

读:

#include "../head.h"

int main(void)
{
    //1.创建 key 值
    key_t key;
    int semid = 0;
    int shmid = 0;
    int val[2] = {0, 1};
    char *pshmaddr = NULL;

    key = ftok("/", 'a');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建共享内存
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (-1 == shmid)
    {
        perror("fail to shmget");
        return -1;
    }

    //3.创建信号量数组
    semid = semget(key, 2, IPC_CREAT | 0664);
    if (-1 == semid)
    {
        perror("fail to semget");
        return -1;
    }

    //4.对信号量数组初始化
    init_sem(semid, val, 2);

    //5.映射共享内存
    pshmaddr = shmat(shmid, NULL, 0);
    if (NULL == pshmaddr)
    {
        perror("fail to shmat");
        return -1;
    }

    while (1)
    {
        //6.申请读资源
        sem_p(semid, 0);

        //7.读操作
        printf("内容:%s\n", pshmaddr);
        if (!strcmp(pshmaddr, ".quit"))
        {
            break;
        }

        //8.释放写资源
        sem_v(semid, 1);
    }

    shmdt(pshmaddr);
    
    shmctl(shmid, IPC_RMID, NULL);

    semctl(semid, 0, IPC_RMID);

    return 0;
}

写:

#include "../head.h"

int main(void)
{
    //1.创建 key 值
    key_t key;
    int semid = 0;
    int shmid = 0;
    int val[2] = {0, 1};
    char *pshmaddr = NULL;

    key = ftok("/", 'a');
    if (-1 == key)
    {
        perror("fail to ftok");
        return -1;
    }

    //2.创建共享内存
    shmid = shmget(key, 4096, IPC_CREAT | 0664);
    if (-1 == shmid)
    {
        perror("fail to shmget");
        return -1;
    }

    //3.创建信号量数组
    semid = semget(key, 2, IPC_CREAT | 0664);
    if (-1 == semid)
    {
        perror("fail to semget");
        return -1;
    }

    //4.对信号量数组初始化
    init_sem(semid, val, 2);

    //5.映射共享内存
    pshmaddr = shmat(shmid, NULL, 0);
    if (NULL == pshmaddr)
    {
        perror("fail to shmat");
        return -1;
    }

    while (1)
    {
        //6.申请写资源
        sem_p(semid, 1);

        //7.写操作
        gets(pshmaddr);

        //8.释放读资源
        sem_v(semid, 0);

        if (!strcmp(pshmaddr, ".quit"))
        {
            break;
        }
    }

    shmdt(pshmaddr);

    return 0;
}
#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>

typedef struct msg
{
    long mtype;
    char mtext[256];
}message_t;

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

extern int sem_v(int semid, int semnum);
extern int sem_p(int semid, int semnum);
extern int init_sem(int semid, int *parray, int len);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值