Linux —进程间的五种通信方式—(半双工管道、命名管道、消息队列、信号、共享内存),外加信号量。直接上代码:

无名管道pipe(半双工):(仅限同一个程序运行)

创建无名管道会生成特殊文件,只存在于内存中

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//int pipe(int pipefd[2]);

//ssize_t write(int fd, const void *buf, size_t count);
int main()
{
        int  pid;
        int  fd[2];
        char buf[128];

        if(pipe(fd) == -1){ //创建无名管道
                printf("create pipe failed!\n");
        }

        pid = fork();//创建进程
        if(pid < 0){
                printf("creat fork failed!\n");
        }else if(pid > 0){//父进程
                sleep(3); //延迟3秒
                printf("This is father!\n");
                close(fd[0]); //关闭读通道
                write(fd[1],"Hello , From Father!",strlen("Hello , From Father!"));
                //写入通道给子进程
                wait();//等待子进程退出收集exit信号
        }else{            //子进程
                printf("This is Child!\n");
                close(fd[1]);//关闭写通道
                read(fd[0],buf,128);//读通道父进程发送的数据
                printf("read from father:%s\n",buf);//打印读取的数据
                exit(0);//退出信号
        }
        return 0;
}

运行效果如下: (因为父进程等待3秒,所以子进程先运行,等待3秒后父进程运行)

命名管道fifo:(可不同程序运行)

创建命名管道会以特殊设备文件形式存在于文件系统中,如以下我创建的file

程序A:(mkfifo)创建管道(read)读

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
        char buf[30] = {0};
        int nread = 0;
        if((mkfifo("./file",0600) == -1) && errno!= EEXIST)//创建命名管道,可读可写
        {
                printf("mkfifo file failed!\n");
                perror("why:");//perror函数获取失败信息
        }
        int fd = open("./file",O_RDONLY);//只读的方式打开管道file
        while(1){
                nread = read(fd,buf,30);//读管道中的数据
                printf("read=%dbyte from buf:%s\n",nread,buf);//打印读取的字节数,和数据内容
        }
        close(fd);//关闭文件
        return 0;
}

程序B:(write)写

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
//       int mkfifo(const char *pathname, mode_t mode);
int main()
{
        int cnt = 0;
        char *buf = "Ivan hen shuai!"; //要写入的字符串
        int fd = open("./file",O_WRONLY);//只写的方式打卡file
        printf("write open success\n");
        while(1){ //写入5次后跳出循环
                write(fd,buf,strlen(buf));//写入
                sleep(1);//延时一秒等待写入缓存
                if(cnt == 5){
                        break;
                }
        }

        close(fd);//关闭文件
        return 0;
}

运行效果如下: (先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序A打印读取的数据)(fiforead为程序A,fifowrite为程序B)

 

消息队列:msg(可不同程序运行)

再Linux内核中,有专门的结构体链表,来管理和存储消息队列

思路:1、A如何加消息到队列

           2、B如何从队列拿到消息

程序A: msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

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

int main()
{
        int key; 
        key = ftok(".",'z');//通过ftok函数解析成key 
        printf("key=%x\n",key);
        //每一个消息队列都有一个id号
        int msgID = msgget(key,IPC_CREAT|0777);//创建/获取消息队列,可读可写可执行
        if(msgID == -1){
              printf("get que failed!\n");
        }
        //将写入队列的内容读出来,消息类型长度888,编号0
        msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),888,0);
        //打印读取的数据
        printf("read from que:%s\n",readbuf.mtext);
        
        //创建数据,是结构体类型,消息类型长度988
        struct msgbuf sendbuf = {988,"Thank you for reach!\n"};
        //msgsend函数将结构体的数据写入队列
        msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);

        //用完程序,将不用的垃圾队列移除
        msgctl(msgID,IPC_RMID,NULL);
        return 0;
}

程序B: msgsend(整体和msgget差不多,却决于谁先发送,谁先读)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
struct msgbuf readbuf;
struct msgbuf sendbuf = {888,"This is maessage from queue!\n"};
int main()
{
        int key;
        key = ftok(".",'z');
        printf("key=%x\n",key);

        int msgID = msgget(key,IPC_CREAT|0777);
        if(msgID == -1){
              printf("get que failed!\n");
        }

        //msgsend函数将队列里的数据读出来
        msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);
        printf("send over!\n");
        //发送数据到队列

        msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),988,0);
        printf("return from que:%s\n",readbuf.mtext);
        
        //讲垃圾队列移除
        msgctl(msgID,IPC_RMID,NULL);
        return 0;
}

运行效果如下:(先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序AB打印读取的数据)(msgget为程序A,msgsend为程序B)

 内存共享:shm(可不同程序运行)

 内存共享,顾名思义就是他们可以共享某一个内存

思路:1、先创建/获取一个共享内存

           2、文件连接这个内存

           3、释放内存

           4、将内存移除

 程序A: shaget

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//       int shmget(key_t key, size_t size, int shmflg);

int main()
{

    int shmID;
    key_t key;
    char *shmaddr;
    key = ftok(".",1);//获取key值
    shmID = shmget(key,1024*4,IPC_CREAT|0666);//创建共享内存,可读可写,生成一个内存的id号
    if(shmID == -1){
          printf("get failed!\n");
    }        
    /*连接这个内存,第一个0是默认,第二行0是代表映射进去的内容可读可写
    生成一个指针变量,并指向内存*/
    shmaddr = shmat(shmID,0,0);
    printf("shmat ok!\n");//连接成功
    strcpy(shmaddr,"Ivan nb123!");//将字符串写入指针变量
    sleep(5);//等待5秒

    shmdt(shmaddr);//释放内存
    shmctl(shmID,IPC_RMID,0);//将内存移除

    printf("quit\n");
    return 0;
}

