(单机)进程间通信(IPC)笔记

目录

一、无名管道:

二、有名管道:

三、消息队列

四、共享内存

五、信号:

六、信号量


进程间通信的方式有六种:

1、无名 管道(单向通信)

2、有名管道(单向通信)

3、消息队列(双向通信)

4.、共享内存(双向通信)

5、信号(单向通信)

6、信号量(可配合共享内存来使用、合理分配共享资源)

一、无名管道:

        创建无名管道:int pipe(int pipefd[2]);

        pipefd[2]为一个数组用以控制无名管道的读端和写端;pipefd[0]为读端;pipefd[1] 为写端       

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

demo1.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

int main()
{
        int status;
        int fd[2];
        pid_t pid;
        char *buf = "hello from farther!";
        char *readBuf = (char *)malloc(strlen(buf));

        if(pipe(fd) == -1){
                printf("pipe fail!\n");
        }

        pid = fork();
        if(pid < 0){
                printf("fork fail!\n");
        }else if(pid > 0){
                printf("this is farther\n");
                close(fd[0]);
                if(write(fd[1],buf,strlen(buf)) == -1){
                        perror("write fail:");
                }
                close(fd[1]);
                wait(&status);
                if(WEXITSTATUS(status) == -1){
                        printf("child pro qiut becauseof error!\n");
                }
        }else{
                printf("this is child\n");
                close(fd[1]);
                if(read(fd[0],readBuf,strlen(buf)) == -1){
                        perror("read fail:");
                        exit(-1);
                }
                printf("%s\n",readBuf);
                close(fd[0]);
                exit(1);
        }
        return 0;
}
~                                                                                                                                                               
-- INSERT --                                                                                                                                  11,14-21      All

二、有名管道:

        创建有名管道:int mkfifo(const char *pathname, mode_t mode);使用mkfifo来创建一个特殊的文件用以进程间通信

        pathname为所创建的文件名,mode为文件的权限 0600为可读可写

        创建后,使用open函数打开文件,进程一write写入信息内容,进程二read读取信息,最后       

        将文件close;

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

demo2.c

        进程一写入信息

        

include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

int main()
{
        int n_write;
        char *buf = "caiqingfeng";

        if(mkfifo("./fifo",0600) == -1 && errno != EEXIST){
                printf("mkfifo fail!\n");
                perror("why");
        }
        int fd = open("./fifo",O_WRONLY);
        if(fd == -1){
                printf("open fail!\n");
        }
        while(1){
                n_write = write(fd,buf,strlen(buf));
                if(n_write == -1){
                        printf("write fail!\n");
                }
                sleep(1);
        }
        return 0;
}
~                                                                                                                                                               
~       

demo3.c

        进程二读取信息

        

#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>


int main()
{
        char *readBuf = (char *)malloc(50);
        int n_read;

        int fd = open("./fifo",O_RDONLY);
        if(fd == -1){
                printf("open fail!\n");
        }
        while(1){
                n_read = read(fd,readBuf,50);
                if(n_read == -1){
                        printf("read fail!\n");
                }
                printf("%s\n",readBuf);
        }
        return 0;
}
~                                                                                                                                                               
~                                                                                                                                                               
~                                                                                                                                                               
~                                                                                                                                                               
~                                                                                                                                                               
~                                   

三、消息队列

1、创建消息队列:int msgget(key_t key, int msgflg);

        key为获取和创建消息队列的编码,读端和写端必须key一致才能保证双方是同一个消息队列

        key可用ftok()来生成键值

        msgflg通常为IPC_CREAT|0777

        返回值:成功返回消息队列的ID,错误返回-1

 2.、消息队列的写入:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

        msqid为消息队列的ID,msgp为你所写的内容 常常是一个结构体因为除了写入的内容外 我们还需要传递信息的类型,msgsz为写入的大小,msgflg正常情况下为0,以默认模式写入信息

  3、消息队列的读取: ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
        msqid消息队列ID;msgp常常也是为结构体;msgtyp信息的类型;msgflg正常情况下为0

4、消息队列的管理:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
        msqid消息队列的ID;cmd若要删除消息队列则为宏(IPC_RMID);buf在删除消息队列的情况下为NULL 以默认情况下删除消息队列

写端demo

        

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//int msgget(key_t key, int msgflg);
struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[128];    /* message data */
};

int main()
{
        key_t key;
        int msgId;
        struct msgbuf sendBuf = {808,"caiqingfeng"};
        key = ftok(".",2);
        if(key == -1){
                printf("ftok fail!\n");
        }
        msgId = msgget(key,IPC_CREAT|0777);
        if(msgId == -1){
                printf("msgget fail!\n");
        }
        if(msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0) == -1){
                printf("msgsend error!\n");
        }
        printf("send massge over!\n");
        return 0;
}

