linux进程间通信IPC、管道无名pipe、有名FIFO、消息队列msgget、共享内存shmget/at/dt/ctl、信号signal/action/queue信号量semget/ctl/op

用exit和exec无法完全进程间通信

进程间通信IPC有:无名管道pipe、有名管道、消息队列、信号量、共享存储、socket。strams流 两个支持不同主机上两个进程ipc

无名管道pipe(int fd[2]),成功返回0,失败返回-1,fd01 读写

:特点 半双工 、用于亲缘间通信、内在内存中 进程结束即消失

//pipe适用于亲缘之间通信 成功返回0 失败返回-1、fd[0]读 fd[1]写
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//       ssize_t write(int fd, const void *buf, size_t count);

int main()
{
        int fd[2];
        pid_t pid;
        char *p = "hello world!!";
        char buf[128] = {0};

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

        pid = fork();        //创建子进程
        if(pid == -1){        //创建失败
                printf("creat fork fail\n");
        }
        else if(pid > 0){     //父进程 关闭读 开启写
                sleep(3);
                printf("this is the father\n");
                close(fd[0]);
                write(fd[1],p,strlen(p));
                wait(NULL);
        }
        else{                //子进程 关闭写 开启读 如果没有读到就会阻塞在read
                printf("this is the child\n");
                close(fd[1]);
                read(fd[0],buf,128);
                printf("zi jin cheng read:%s\n",buf);
        }

        return 0;
}

 

 FIFO 命名管道 是一种文件类型        int mkfifo(*path,mode)成功返回0 失败返回-1,mode和open函数一样,可以与无关进程相互交换数据,

如果要实现命名管道间相互交换数据、必须一读一写 搭配使用才行。

 读写配合

//命名管道通信:mkfifo先创建管道(读或者写端创建管道)对该文件 一端读 一端写
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>



int main()        //创建管道 并阻塞读取内容
{
//       int mkfifo(const char *pathname, mode_t mode);
        int mk;
        mk = mkfifo("./file1",0600);                //创建管道 正确返回0 错误返回-1
        if((mk == -1) && errno != EEXIST){          //创建失败 且错误号不为EEXIST
                printf("creat mkfifo fail\n");      //errno 错误号
                perror("why:");
        }
        else if(mk == 0){                            //创建管道成功
                printf("creat mkfifo succeed\n");
        }

        int fd;
        char buf[30] = {0};
        fd = open("./file1",O_RDONLY);                //在管道中打开该文件

        while(1){
                int n_read = read(fd,buf,30);        //阻塞读取内容
                printf("read:%d  %s\n",n_read,buf);
                sleep(1);
        }
        close(fd);

        return 0;
}


int main()
{
        int cnt = 0;
        int fd;
        char *str = "mkfifo succeed!!!";
        fd = open("./file1",O_WRONLY);                //打开管道中的文件 并写内容

        while(1){
                int n_write = write(fd,str,strlen(str));//写内容
                cnt++;
                if(cnt == 5){
                        break;
                }
                printf("write:%d  %s\n",n_write,str);
                sleep(2);
        }
        close(fd);

        return 0;
}

 消息队列、

        key_t key;
        key = ftok(".",'A');
        int msg_id = msgget(key,IPC_CREAT|0777);

// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//     msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

//消息队列 创建消息队列 存放或读取消息队列结构体里面的消息 发送和接收端口号要一致 
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct msgbuf {          //获取到消息队列id后 信息再这个结构体里面
       long mtype;       /* message type, must be > 0 */
       char mtext[128];    /* message data */
};

int main()
{
        //       int msgget(key_t key, int msgflg);
        //       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
        //       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,                                                                                 
                                                                          //intmsgflg);
        
        key_t key;                
        key = ftok(".",5);      //fork函数创建一个键值,"."当前路径 5/A键值
        printf("key:%x\n",key);

        struct msgbuf readBuf;
        struct msgbuf writeBuf = {988,"zhe shi 988 jie shou\n"};

        int msgId = msgget(key,IPC_CREAT|0777);    //获取消息队列的ID
        if(msgId == -1){                            //获取失败
                printf("msgget faile!\n");
        }

        msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
        //依据消息队列ID读取结构体里面的消息 根据端口号对接 没有的话会阻塞在这里等待 
        printf("888 readBuf:%s\n",readBuf.mtext);           //打印获取到的消息

        msgsnd(msgId,&writeBuf,strlen(writeBuf.mtext),0); //发送端口号998 结构体里面的内容
        printf("get:send over!!\v");

        msgctl(msgId,IPC_RMID,NULL);    //防止产生过多队列 删除该消息队列
        return 0;
}


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