程序B: sharead

#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//       int shmget(key_t key, size_t size, int shmflg);

int main()
{

    int shmID;
    key_t key;
    char *shmaddr;
    key = ftok(".",1);
    shmID = shmget(key,1024*4,0);//获取
    if(shmID == -1){
          printf("get failed!\n");
    }
    shmaddr = shmat(shmID,0,0);//连接这个内存
    printf("data:%s\n",shmaddr);//将内存中的数据打印出来
    printf("shmat ok!\n");
//    sleep(5);

    shmdt(shmaddr);//释放内存
    //shmctl(key,IPC_RMID,0);//因为程序已经有了移除内存操作,所以这里不用
    printf("quit\n");
    return 0;
}

运行效果如下:(先运行程序A,等待数据写入读数据到内存,再运行程序B,读取数据,最后程序B打印读取的数据)(shmg为程序A,shmr为程序B)

 

信号:sig(可不同程序运行)

信号对于Linux来说,就像单片机中的硬件中断,这个信号为“软中断”

信号处理有三种方式:忽略,捕捉信号,默认动作

(SIGKILL和SIGSTOP不能被忽略,他们是ctl+C和ctl+Z)

每一个信号都有名字和编号,都已SIG开头

用kill -l来查看linux中有哪些信号

比如说当A程序无法退出时,我们可以再cmd另一端调用这个信号

如KILL  (pid:-9,就是SIGKILL)(A程序的pid号)

前提是我们让程序A绑定了这个KILL这个信号来杀死这个进程

以上是低级信号的操作,下面直接上高级信号操作:

直接上代码demo

程序A: sigaction收信号

#include <signal.h>
#include <stdio.h>
//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
//函数指针,第一个为程序的num,第二个为结构体,第三个非空可接收数据
void handler(int signum, siginfo_t *info, void *context)
{
    printf("get signum:%d\n",signum);//打印出程序的num
    if(context != NULL){//非空可接收数据
            printf("get data:%d\n",info->si_int);//打印出结构体里面的整型数据
            printf("get data:%d\n",info->si_value.sival_int);//可自选
            printf("form:%d\n",info->si_pid);//将程序B的pid打印出来
    }
}

int main()
{

    struct sigaction act;
    printf("pid = %d\n",getpid());//打印出本程序的pid号

    act.sa_sigaction = handler;//额外接收数据,为函数指针
    act.sa_flags     = SA_SIGINFO;//收信号默认

    //第一参数是自定义信号量,第二个是一个结构体,绑定函数,第三个为备份,可NULL
    sigaction(SIGUSR1,&act,NULL);//处理信号函数注册

    while(1);
    return 0;
}

 程序B: sigaqueue发信号

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

//int sigqueue(pid_t pid, int sig, const union sigval value);
int main(int argc,char **argv)
{
    int signum;
    int pid;
    signum = atoi(argv[1]); //因为signum为整型,调用atoi来强制转换ASI码
    pid    = atoi(argv[2]); //因为pid为整型   ,调用atoi来强制转换ASI码

    union sigval value;
    value.sival_int = 100;
   

    sigqueue(pid,signum,value); //发信号,第三个参数为联合体,存储要发的数据
    printf("pid=%d,done\n",getpid());
    return 0;
}

运行效果如下:(程序B为发送信号,程序A为接收信号)

 

 信号量

它不是一个通信方式,它是一个以锁的形式附加在以上其他通信方式的用法

它来决定谁先运行,谁后运行,

可理理解为:

钥匙:信号量,信号量集

房间:临界资源(打印机)

有两个操作:

1、P操作(pgetkey)拿钥匙

2、V操作(vputbackkey)把钥匙放回去

上代码理解:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
//int semget(key_t key, int nsems, int semflg);
//int semop(int semid, struct sembuf *sops, unsigned nsops)
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 pGetKey(int id)
{
        struct sembuf sops;
        sops.sem_num = 0;//信号量的编号
        sops.sem_op  = -1; //取钥匙操作
        sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)
        semop(id, &sops, 1);
        printf("get keys!\n");
}
void vPutBackKey(int id)
{
        struct sembuf sops;
        sops.sem_num = 0;//信号量的编号
        sops.sem_op  = 1; //取钥匙操作
        sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)
        semop(id, &sops, 1);
        printf("put back keys!\n");
}

int main()
{
        int semid;
        key_t key;

        key = ftok(".",2);
                      //信号量集合中有一个信号量        
        semid = semget(key,1,IPC_CREAT|0666);//创建/获取信号量

        union semun initsem;
        initsem.val = 0;
                   //操作第0个信号量 编号0
        semctl(semid,0,SETVAL,initsem);
                   //SETVAL设置信号量的值,设置为initsem
        int pid = fork();
        if(pid > 0){
                //去拿锁
                pGetKey(semid);
                printf("This is Father\n");
                //锁放回去
                vPutBackKey(semid);
                semctl(semid,0,IPC_RMID);//销毁锁
        }else if(pid == 0){
                //这里不拿锁,因为初始状态没有锁,等父进程不执行,来到这里
                printf("This is Child\n");
                //这时调用拿锁操作,锁池就有一把锁
                vPutBackKey(semid);
        }else{
                printf("fork error!\n");
        }
        return 0;
}
                                                                                                          65,1         底端

运行效果如下:(因为初始化没有钥匙,父进程没有拿到钥匙,子进程先运行,然后将钥匙放入,父进程拿到钥匙再运行)

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值