Linux进程通信

      1.进程间的通信IPC是指在不同进程间的传播和交换信息。

      2.IPC的方式通常有管道(包括无名管道和命名管道),消息队列,信号量,共享存储,Socket,Streams等其中Socket和Streams支持不同主机上的两个进程IPC

      3.管道:

        通常指无名管道特点是:1.他是半双工(数据只能在一个方向上流动)具有固定的读fd[0]写fd[1]端。2.只能用于亲缘关系的进程之间(如父子兄弟进程之间)。3.他是一种特殊文件,不 属于任何文件系统,并且只存在于内存中

  原型:int pipe(int pipefd[2]);

  int main()

{

      int fd[2]; //定义一个数组fd[0]为只读;fd[1]为只写;

      int pid;  //fork返回值;

      char buf[128];

      if(pipe(fd) == -1){  //判断创建无名管道是否成功;

             printf("creat pipe falied\n");

      }

      pid=fork();

      if(pid<0){ // 判断子进程创建;

             printf("creat child falied\n");

      }else if(pid>0){ //进入父进程;

            sleep(2);

            printf("this is father\n");

            close(fd[0]);//关闭只读口准备写

            int nwrite= write(fd[1],"qwedrft",strlen("qwedrft"));

            wait();// 防止成为僵尸进程;

      }else{

            printf("this is child\n");

            close(fd[1]); //关闭只写;

            read(fd[0],buf,128);//从fd[0]将内容读到buf里;

            printf("read from father:%s\n",buf);

            exit(0);

      }

      return 0;

}

      4.FIFO,也叫命名管道,是一种文件类型,特点:(1)可在无关进程之间交换数据,与无名管道不同,(2)FIFO有路径名与之相关联以一种特殊设备文件形式存在于文件系统中。

 fifo具有管道特性,数据传输完成后管道中的数据全部清空

     原型:int mkfifo(const char *pathname, mode_t mode); pathname为带路径的文件名;mode和open中的mode一样。若成功返回0;出错返回-1.

#include <errno.h>

#include <fcntl.h>//判断是否有原文件的头文件。

                        接收端:

         int main()

{

       char buf[128];// 定义一个字符数组接收信息;

       int fd;

       if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //判断是否成功若file文件开始就存在也不会报错。

              printf("mkfifo failuer\n");

              perror("why:\n");

       }else{

              fd=open("./file",O_RDONLY);//打开文件且权限为只读;若不强调则默认阻塞,若强调O_NONBLOCK则不阻塞,分情况用。

              printf("open success\n");      

       }

       while(1){

             int nread=read(fd,buf,128);

             printf("nread=%d;from file:%s\n",nread,buf);

       }                                                                               

       close(fd);                                                                   

       return 0;

}

                            发送端:

int main()

{

      int fd;

      char *str="qwdegtj";

      int cnt=0;

      fd=open("./file",O_WRONLY);  //打开mkfifo创建的文件,并设置只写权限;

      printf("open success\n");

      while(1){

            write(fd,str,strlen(str));//将str中的内容写入fd中

            sleep(1);

            cnt++;//计数标记

            if(cnt == 5){

                  break;

            }

      }

      close(fd);

      return 0;

}