int main()
{
        //       int msgget(key_t key, int msgflg);
        //       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
        //       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
        key_t key;
        key = ftok(".",5);
        printf("key:%x\n",key);

        struct msgbuf writeBuf = {888,"zhe shi 888 jie shou\n"};
        struct msgbuf readBuf;

        int msgId = msgget(key,IPC_CREAT|0777);
        if(msgId == -1){
                printf("msgget faile!\n");
        }

        msgsnd(msgId,&writeBuf,strlen(writeBuf.mtext),0);
        printf("send:send over!\n");

        msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);
        printf("988 readBuf:%s\n",readBuf.mtext);

        msgctl(msgId,IPC_RMID,NULL);
        return 0;
}

 


共享内存

//写 创建共享内存 指针映射内存 写数据 删地址 删共享内存
//读 获取同一个共享内存 映射 读数据--打印 删地址
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <string.h>

int main()
{

//       int shmget(key_t key, size_t size, int shmflg);
        char * shmaddr = NULL;

        key_t key;    //键值
        key = ftok(".",1);
        int shmId = shmget(key,2,IPC_CREAT|0777); //创建共享内存以M对齐成功返回id 失败返回-1
        if(shmId == -1){
                printf("shamget faile!\n");
                perror("why:\n");
        }

//       void *shmat(int shmid, const void *shmaddr, int shmflg);
        shmaddr = shmat(shmId,0,0);        //映射到一块地址空间 00 默认配置

        strcpy(shmaddr,"hello world!");    //写入数据
        printf("xie ru succeed!\n");
        sleep(5);                            //延时等待读取
//       int shmdt(const void *shmaddr);        
        shmdt(shmaddr);                    //断开连接
//       int msgctl(int msqid, int cmd, struct msqid_ds *buf);
        msgctl(shmId,IPC_RMID,0);            //删除共享内存

        return 0;
}


int main()
{

//       int shmget(key_t key, size_t size, int shmflg);
        char * shmaddr = NULL;

        key_t key;
        key = ftok(".",1);
        int shmId = shmget(key,2,IPC_CREAT|0777); //获取同意共享内存 返回id 失败返回-1
        if(shmId == -1){
                printf("shamget faile!\n");
                perror("why:\n");
        }

//       void *shmat(int shmid, const void *shmaddr, int shmflg);
        shmaddr = shmat(shmId,0,0);               //共享内存映射到地址空间
        printf("read:%s\n",shmaddr);              //读取内容 打印出来
//       int shmdt(const void *shmaddr);        
        shmdt(shmaddr);                            //打开连接

        return 0;
}