读端demo

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//int msgget(key_t key, int msgflg);
struct mymsg
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
        int msgId;
        struct mymsg rcvBuf;
        key_t key;
        key = ftok(".",2);
        if(key == -1){
                printf("ftok fail!\n");
        }
        msgId = msgget(key,IPC_CREAT|0777);
        if(msgId == -1){
                printf("msgget fail!\n");
        }
        if(msgrcv(msgId,&rcvBuf,sizeof(rcvBuf),808,0) == -1){
                printf("msgsend error!\n");
        }
        printf("%s\n",rcvBuf.mtext);
        if(msgctl(msgId,IPC_RMID,NULL) == -1){
                printf("msgctl fail!\n");
        }
        return 0;
}
~                                                  

四、共享内存

说明:

1、查看共享内存,使用命令:ipcs -m

2、删除共享内存,使用命令:ipcrm -m [shmid]

1、创建共享内存:
                      int shmget(key_t key, size_t size, int shmflg);

                       key与消息队列的key相同;size为所创建的共享内存的大小(只能以兆为单位);           shmflg为标识符,IPC_CREAT|0666 即以可读可写的方式创建共享内存

                        返回值:成功返回shmID,错误返回-1

2、挂载共享内存:

                       void *shmat(int shmid, const void *shmaddr, int shmflg);
                        shmid为shmID;shmaddr和shmflg正常情况下设为0;

                         返回值:成功返回一个共享内存的地址,进程可直接对该地址进行读写、打印等操作;错误返回-1

注:写入时可直接用strcpy();将字符串写入共享内存

3、解除挂载:

        int shmdt(const void *shmaddr);

        shmaddr为共享内存的地址 即函数shmat的返回值

 4、删除共享内存:

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

        shmid为shmID,cmd为IPC_RMID,buf为0

   写端demo:

        

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

int main()
{
        int shmId;
        char *shmaddr;

        key_t key;
        key = ftok(".",1);
        if(key == -1){
                printf("ftok fail!\n");
        }

        shmId = shmget(key,1024*5,IPC_CREAT|0666);
        if(shmId == -1){
                printf("shmget fail!\n");
        }

        shmaddr = shmat(shmId,0,0);
        if(*shmaddr == -1){
                printf("shmat fail!\n");
        }

        strcpy(shmaddr,"caiqingfeng");

        if(shmdt(shmaddr) == -1){
                printf("shmdt fail!\n");
        }
        return 0;
}
~                                                                                                                                                               
~                                                                                                                                                               
~                     

读端demo:

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

int main()
{
        int shmId;
        char *shmaddr;

        key_t key;
        key = ftok(".",1);
        if(key == -1){
                printf("ftok fail!\n");
        }

        shmId = shmget(key,1024*5,0);
        if(shmId == -1){
                printf("shmget fail!\n");
        }

        shmaddr = shmat(shmId,0,0);
        if(*shmaddr == -1){
                printf("shmat fail!\n");
        }

        printf("shm massage:%s\n",shmaddr);

        if(shmdt(shmaddr) == -1){
                printf("shmdt fail!\n");
        }

        if(shmctl(shmId,IPC_RMID,0) == -1){
                printf("shmctl fail!\n");
        }
        return 0;
}

五、信号:

                入门版(无法携带消息):

                  1、绑定信号: sighandler_t signal(int signum, sighandler_t handler);       

                   signum为你所要接受的信号编号或宏(指令kill-l查看);handler为你接受到函数后所做出的操作函数

                   2、发送信号:int kill(pid_t pid, int sig);

                pid为所发送的进程的进程号,sig为发送的信号编号或宏

发送端demo:

        

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
        int pid;
        int signum;

        if(argc != 2){
                printf("argv error!\n");
        }

        signum = atoi(argv[1]);
        pid = atoi(argv[2]);

        if(kill(pid,signum) == -1){
                printf("kill fail!\n");
        }

        return 0;

接受端demo:

       

#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
        printf("signum = %d\n",signum);
        printf("I never be killed!\n");
}

int main()
{
        signal(2,handler);
        while(1);
        return 0;
}
~                                                                                                                                                               
~                                                                                                                                                               
~                                                                                                                                                               
~                      

高级版(可携带消息):

        1、绑定信号:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

        signum为所接受的信号编号和宏,oldact正常情况为NULL,先定义一个struct sigaction的结构体该结构体内容如下

  

 struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

若要发送消息,则需要把sa_flags初始化为SA_SIGINFO;且第二个操作函数初始化为你的操作函数;