5.消息队列是消息的链接表,存放在内核中,一个消息队列有一个标识符(即队列ID)来标识,可以进行非亲属进程之间的消息传递。

    特点:(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

              (2)消息队列独立于发送与接受进程,进程结束时,消息队列及其内容不会被删除。

              (3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

              (4)要使两个不同进程通过消息队列传递信息,则fotk的参数必须一样,得到的key值才能一样,且信息类型也要相同。

typedef struct{        //消息队列的消息格式:

       long mtype;  //消息类型,必须是长整形,必须放在结构体的第一个,可以自己定值;

       char mtext[128];//消息内容,可以有多个成员,可以是多种类型的数据;

}msg;

int main()

{

       msg snd_buf={988,"thank you for reach"};//定义一个结构体变量,并输入内容,消息类型为888,内容为一串字符 

       msg rcv_buf;  //接收端消息内容结构体变量;                                                

       int msg_id; //消息队列标识符;由msgget函数返回;

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

  //pathname为路径名,"."代表当前路径;proj_id为项目ID只有低8位有效,可自己定值;

       key_t key;                

       key=ftok(".",'5');        //若成功返回key值,失败返回-1;key值相当于open里的fd

       printf("the key=%x\n",key);

// int msgget(key_t key, int msgflg); //msgflg为标识函数行为,key为IPC键值;                         

       msg_id=msgget(key,IPC_CREAT|0777);//若没有队列则创建权限为0777的队列;(IPC_EXCL:若原有则报错返回失败-1)返回值为队列ID

       if(msg_id == -1){

                printf("msg_id get error!!");

       }

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

     发送消息msg_id:队列标识符;*msgp:待发消息结构体的地址;msgsz:消息正文字节数;msgflg:函数控制属性(若为0则阻塞;若IPC_NOWAIT则不阻塞)

       msgsnd(msg_id,&snd_buf,strlen(snd_buf.mtext),0);

       printf("snd oxer!!");

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

     接收消息:msgid:队列ID;*msgp:存放消息结构体地址;msgsz:消息正文字节数;msgtyp接收的消息的类型;msgflg(0则阻塞;IPC_NOWAIT不阻塞)

       msgrcv(msg_id,&rcv_buf,sizeof(rcv_buf.mtext),988,0);

       printf("from snd:%s\n",rcv_buf.mtext);

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

msqid为要操作的队列id;cmd有三个指令如下:

a9e7d3c49dcd676d7b6982c75c0f4dae.png

 //buf用来存放或更改队列的各种信息和属性NULL就是不需要存放

       msgctl(msg_id,IPC_RMID,NULL);

       return 0;

}

6.共享内存

       特点:(1)允许两个不相关的进程访问同一个内存;

                 (2)共享内存是两个正在运行的进程之间传递数据的最有效的方式;

                 (3)不同进程之间的共享内存通常安排为同一段物理内存;

                 (4)共享内存不提供任何互斥和同步机制,一般用信号量对临界资源进行保护;

                 (5)接口简单;

             创建端:

int main()

{

       key_t key;  

//int shmget(key_t key, size_t size, int shmflg); //size只能为1024的整数倍;shmflg:权限标志(IPC_CREAT:创建内存并设置权限)

      int shm_id;                                                                                                                                        char *shmaddr;   //联接共享内存的指针。

       key=ftok(".",'1');      //获得key值

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

      shm_id=shmget(key,1024*4,IPC_CREAT|0600);//创建4M的共享内存;得到shm_id的值;

       printf("ID:%d\n",shm_id);

       if(shm_id == -1){    //判断是否创建成功;

                 printf("shm get error!!\n");

                 exit(-1);

       }

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

         //shmid:由shmget返回值得到;shmaddr:用来指定共享内存映射到进程中的位置,若要成功flg必须为SHM_RDN标志。

      shmaddr=shmat(shm_id,0,0);//第一个0表示让系统自动分配地址;减轻程序对硬件的依赖,第二个0表示内存可读可写。

strcpy(shmaddr,"qedweferh");//将内容写入联接地址;

 sleep(5);//等5秒,等待接受端;

//int shmdt(const void *shmaddr);//解除映射;

 shmdt(shmaddr);

//int shmctl(int shmid, int cmd, struct shmid_ds *buf); //cmd控制指令(IPC_RMID:删除这块共享内存);buf接受内存结构体中的信息,若删除就写0;

 shmctl(shm_id,IPC_RMID,0);

 printf("quit\n");

 return 0;

}

             接收端:

int main()

{

       key_t key;

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

       int shm_id;

       char *addr;

       key=ftok(".",'1');//保证key值相等

以上都相同;

       shm_id=shmget(key,1024*4,0);//直接引用内存不用在创建

       printf("ID=%d\n",shm_id);

       if(shm_id == -1){

                 printf("shm get error!!\n");

                 exit(-1);

       }

//void *shmat(int shmid, const void *shmaddr, int shmflg);//映射到共享内存

      addr=shmat(shm_id,0,0);//接收信息

      printf("shmat ok!\n");

      printf("from write:%s\n",addr);

//int shmdt(const void *shmaddr);

      shmdt(addr);//解除映射

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

      shmctl(shm_id,IPC_RMID,0);//销毁内存

      printf("quit\n");

      return 0;                                     //共享内存可以互相发送和接受用信号量控制

}

7,信号

             同一进程的信号发送:

//typedef void (*sighandler_t)(int);  //信号执行函数

//sighandler_t signal(int signum, sighandler_t handler);

             //signum:信号代号;handler:执行函数 

void handler(int signum)

{

        printf("get signum:%d\n",signum);

        printf("never quit\n");

        switch(signum){

               case 2:

                     printf("SIGINT\n");

                     break;

               case 9:

                     printf("SIGLILL\n");

                     break;

               case 10:

                     printf("SIGUSR1\n");

                     break;

        }

}

int main()

{

        signal(SIGINT,handler);

        signal(SIGKILL,handler);

        signal(SIGUSR1,handler);

        while(1);

        return 0;

}

//int kill(pid_t pid, int sig);//调用kill函数和system函数在不同进程传递信号

信号处理的三种方式:1.接收信号调用处理函数;2.忽略; 3.系统默认处理

int main(int argc,char **argv)

{

            int pid;    //输入main函数的变量                                                  

            int signum;

            char cmd[128];                                                                                                                  

            pid=atoi(argv[2]);  //将字符型转化为整形                                                                          

            signum=atoi(argv[1]);

            printf("pid:%d;signum:%d\n",pid,signum);                                                                       

            sprintf(cmd,"kill -%d %d",signum,pid);//将命令赋于cmd;(kill -9 40333)

            system(cmd);//使用system函数

  //        kill(pid,signum);//调用kill函数;pid为进程号,signum为信号的代号

            printf("kill send ok!!");

            return 0;

}

8.通过信号携带信息,信号的高级使用,sigaction函数捕获信号:

/*struct sigaction{

         void (*sa_sigaction)(int,siginfo_t*,void*);//信号处理函数

         int sa_flags;//指定了对信号的某些特殊处理;

};*/

void handler(int signum,siginfo_t *info,void *context)//当需要处理信息时调用这个函数,signum为要捕获的信号;*info为一个结构体包里面包含信号携带的各种信息;context为NULL是说明信息没有传成功。

{

        printf("get signum:%d\n",signum);

        if(context != NULL){

                printf("get data:%d\n",info->si_value.sival_int);//info中还包含一个联合体value

                printf("det data:%d\n",info->si_int);//和上面那段代码作用一样

  //              printf("get data:%s\n",info->si_value.sival_ptr);//这是一块内存

                printf("thr first pid:%d\n",info->si_pid);//打印发送进程的pid

        }

}

int main()

{

//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

   signum;要捕获的信号;*act:对信号处理的结构体;*oldact:对原来处理信号信息的备份,若不用备份写NULL;

        struct sigaction act;//定义一个信号处理的结构体函数;

        printf("the pid:%d\n",getpid());//打印本进程的pid

        act.sa_sigaction=handler;//定义信号信息处理函数;

        act.sa_flags=SA_SIGINFO;//要想处理信息就必须是这个命令

        sigaction(SIGUSR1,&act,NULL);//act中最重要的三个变量flags决定需不需要处理信息若需要用sa.siigaction函数若不需要用handler函数相当于signal()。

        while(1);                                        

        return 0;         

发送端:                                                                

int main(int argc,char **argv)

{

          int signum;

          int pid;

          pid=atoi(argv[2]);

          signum=atoi(argv[1]);

          printf("pid:%d\n",pid);

          union sigval value;//定义信息处理函数中携带信息的联合体value;

          value.sival_int=100;//输入数据;

    //      value.sival_ptr={"meiyoumingtianle"};//不对

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

          sigqueue(pid,signum,value);//传递信号;pid接收进程的pid,signum传递的信号;value:信息联合体

          printf("pid:%d\n",getpid());

          printf("done\n");

          return 0;

}

9.信号量

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_get_key(int sem_id)//p操作得到钥匙

{

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

       struct sembuf sops;

       sops.sem_num=0;

       sops.sem_op=-1; //1表示对信号量进行p操作,调用一次信号量数减一;拿钥匙的过程;

       sops.sem_flg=SEM_UNDO;

       semop(sem_id,&sops,1);

       printf("get key ok\n");

}

void v_put_key(int sem_id)//v操作放回钥匙

{

//int semop(int semid, struct sembuf *sops, unsigned nsops);//访问信号量集的函数;semid为semget的返回值;*sops为存放对信号量操作的的结构体。nsops有几个信号量结构体就是几。

       struct sembuf sops;

       sops.sem_num=0;//信号量的下标,0表示第一个信号量;

       sops.sem_op=1; //1表示对信号量进行v操作,调用一次信号量数加一;放钥匙的过程;

       sops.sem_flg=SEM_UNDO;//只有两个值IPC_NOWAIT不阻塞无意义;SEM_UNDO阻塞

       semop(sem_id,&sops,1);

       printf("put key ok\n");

}

int main(int argc,char **argv)

{

       int sem_id;

       key_t key;

       int pid;

       key=ftok(".",1);

//int semget(key_t key, int nsems, int semflg);// 创建信号量;nsems为信号量集个数;flg和open函数一样

       sem_id=semget(key,1,IPC_CREAT|0666);

       union semun tmp;//这个联合体中只用到第一个参数即信号量的值,为0时挂起大于0时可执行,一般只有0和1两种。

       tmp.val=0;//先设置其为挂起状态

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

       semctl(sem_id,0,SETVAL,tmp);//控制信号量sen_id为semget的返回值为信号量的id;semnum为要创建的信号量集的个数,一般为1cmd将要采取的动作;若为SETVAL则引入第四个变量联合体semun。

       pid=fork();

       if(pid>0){

            p_get_key(sem_id);//拿钥匙p操作,因为信号量设置为0所以会等待

            printf("this is father\n");

            v_put_key(sem_id);//放钥匙v操作

            semctl(sem_id,0,IPC_RMID);//销毁信号量集

       }else if(pid == 0){

            printf("this is child\n");

            v_put_key(sem_id);//先运行子进程后v操作放回钥匙父进程才能正常拿到钥匙运行

       }else{

            printf("error\n");

       }

       return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值