int main()
{
        int shmget_id;
        char * addr;

        key_t key;
        key = ftok(".",2);
        shmget_id = shmget(key,1024*4,IPC_CREAT|0644);
        if(shmget_id == -1)
        {
                printf("shmget failuer\n");
                perror("why");
        }

        addr = shmat(shmget_id,0,0);
        printf("shmat ok!!\n");

        strcpy(addr,"hello world");
        sleep(5);
        shmdt(addr);

        shmctl(shmget_id,IPC_RMID,0);
        printf("quit!!\n");
        return 0;

接收部分

        int shmget_id;
        char * addr;

        key_t key;
        key = ftok(".",2);
        shmget_id = shmget(key,1024*4,0);
        if(shmget_id == -1)
        {
                printf("shmget failuer\n");
                exit(-1);
        }

        addr = shmat(shmget_id,0,0);
        printf("recrive over\n");
        printf("datas:%s\n",addr);

//      shmdt(shmaddr);

//      shmctl(shmget_id,IPC_RMID,0);
//      printf("quit!!\n");
        return 0;


信号 signal(signum信号编号 传给handler, handler是中断函数指针);----》void handler(signum) 对应demo2 signalDemo1/2/3.c

//int kill(pid_t pid, int sig);
//typedef void (*sighandler_t)(int);
//sighandler_t signal(int signum, sighandler_t handler);

x信号有三种处理方式 :忽略(SIGKILL和SIGSTOP不能忽略)、捕获(写一个中断函数)、默认

kill -l 查看signum                     ps aux|grep ./a.out查看pid

//进程1 用signal 注册信号处理函数,handler信号处理-- 编译运行aa ps查看该信号pid
//进程2 通过main函数传参 把进程1的信息 signum和pid传进来 用kill函数 或system来实现通信

#include <stdio.h>
#include <signal.h>
#include <stdio.h>
void handler(signum)//信号处理函数 
{
        switch(signum){
                case 2:printf("signum:%d\n",signum);break;//捕获SIGINT信号
                /*用户键入 Ctrl + C 产生。当接收进程接收到这个信号时,signal_handler函数会被调用,并打印出接收到的信号编号。其他指令只能通过ps -aux|grep ./a.out 查看pid kill -x pid*/
                case 3:printf("signum:%d\n",signum);break;
                case 9:printf("signum:%d\n",signum);break;//杀死信号 不能捕获
        }
}

int main()
{
//       typedef void (*sighandler_t)(int);
//       sighandler_t signal(int signum, sighandler_t handler);
        signal(SIGINT,handler);        //捕获  注册信号处理函数
        signal(SIGQUIT,handler);
        signal(SIGKILL,handler);

        while(1);                    //等待信号
        return 0;
}


//进程2
int main(int argc,char **argv)   //传入进程1 的signum和pid
{
        int signum;
        int pid;
        char cmd[128];

        signum = atoi(argv[1]);    //把字符串转换成整型数
        pid = atoi(argv[2]);       //把字符串转换成整型数
        printf("signum:%d  pid:%d\n",signum,pid);
/*
        int k_id = kill(pid,signum); //方法一:kill执行kill -signum pid
        if(k_id == -1){              //错误返回-1
                printf("kill falie!\n");
        }       
*/
        sprintf(cmd,"kill -%d %d",signum,pid);    //方法二:把中间字符串放到cmd里面
        system(cmd);                              //调用函数 执行这个指令
        printf("cmd:%s\n",cmd);
        return 0;
}

 

signal()函数
作用:注册一个信号捕捉函数

typedef void (*sighandler_t)(int);
函数原型:sighandler_t signal(int signum, sighandler_t handler);
函数参数:signum:欲要设置捕捉的信号编号;
handler:回调函数,信号捕捉后所要执行的函数;

注意:该函数由 ANSI 定义,由于历史原因在不同版本的 Unix 和不同版本的 Linux 中可能有不同的行为。因此应该尽量避免使用它,取而代之使用 sigaction 函数。

sigaction() 函数
作用:修改(捕捉)信号处理动作(通常在 Linux 用其来注册一个信号的捕捉函数)

函数原型: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
返回值:成功:0;失败:-1,设置 errno
参数:signum:欲要设置捕捉的信号编号;
act:传入参数,新的处理方式。
oldact:传出参数,旧的处理方式。

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_restorer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素。(弃用)
sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用)
重点掌握:
① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为 SIG_IGN 表忽略 或 SIG_DFL 表执行默认动作
② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
③ sa_flags:通常设置为 0,表使用默认属性。

信号捕捉特性:

进程正常运行时,默认 PCB 中有一个信号屏蔽字,它决定了进程自动屏蔽哪些信号。当注册了某个信号捕捉函数,捕捉到该信号以后,要调用该函数。而该函数有可能执行很长时间,在这期间所屏蔽的信号不由默认mask来指定。而是用 sa_mask 来指定。调用完信号处理函数,再恢复为默认mask。
XX 信号捕捉函数执行期间,XX 信号自动被屏蔽。
阻塞的常规信号不支持排队,产生多次只记录一次。(后 32 个实时信号支持排队)
 

信号高级 发送接收

//高级信号接收sigaction   
#include <stdio.h>
#include <signal.h>
/*
struct sigaction {
       void     (*sa_handler)(int);
       void     (*sa_sigaction)(int, siginfo_t *, void *);
       sigset_t   sa_mask;
       int        sa_flags;
       void     (*sa_restorer)(void);
};
*/

void handler(int signum,siginfo_t *info,void *context) //3 回调函数handler三个参数
{
        printf("signum:%d\n",signum);                   //打印信号编号
        if(context != NULL){                    //只有第三个参数不为空 第二个参数才有值
                printf("get data:%d\n",info->si_int);//指针里面的值
                printf("get data:%d\n",info->si_value.sival_int);//指针里面结构体的值
        }

}

int main()
{


        struct sigaction act;                   //2  信号处理函数 结构体 封装成act
        act.sa_sigaction = handler;             //配置结构体的sa_sigaction 回调handler函数
        act.sa_flags = SA_SIGINFO;              //sa_flags为SA_SIGINFO才会执行handler函数

//       int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
        int sigId = sigaction(SIGUSR1,&act,0);  //1 获取信号函数 信号 处理函数 不备份
        if(sigId == -1){
                printf("sigaction failer!\n");
        }
        printf("%d\n",getpid());
        while(1);
        return 0;
}



//发送sigqueue
/*
   union sigval {
       int   sival_int;
       void *sival_ptr;
   };

*/
int main(int argc,char **argv) //通过这个主函数入口把接收函数的signum 和pid值传进来
{
        int signum;
        int pid;
        signum = atoi(argv[1]);        //转换成整型
        pid = atoi(argv[2]);

        union sigval value;//这个是联合体 即把联合体里面的值传给接收函数
        value.sival_int = 100;
        printf("my pid:%d\n",getpid());
//       int sigqueue(pid_t pid, int sig, const union sigval value);
        sigqueue(pid,signum,value); //封装第三个参数

        printf("send over!\n");
        return 0;
}

 