第二个操作函数的形参int为信号编号,void *无消息传递时为NULL,反之为非空,siginfo_t *为结构体内容如下

  siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               pid_t    si_pid;      /* Sending process ID */
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */
               int      si_int;      /* POSIX.1b signal */
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               long     si_band;     /* Band event (was int in
                                        glibc 2.3.2 and earlier) */
               int      si_fd;       /* File descriptor */
               short    si_addr_lsb; /* Least significant bit of address
                                        (since kernel 2.6.32) */
           }

void    *si_ptr;和 int      si_int伴随发送信号指令所携带的消息就储存在这两个变量中,打印出来即可

2、发送信号:

           int sigqueue(pid_t pid, int sig, const union sigval value);

             pid发送进程的pid;sig为发送的信号;const union sigval value为联合体内容如下

 union sigval {
               int   sival_int;
               void *sival_ptr;
           };

可以使信号携带整形和字符串类型的消息

发送端demo:

#include <stdio.h>
#include <signal.h>

int main(int argc,char **argv)
{
        int pid;
        int signum;

        pid = atoi(argv[2]);
        signum = atoi(argv[1]);

        if(argc != 2){
                printf("argc fail!\n");
        }
//      int sigqueue(pid_t pid, int sig, const union sigval value);
        union sigval value;
        value.sival_int = 404;
        value.sival_ptr = "access denied!";
        if(sigqueue(pid,signum,value) == -1){
                printf("sigqueue fail!\n");
        }
        printf("my pid = %d\n",getpid());
        return 0;
}
~                                      

接收端demo:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

//void       (*sa_sigaction)(int, siginfo_t *, void *);
void handler(int signum,siginfo_t *info,void *context)
{
        printf("signum = %d\n",signum);
        if(context != NULL){
                printf("si_int = %d\n",info->si_int);
                printf("si_ptr = %p\n",info->si_ptr);
                printf("si_pid = %d\n",info->si_pid);
        }else{
                printf("no massage\n");
        }
}

int main()
{

        printf("my pid = %d\n",getpid());
        //int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
        struct sigaction *act;
        act = malloc(1024);
        act->sa_sigaction = handler;
        act->sa_flags = SA_SIGINFO;

        if(sigaction(3,act,NULL) == -1){
                printf("sigaction fail!\n");
        }
        while(1);
        return 0;
}
~    

六、信号量


        我们可以把信号量看成一个锁,把临界资源看成一个房间,信号量可以管理临界资源,只允许同时只有一个进程来使用临界资源,以免造成临界资源的损坏

       1、 创建信号量:int semget(key_t key, int nsems, int semflg);

        key与消息队列、共享内存的key相同,nsems为你创建信号量的数量;semflg为IPC_CREAT|0666

        返回值:成功返回信号量ID;失败返回-1

        2、初始化信号量:int semctl(int semid, int semnum, int cmd, ...);

        semid为信号量ID,semnum为你要初始化哪个信号量 0为第一个;cmd填宏SETVAL,cmd填SETVAL会有第四个参数,是一个联合体内容如下:

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

        要先定义一个union semun联合体变量;初始化val ;1为有锁状态,0为无锁状态,并将联合体传入第四个参数即可

        3、p、v操作(上锁、解锁):int semop(int semid, struct sembuf *sops, unsigned nsops);

        semid为信号量的ID;nsops为上锁的数量;sops为结构体内容如下

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

sem_num为你操作的第几个锁 0为第一个;sem_op为1加锁;为-1则为解锁;sem_flg常规为0

4、删除信号量:int semctl(int semid, int semnum, int cmd, ...);

        cmd为IPC_RMID前两个参数和第2点相同

demo:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.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 p(int semid)
{
        struct sembuf pop;
        pop.sem_num = 0;
        pop.sem_op = -1;
        pop.sem_flg = 0;
        if(semop(semid,&pop,1) == -1){
                printf("p:semop fail!\n");
        }
}

void v(int semid)
{
        struct sembuf vop;
        vop.sem_num = 0;
        vop.sem_op = 1;
        vop.sem_flg = 0;
        if(semop(semid,&vop,1) == -1){
                printf("v:semop fail!\n");
        }
}

int main()
{
        key_t key;
        if(ftok(".",1) == -1){
                printf("ftok fail!\n");
        }
        int semid;
        union semun setval;
        pid_t pid;
setval.val = 0;

        semid = semget(key,1,IPC_CREAT|0666);
        if(semid == -1){
                printf("semget fail!\n");
        }
        if(semctl(semid,0,SETVAL,setval) == -1){
                printf("init sem fail!\n");
        }

        pid = fork();
        if(pid < 0){
                printf("fork fail!\n");
        }else if(pid == 0){
                printf("this is child pro\n");
                v(semid);
        }else{
                p(semid);
                printf("this is farther pro\n");
                v(semid);
        }
        if(semctl(semid,0,IPC_RMID) == -1){
                printf("delete sem fail!\n");
        }

        return 0;
}
                                                                                                                                      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值