进程间通信

作者: 吃苹果的猫C | 来源:发表于2016-08-04 11:39 被阅读82次

    进程间通信

    信号

    kill -l 显示常用信号列表

    SIGKILL SIGSTOP 两个信号不能被忽略

    SYNOPSIS

           #include <signal.h>
    
           typedef void (*sighandler_t)(int);
    
           sighandler_t signal(int signum, sighandler_t handler);
           
    

    @signal:信号number

    @handler: 传进去一个自定义处理函数,int参数是信号编号


    • SIG_IGN:忽略
    • SIG_DFL:默认处理,具体查看signal(7)

    This signal() facility is a simplified interface to the more general sigaction(2) facility.

    如果需要传进参数 可以使用sigaction(2)(如果需要使用更多特性)


    RETURN 返回上一次的函数指针,如果失败则SIG_ERR会被返回,errno会被设定


    Example

    子进程退出,父进程 接受到信号,回收子进程。

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    
    
    // 信号处理函数
    void signal_fun(int a)
    {
        int status;
        printf("收到信号\n");
        //回收子进程,WNOHANG无阻塞 
        waitpid(-1,&status,WNOHANG);
        if (WIFEXITED(status))
        {
            printf("子进程正常退出\n");
        }
    }
    
    int main(int argc, char const *argv[])
    {
        // 子进程退出时候会向父进程发送SIGCHLD信号
        if(signal(SIGCHLD,signal_fun)==SIG_ERR)
        {
            perror("signal failure");
            exit(EXIT_FAILURE);
        }
        int pid=fork();
        if (pid<0)
        {
            perror("fork failure"); exit(EXIT_FAILURE);
        }
    
        // 子进程
        if (pid==0)
        {
            printf("this is son\n");
            exit(EXIT_SUCCESS);
        }
    
        // 父进程
        while(1)
        {
            sleep(1);
        }
    
        return 0;
    }
    
    

    简单处理方法:显示声明忽略SINGHLD信号

    signal(SIGCHLD,SIG_IGN)


    IPC对象

    消息队列

    一 IPC 对象 ---- 消息队列

    IPC 对象命令

    查看系统中IPC对象

    • ipcs -a 显示所有的IPC对象
    • ipcs -s/-q/-m

    删除系统中的IPC对象

    • ipcrm -q/-s/-m ID

    第一步:获得key值

    key_t ftok(const char *pathname, int proj_id);  
    

    参数:

    @pathname 已经存在的文件路径
    @proj_id 获取这个整数的低8bit

    返回值:

    成功返回 key值,失败返回-1


    第二步:创建IPC对象

    int msgget(key_t key, int msgflg);  
    

    参数:

    @key IPC_PRIVATE(用户亲缘关系进程间通信) 或 ftok函数产生key值
    @msgflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666 (判断IPC对象是否存在)

    返回值:

    成功返回ID,失败返回-1

    注意:

    如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID


    第三步.发送消息

    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);  
    

    参数

    @msqid 消息队列ID
    @msgp 需要发送的消息存放的地址
    @msgsz 消息正文的大小
    @msgflg 0:阻塞的方式发送 IPC_NOWAIT:非阻塞方式调用

    返回值:

    成功返回0,失败返回-1

    结构体体

    typedef struct{
        long  msg_type;   //消息类型必须在第一个位置,
        char  mtxt[1024];
        ...
    }msg_t;
    

    第四步.接收消息

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);  
    

    参数:

    @msqid 消息队列ID
    @msgp 存放接收到的消息
    @msgsz 正文大小
    @msgtyp 消息类型 , 0: 总是从消息队列中提取第一个消息
    @msgflg 0:阻塞的方式接收 IPC_NOWAIT:非阻塞方式调用

    返回值:

    成功返回 接收消息正文大小,失败返回-1


    第五步.删除消息队列

    int msgctl(int msqid, int cmd, struct msqid_ds *buf); 
    

    参数:

    @msgqid 消息队列
    @cmd IPC_RMID(删除消息队列) IPC_SET(设置消息队列的属性信息) IPC_STAT(获取消息队列属性信息)
    @buf 存放消息队列属性

    返回值:

    成功返回0,失败返回-1

    Example

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    
    
    static void error_judge(int ret,char* mes);
    typedef struct msgtype
    {
        long msg_type;
        char msg_data[512];
    }MSGTYPE;
    
    int main(int argc, char const *argv[])
    {
        MSGTYPE sen_mes={100,"hello zhaohe!"};
        MSGTYPE rec_mes;
    
        key_t key=ftok("./msg_ipc_client.c",'k');
        error_judge(key,"ftok failure");
    
        int msqid=msgget(key,IPC_CREAT|0666);
        error_judge(msqid,"msgget failure");
    
    
        printf("发送的信息是%s 编号是:%ld \n",sen_mes.msg_data,sen_mes.msg_type);
        
        int ret=msgsnd(msqid,&sen_mes,sizeof(MSGTYPE)-sizeof(long),0);
        error_judge(ret,"msgsnd failure");
    
        ret=msgrcv(msqid,&rec_mes,sizeof(MSGTYPE)-sizeof(long),100,0);
        error_judge(ret,"msgsnd failure");
    
        printf("收到的信息是%s 编号是:%ld\n",rec_mes.msg_data,rec_mes.msg_type);
    
        int msgctl(msqid,IPCRMID,NULL); 
        return 0;
    }
    
    
    static void error_judge(int ret,char* mes)
    {
        if (ret<0)
        {
            perror(mes);
            exit(EXIT_FAILURE);
        }
    }
    

    共享内存 :内核空间预留出来的一块内存,用于进程间通信

    第一步

    int shmget(key_t key, size_t size, int shmflg);  
    

    功能:获取共享内存段的ID

    参数:

    @key IPC_PRIVATE 或 ftok()
    @size 申请的共享内存段大小 [4k的倍数]
    @shmflg IPC_CREAT | 0666IPC_CREAT | IPC_EXCL
    有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST

    返回值:

    成功返回ID,失败返回-1

    第二步

    void *shmat(int shmid, const void *shmaddr, int shmflg);  
    

    功能:映射共享内存到用户空间
    参数:

    @shmid 共享内存段ID
    @shmaddr NULL:系统自动完成映射
    @shmflg SHM_RDONLY:只读 0:读写

    返回值:

    成功返回映射后的地址,失败返回(void *)-1

    其他

    int shmdt(const void *shmaddr);  
    

    功能:撤销映射
    参数:

    @shmaddr 共享内存映射的地址

    注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射

    int shmctl(int shmid, int cmd, struct shmid_ds *buf);  
    

    功能:根据命令控制共享内存
    参数:

    @shmid 共享内存段的ID
    @cmd

    IPC_STAT 获取属性
    IPC_SET 设置属性
    IPC_RMID删除IPC对象

    @buf 保存属性

    返回值:

    成功返回0,失败返回 -1


    注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。只有当共享内存映射 次数为0,才会删除共享内存对象


    Example

    描述

    两个进程一端发送信息,另一端接受信息,通过共享空间实现信息交互,使用两个char的变量模拟信号量进行同步。对模拟信号量的初始化,这里约定有空间空间的创建者进行初始化

    写端代码

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <errno.h>
    #include <sys/shm.h>
    #include <string.h>
    
    #define SHM_SIZE (4096*1)
    #define BUFF_SIZE (SHM_SIZE-2*sizeof(char))
    
    static void error_judge(int ret,char* mes);
    typedef struct msgtype
    {
        char w_flag;
        char r_flag;
        char buff[SHM_SIZE-2*sizeof(char)];
    }MSGTYPE;
    
    int main(int argc, char const *argv[])
    {
        //构建一个地址,之后接受共享地址
        MSGTYPE *sen_mes;
        int flags=0;
    
        // 1.首先创建一个key
        key_t key=ftok("./msg_ipc_client.c",'a');
        error_judge(key,"ftok failure");
    
        // 2.向内核申请共享空间
        int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
        if (shmid<0&&errno!=EEXIST)
        {
            perror("shmget failure");
            exit(EXIT_FAILURE);
        }
        if (shmid<0&&errno==EEXIST)
        {
             shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
             flags=1;
        }
    
        // 3.获得共享空间的在用户空间的地址映射
        sen_mes=shmat(shmid,NULL,0);
    
        // 初始化信号量  这里是模拟信号量 用来同步两个进程之间的读写顺序 占用cpu资源不推荐
        if (flags==0)
        {
            sen_mes->w_flag=1;
            sen_mes->r_flag=0;
        }
    
        printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);
        // sen_mes=shmat(shmid,NULL,0);
    
    
        // 4.之后就可以像操作地址空间一样操作共享内存
        while(1)
        {
            if (sen_mes->w_flag==1)
            {
                fgets(sen_mes->buff,BUFF_SIZE,stdin);
                sen_mes->w_flag=0;
                sen_mes->r_flag=1;
                if (strcmp(sen_mes->buff,"quit\n")==0)
                {
                    break;
                }
            }
        }
    
        printf("程序结束\n");
    
        // 5。释放映射
        shmdt(sen_mes);
    
        // 6.由创造者删除共享内存
        if (flags==0)
        {
            shmctl(shmid,IPC_RMID,NULL);
        }
    
        return 0;
    }
    
    
    static void error_judge(int ret,char* mes)
    {
        if (ret<0)
        {
            perror(mes);
            exit(EXIT_FAILURE);
        }
    }
    

    读端代码

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <errno.h>
    #include <sys/shm.h>
    #include <string.h>
    
    #define SHM_SIZE (4096*1)
    #define BUFF_SIZE (SHM_SIZE-2*sizeof(char))
    static void error_judge(int ret,char* mes);
    typedef struct msgtype
    {
        char w_flag;
        char r_flag;
        char buff[SHM_SIZE-2*sizeof(char)];
    }MSGTYPE;
    
    int main(int argc, char const *argv[])
    {
        MSGTYPE *sen_mes;
        int flags=0;
    
        key_t key=ftok("./msg_ipc_client.c",'a');
        error_judge(key,"ftok failure");
    
        int shmid=shmget(key,SHM_SIZE,IPC_CREAT|IPC_EXCL|0666);
        if (shmid<0&&errno!=EEXIST)
        {
            perror("shmget failure");
            exit(EXIT_FAILURE);
        }
        if (shmid<0&&errno==EEXIST)
        {
             shmid=shmget(key,SHM_SIZE,IPC_CREAT|0666);
             flags=1;
        }
        sen_mes=shmat(shmid,NULL,0);
    
        if (shmid==0)
        {
            sen_mes->w_flag=1;
            sen_mes->r_flag=0;
        }
    
        printf("shmid is %d w_flag is %d r_flag is %d\n",shmid,sen_mes->w_flag,sen_mes->r_flag);
    
    
        while(1)
        {
            if (sen_mes->r_flag==1)
            {
                printf("I have read:%s",sen_mes->buff);
                if (strcmp(sen_mes->buff,"quit\n")==0)
                {
                    break;
                }
                sen_mes->w_flag=1;
                sen_mes->r_flag=0;
            }
        }
    
        printf("程序结束\n");
        shmdt(sen_mes);
        shmctl(shmid,IPC_RMID,NULL);
        return 0;
    }
    
    
    static void error_judge(int ret,char* mes)
    {
        if (ret<0)
        {
            perror(mes);
            exit(EXIT_FAILURE);
        }
    }
    

    信号灯

    头文件

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

    (1)创建信号灯集

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

    参数:

    @key IPC_PRIVATE , ftok()
    @nsems 信号灯集中信号灯的个数
    @semflg 常用IPC_CREAT | 0666, IPC_CREAT | IPC_EXCL| 0666
    有则打开,没有则创建 返回shemid |第二标示表示 没有创建,但当文件存在的时候返回 -1并将errno设置为EEXIST

    返回值:

    成功返回ID,失败返回-1

    (2)初始化信号灯集中信号灯的值

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

    参数:

    @semid 信号灯集的ID
    @semnum 信号灯的编号[编号从0开始]
    @cmd SETVAL[设置信号灯的值] ,GETVAL(获取信号灯的值),IPC_RMID[删除信号灯集]

    返回值:

    成功返回0,失败返回-1

    (3)PV操作

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

    功能:完成PV操作

    参数:

    @semid 信号灯集的ID
    @sops 操作方式结构体首地址
    @nsops 操作信号灯的个数

    返回值:

    成功返回0,失败返回-1

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

    sem_num :

    信号集信号编号

    sem_op :

    <1>0 等待信号灯的值变成0
    <2>1 释放资源,V操作
    <3>-1 申请资源,P操作


    sem_flg:

    0 : 阻塞方式
    IPC_NOWAIT : 非阻塞方式调用
    SEM_UNDO : 进程结束的时候,它申请的资源自动释放


    Example

    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    
    static void error_judge(int ret,char* mes);
    void p(int sem_id,int sem_num);
    void v(int sem_id,int sem_num);
    
    void release(int sem_id);
    
    void prf_sem(int sem_id,int sem_num);
    
    int main(int argc, char const *argv[])
    {
    
        key_t key=ftok("./msg_ipc_client.c",'a');
        error_judge(key,"ftok failure");
    
        // int semget(key_t key, int nsems, int semflg);
        //根据key数值创建一个包含两个信号的信号集
        int sem_id=semget(key,2, IPC_CREAT | 0666);
        error_judge(sem_id,"semget failure");
    
        // int semctl(int sem_id, int semnum, int cmd, ...);
        // 将第一个信号的数字初始化为1
        int ret=semctl(sem_id, 0, SETVAL,1);
        error_judge(ret,"semctl failure");
        prf_sem(sem_id,0);
    
        // 将第一个信号的数字初始化为2
        ret=semctl(sem_id, 1, SETVAL,2);
        error_judge(ret,"semctl failure");
        prf_sem(sem_id,1);
    
        p(sem_id,0);
        prf_sem(sem_id,0);
    
        v(sem_id,0);
        prf_sem(sem_id,0);
    
    
        p(sem_id,1);
        prf_sem(sem_id,1);
    
        p(sem_id,1);
        prf_sem(sem_id,1);
    
    
    
        // p(sem_id,0);
    
    
    
    
    
        printf("this is 信号集实验\n");
        release(sem_id);
        return 0;
    }
    
    
    void p(int sem_id,int sem_num)
    {
        struct sembuf sem;
    
        sem.sem_num = sem_num;
        sem.sem_op  = -1;
        sem.sem_flg = 0;
    
        if(semop(sem_id,&sem,1) < 0)
        {
            release(sem_id);
            error_judge(-1,"semop:fun_p");
        }
    }
    
    void v(int sem_id,int sem_num)
    {
        struct sembuf sem;
    
        sem.sem_num = sem_num;
        sem.sem_op  = 1;
        sem.sem_flg = 0;
    
        if(semop(sem_id,&sem,1) < 0)
        {
            release(sem_id);
            error_judge(-1,"semop:fun_v");
        }
    }
    
    
    static void error_judge(int ret,char* mes)
    {
        if (ret<0)
        {
            perror(mes);
            exit(EXIT_FAILURE);
        }
    }
    
    
    void prf_sem(int sem_id,int sem_num)
    {
        unsigned short sem_value=0;
        sem_value=semctl(sem_id, sem_num, GETVAL,&sem_value);
        printf("sem_id:%d num:%d value:%d\n",sem_id,sem_num,sem_value);
    }
    
    void release(int sem_id)
    {
        semctl(sem_id, 0,IPC_RMID);
    }
    

    相关文章

      网友评论

        本文标题:进程间通信

        本文链接:https://www.haomeiwen.com/subject/bkpcsttx.html