信号量!!:用于进程间互斥和同步、不存储信息 3个API

semget获取信号量 semctl配置信号量 semop拿锁放回锁

key相当于信息量、多个信息量---信号量集、房间或者进程叫临界资源 一次只允许一个进程使用所以用到key,拿到钥匙给他上锁 别人进不去、出来再放回锁

//信号量 获取创建信号量semget、初始化semctl、fork父子进程、拿锁放锁semop
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//       int semget(key_t key, int nsems, int semflg);
//       int semctl(int semid, int semnum, int cmd, ...);
//       int semop(int semid, struct sembuf *sops, unsigned nsops);
//       int semctl(int semid, int semnum, int cmd, ...);

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 semid){    //拿锁
        struct sembuf sops; //定义semop里面的结构体、也可以是结构体数组 sops[2] nsops = 2
        sops.sem_num = 0;   //起始信号量为0
        sops.sem_op = -1;   //拿锁 锁减1
        sops.sem_flg = SEM_UNDO; //里面没锁 阻塞
        semop(semid,&sops,1);    //拿锁函数 id sops结构体配置 sops个数
}
void vBanckkey(int semid){ //放锁
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op = +1;    //放锁 锁+1
        sops.sem_flg = SEM_UNDO;
        semop(semid,&sops,1); //
}

int main()
{
        key_t key = ftok(".",18);
        int semId = semget(key,1,IPC_CREAT|0600); // 1 创建信号量 返回信号量id
        if(semId == -1){
                printf("semget error!\n");
        }

        union semun value;
        value.val = 0;    //锁的个数
        semctl(semId,0,SETVAL,value);     // 2 信号量初始化参数:id 起始信号量编号0 
                      //SETVAL是信号量的值 具体值通过value联合体设置-----即锁的个数

        pid_t pid = fork();
        if(pid > 0){

                pGetkey(semId);//父进程 拿不到锁等待
                printf("this is the father!\n");
                vBanckkey(semId); //放回锁
        }
        else if(pid == 0){    //子进程有锁 放回去 子进程先运行
                printf("this is the child!\n");
                printf("father wait 3s!\n");
                sleep(3);
                vBanckkey(semId);

        }
        else{
                printf("fork error!\n");
        }
        semctl(semId,1,SETVAL,value); //销毁锁
        return 0;
}
//首先用semget获取信号量、对信号量初始化、fork调用子进程、针对父进程和子进程P操作V操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdio.h>

//int semop(int semid, struct sembuf *sops, unsigned nsops);
//锁函数    (信号量id,结构体--取地址 不是指针,nsops几个结构体数组1)
void vBackKey(int id)//放回去锁
{       struct sembuf set;
        set.sem_num  = 0;//信号量编号 0开始
        set.sem_op = 1;//放锁+1
        set.sem_flg= SEM_UNDO;//相当于等待 

        semop(id,&set,1);//放钥匙
        printf("key: fang hui qu\n");
}
void pGetKey(int id)//拿锁
{       struct sembuf set;
        set.sem_num  = 0;//信号量编号
        set.sem_op = -1;//拿锁-1
        set.sem_flg= SEM_UNDO;//等待 
        semop(id,&set,1);//拿锁
        printf("key: na dao key\n");
}

union semun {//与initsem有关 一般只取第一个
        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) */
};

//       int semget(key_t key, int nsems, int semflg);
int main(int argc,char const *argv[])
{
        key_t key;
        key = ftok(".",1);
        int semid = semget(key,1,IPC_CREAT|0666);//(key,几个信号量1,权限)
                            
//       int semctl(int semid, int semnum, int cmd, ...);//(semid,几个一般从0开始算,cmd决定三个还是4个参数 如果是SETVAL 四个参数第四个由结构体构成,RMID则只要三个参数);
        union semun initsem;
        initsem.val = 0;//设置刚开始里面没锁 则父进程没法拿锁 由子进程放锁后才有锁 再执行父进程
        semctl(semid,0,SETVAL,initsem);

        int pid = fork();
        if(pid > 0){
                pGetKey(semid);//拿锁----没锁会一直等待
                printf("this is the father\n");
                vBackKey(semid);//放回锁
        }
        else if(pid == 0){
                vBackKey(semid);//放回锁
                printf("this is the child\n");
        }
        else{
                printf("pid error\n");
        }

        return 0;
}
                            

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值