Linux进程间通信

   

进程间通信:

              ·早期UNIX进程间通信方式:

                     无名管道(pipe)

                     有名管道(fifo)

                     信号(signal)

              ·System V IPC:

                     共享内存(share memory)

                     消息队列(message queue)

                     信号灯集(semaphore set)

              ·套接字(socket)

 

·早期UNIX进程间通信方式:

     无名管道:

              特点:

                     1.只能用于具有亲缘关系的进程之间的通信

                     2.单工的通信模式,具有固定的读端和写端

                     3.无名管道创建时会返回两个文件描述符,分别用于读写管道

                     4.无名管道内的数据是存在内存中的,读取后就会清空

              无名管道创建:

                     pipe.c

                     #include <unistd.h>

                     int pipe(int pfd[2]);

 

                     成功时返回0,失败返回EOF

                     pfd包含两个元素的整形数组,用来保存文件描述符

                     pfd[0]用于读管道,pfd[1]用于写管道

 

                     示例:

                            pipe.c

                            #include <stdio.h>

                            #include <stdlib.h>

                            #include <unistd.h>

                            #include <string.h>

                            #include <sys/types.h>

                            #include <sys/wait.h>



                            int main(void)

                            {

                                   pid_t pid1,pid2;

                                   char buf[32];

                                   int pfd[2];



                                   if (pipe(pfd) < 0)

                                   {

                                          perror("pipe");

                                          exit(-1);

                                   }



                                   if ((pid1 = fork()) < 0)

                                   {

                                          perror("fork");

                                          exit(-1);

                                   }

                                   else if(pid1 == 0)

                                   {

                                          strcpy(buf,"I'm process1");

                                          write(pfd[1],buf,32);

                                          exit(0);

                                   }

                                   else

                                   {

                                          if ((pid2 = fork()) < 0)

                                          {

                                                 perror("fork");

                                                 exit(-1);//表示程序异常退出。

                                          }

                                          else if (pid2 == 0)

                                          {

                                                 sleep(1);

                                                 strcpy(buf,"I'm process2");

                                                 write(pfd[1],buf,32);

                                          }

                                          else

                                          {

                                                 wait(NULL);

                                                 read(pfd[0],buf,32);

                                                 printf("%s\n",buf);

                                                 wait(NULL);

                                                 read(pfd[0],buf,32);

                                                 printf("%s\n",buf);

                                          }

                                   }



                                   return 0;

                            }

              写端存在:

                     ·有数据          read返回实际读取的字节数

                     ·无数据          进程阻塞

              写端不存在:

                     ·有数据          read返回实际读取的字节数

                     ·无数据          read返回0

              读端存在:

                     ·有空间          write返回实际写入的字节数

                     ·无空间          进程阻塞

              读端不存在:

                     ·有空间

                     ·无空间

                     管道断裂!

     有名管道:

              特点:

                     1.对应管道文件,可用于任意进程之间通信

                     2.打开管到时可指定读写方式

                     3.通过文件Io操作,内容存放在内存中

              有名管道创建:

                     #include <unistd.h>

                     #include<sys/types.h>

                     #include<sys/stat.h>

 

                     int mkfifo(const char *path,mode_t mode);

 

                     成功时返回0,失败返回EOF

                     path创建的管道文件路径

                     mode管道文件的权限,如0666

 

              示例:

                     create_fifo.c

                     read_fifo.c

                     write_fifo.c

 

     信号:

              信号机制:

                     ·信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

                     ·linux内核通过信号通知用户进程,不同的信号类型代表不同的事件

                     ·linux对早期unix信号机制进行了扩展

                     ·进程对信号有不同的响应方式

                            1.缺省方式

                            2.忽略信号

                            3.捕捉信号    

                     常用信号:

                            详情见有道云 或 百度百科搜索 信号机制会有详细说明

                                   进程间通信四—信号章节

       信号相关命令:

              kill [-signal] pid

              默认发送SIGTERM

              -sig可指定信号

              pid指定发送对象

 

              killall [-u user | prog]

              prog 指定进程名

              user 指定用户名

 

              例子:

                     kill -9 6437

                     给进程组发信号

                     kill -9 -8126

                     给除init进程和当前进程外的其他进程发信号

                     kill -9 -1   

 

                     killall a.out

                     killall -u linux

       信号相关函数:

              kill / raise

#include <unistd.h>

#inclde <signal.h>


int kill(pid_t pid,int sig);

int raise(int sig);

              用于向任何进程组或进程发送信号。

              成功返回0,失败返回EOF

              pid接收进程的进程号:

                     0代表同组进程         -1代表所有进程

                     sig信号类型

 

              alarm / pause

              int alarm(unsigned char seconds);

              成功时返回上个定时器的剩余时间,失败返回EOF

              seconds定时器的时间

              一个进程中只能设定一个定时器,时间到时产生SIGALRM

 

              int pasue(void);

              进程一直阻塞,直到被信号中断

              被信号中断后返回-1,errno为EINTR

 

              示例:

                     alarm.c

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int main()
{
	alarm(3);

	pause();

	printf("i have been waken up\n");
	return 0;
}

              设置信号响应方式:signal

                     #include <unistd.h>

                     #include <signal.h>

                     void (*signal(int signo,void(*handler)(int)))(int);

 

                     成功时返回原先的信号处理函数,失败返回SIG_ERR

                     signo要设置的信号类型

                     handler指定的信号处理函数;SIG_DFL代表缺省方式;SIG_IGN代表忽略信号

                     只需要设置一次就一直有效

                    

                     示例:

                            signal.c

 #include <stdio.h>

 #include <signal.h>

#include <stdlib.h>

 #include <unistd.h>

 void handler(int signo)

 {

     if(signo == SIGINT)

     {

         printf("i have got SIGINT\n");

     }

    if(signo == SIGQUIT)
    {
        printf("i have got SIGQUIT\n");
    }

}


int main()
{

    signal(SIGINT,handler);
    signal(SIGQUIT,handler);
    while(1)
    {
         pause();
    }
    return 0;
}

System V IPC

       IPC对象包含:

              ·共享内存

              ·消息队列

              ·信号灯集

       每个IPC对象有唯一的ID

       IPC对象创建后一直存在,直到被显示地删除

       每个IPC对象有一个关联的KEY

       ipcs / ipcrm

 

       ipcs  命令   查看 System V 的IPC对象

       ipcrm 命令删除一个或更多的消息队列、信号量集或者共享内存标识。

 

       不同进程通过KEY值能够打开同一个IPC对象

       key为0   是私有对象

 

生成key值——ftok()函数

              #include <sys/types.h>

              #include <sys/ipc.h>

 

              key_t ftok(const cahr *path,int proj_id);

 

              成功时返回合法key值,失败返回EOF

              path存在且可访问的文件路径

              proj_id用于生成key的数字,不能为0

              通过路径与proj_id两者的第八位进行移位组合,生成key

             

1.共享内存:

  1. 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据拷贝
  2. 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
  3. 由于多个进程可同时访问共享内存,因此需要同步和互斥机制的配合使用

 

 

共享内存使用步骤

  1. 创建/打开共享内存
  2. 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
  3. 读写共享内存
  4. 撤销共享内存映射
  5. 删除共享内存对象

1.共享内存创建—shmget

#include <sys/ipc.h>

       #include <sys/shm.h>

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

 

成功返回共享内存的id,失败返回EOF

key 和共享内存关联的key,IPC_PRIVATE或ftok生成

shmflg 共享内存标志位 IPC_CREAT | 0666

size 生成共享内存大小

shmflg:

        0:取共享内存标识符,若不存在则函数会报错

           IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符

           PC_CREAT|IPC_EXCL:如果内核中不存在键值 key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错

 

示例:

        private_shmget.c

        创建一个私有的共享内存,大小512字节,权限0666       

        #include <stdio.h>

        #include <sys/ipc.h>

        #include <sys/shm.h>

        #include <stdlib.h>

        int main()

        {



               int shmid;

               if((shmid = shmget(IPC_PRIVATE,512,066)) < 0)

               {

                      perror("shmget\n");

                      exit(-1);

               }

               return 0;

        }

        创建/打开一个和key关联的共享内存,大小1024,权限0666

        key_shmget.c       

        #include <stdio.h>

        #include <stdlib.h>

        #include <sys/ipc.h>

        #include <sys/shm.h>

        int main()

        {

               key_t key;

               int shmid;

               if((key = ftok(".",'m')) == -1)

               {

                      perror("ftok");

                      exit(-1);

               }

               if((shmid = shmget(key,1024,IPC_CREAT|0666)) < 0)

               {

                      perror("shmget");

                      exit(-1);  

               }

        }

2.共享内存映射—shmat

#include <sys/ipc.h>

#include <sys/shm.h>

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

 

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

shmid 要映射的共享内存id

shmaddr 映射后的地址,NULL表示由系统自动映射

shmflg    标志位0表示可读写;SHM_RDONLY表示只读

3.共享内存撤销映射—shmdt

#include <sys/ipc.h>

#include <sys/shm.c>

int shmdt(void *shmaddr);

 

成功返回0,失败返回EOF

不适用共享内存时应撤销映射

进程结束时自动撤销

4.共享内存控制—shmctl

#include <sys/ipc.h>

#include <sys/shm.h>

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

 

成功时返回0,失败返回EOF

shmid 要操作的共享内存id

cmd 要执行的操作 :

       IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中

       IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内

       IPC_RMID:删除这片共享内存

buf 保存或设置共享内存属性的地址

只是添加删除标记,并不是真正删除

共享内存注意事项

每块共享内存大小有限制

       ipcs –l

       cat /proc/sys/kernel/shmmax

共享内存删除的时间点

       shmctl(shmid,IPC_RMID,NULL)添加删除标记

       nattach 变成0时真正删除

2.消息队列:

消息队列是System V IPC对象的一种

消息队列由消息队列ID来唯一标识

消息队列就是一个消息的列表。用户可在消息队列中添加消息,读取消息等

消息队列使用步骤

  1. 打开/创建消息队列  msgget
  2. 向消息队列发送消息  msgsnd
  3. 从消息队列接收消息  msgrcv
  4. 控制消息队列        msgctl

1.消息队列创建/打开—msgget

#include <sys/ipc.h>

#include <sys/msg.h>

 

int msgget(key_t key,int msgflg);

 

成功时返回消息队列的id,失败返回EOF

key 和消息队列关联的key IPC_PRIVATE 或 ftok

msgflg 标志位,消息队列的建立标志和存取权限。

IPC_CREAT | 0666

IPC_CREAT如果内核中没有此队列,则创建它。

IPC_EXCL当和IPC_CREAT一起使用时,如果队列已经存在,则失败。

       如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。

2.消息发送—msgsnd

#include <sys/ipc.h>

#include <sys/msg.h>

int msgsnd(int msgid,const void *msgp,size_t size,int msgflg);

 

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

msgid 消息队列id

msgp  消息缓冲区地址

size  消息正文长度

msgflg 标志位0或IPC_NOWAIT

 

消息格式:

  1. 通信双方首先定义好统一的消息格式
  2. 用户根据应用需求定义结构体类型
  3. 首成员类型为log,代表消息类型(正整数)
  4. 其他成员都属于消息正文

 

3.消息接收

#include <sys/ipc.h>

#include <sys/msg.h>

int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int msgflg);

 

成功时返回接收到的消息长度,失败返回-1

msgid  消息队列id

msgp   消息缓冲区地址

size   指定接收的消息长度

msgtype  指定接收的消息类型

msgflg   标志欸0或IPC_NOWAIT

4.控制消息队列

#include <sys/ipc.h>

#include <sys/msg.h>

int msgct(int msgid,int cmd,struct msqid_ds *buf);

 

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

msgid 消息队列id

cmd   要执行的操作 IPC_STAT / IPC_SET / IPC_RMID

buf   存放消息队列属性的地址

示例:

要求:两个进程通过消息队列轮流将键盘输入的字符串发送给对方,接收并打印对方发送到消息,输入quit后退出

clientA.c

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>



typedef struct

{

       long mytype;

       char mtext[64];

}MSG;



#define LEN (sizeof(MSG) - sizeof(long))



#define TypeA 100

#define TypeB 200



int main()

{

       key_t key;

       int msgid;

       MSG buf;



       if((key = ftok(".",'q')) == -1)

       {

              perror("ftork");

              exit(-1);

       }

       if((msgid = msgget(key,IPC_CREAT | 0666)) < 0)

       {

              perror("msgget");

              exit(-1);

       }

       while(1)

       {

              buf.mytype = TypeB;

              printf("input > ");

              fgets(buf.mtext,64,stdin);

              msgsnd(msgid,&buf,LEN,0);

              if(strcmp(buf.mtext,"quit\n") == 0)break;  

              if(msgrcv(msgid,&buf,LEN,TypeA,0) < 0)  

              {

                     perror("msgrcv");

                     exit(-1);

              }

              printf("recv from clientB : %s",buf.mtext);

       }

       return 0;

}

client.c

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>



typedef struct

{

       long mytype;

       char mtext[64];

}MSG;



#define LEN (sizeof(MSG) - sizeof(long))



#define TypeA 100

#define TypeB 200



int main()

{

       key_t key;

       int msgid;

       MSG buf;



       if((key = ftok(".",'q')) == -1)

       {

              perror("ftork");

              exit(-1);

       }

       if((msgid = msgget(key,IPC_CREAT | 0666)) < 0)

       {

              perror("msgget");

              exit(-1);

       }

       while(1)

       {

              if(msgrcv(msgid,&buf,LEN,TypeB,0) < 0)   

              {

                     perror("msgrcv");

                     exit(-1);

              }

              printf("recv from clientA : %s",buf.mtext);



              buf.mytype = TypeA;

              printf("input > ");

              fgets(buf.mtext,64,stdin);

              msgsnd(msgid,&buf,LEN,0);

              if(strcmp(buf.mtext,"quit\n") == 0)break;

       }

       return 0;

}

3.信号灯

信号灯也叫信号量,用于进程/线程同步或互斥的机制

信号灯的类型:

  1. Posix 无名信号灯
  2. Posix 有名信号灯
  3. System V 信号灯

信号灯的含义:

       计数信号灯

System V IPC 信号灯特点:

  1. System V 信号灯是一个或多个计数信号灯的集合
  2. 可同时操作集合中的多个信号灯
  3. 申请多个资源时避免死锁

System V 信号灯使用步骤:

       1.打开/创建信号灯    semget

       2.信号灯初始化           semctl

       3.P/V操作                   semop

       4.删除信号灯            semctl

信号灯创建/打开—semget

#include <sys/ipc.h>

#include <sys/sem.h>

 

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

 

成功返回信号灯id,失败返回-1

key 和消息队列关联的key IPC_PRIVATE 或 ftok

nsems 集合中包含的技术信号灯个数

semflg 标志位 IPC_CREAT | 0666  IPC_EXCL

信号灯集合是从0开始

 

 

信号灯控制——semctl

#include <sys/ipc.h>

#include <sys/sem.h>

 

int semctl(int semid,int semnum,int cmd, /*union semun arg*/);

 

成功返回0,失败返回EOF

semid 要操作的信号灯集id

semnum 要操作的集合中的信号灯编号

cmd 执行的操作 SETVAL IPC_RMID

cmd命令:

·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。

·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。

·IPC_RMID将信号量集从内存中删除。

·GETALL用于读取信号量集中的所有信号量的值。

·GETNCNT返回正在等待资源的进程数目。

·GETPID返回最后一个执行semop操作的进程的PID

·GETVAL返回信号量集中的一个单个的信号量的值。

·GETZCNT返回正在等待完全空闲的资源的进程数目。

·SETALL设置信号量集中的所有的信号量的值。

·SETVAL设置信号量集中的一个单独的信号量的值。

 

union semun 取决于cmd:

       union semun {

              short val;          /*SETVAL用的值*/

              struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/

              unsigned short* array; /*SETALL、GETALL用的数组值*/

             struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/

      } arg;

 

信号灯P/V操作——semop

#include <sys/ipc.h>

#include <sys/sem.h>

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

 

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

semid 要操作的信号灯集id

sops 描述对信号灯操作的结构体(数组)

nsops 要操作的信号灯个数

 

信号灯操作结构体——sembuf

struct sembuf

{

       short semnum; 

       short sem_op;

       short sem_flg;

};

semnum    信号灯编号

sem_op    -1:P操作,1:V操作

sem_flg     0/IPC_NOWAIT

 

示例:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <signal.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>



#define N 64

#define READ 0

#define WRITE 1



union semun

{

       int val;

       struct semid_ds *buf;

       unsigned short *array;

       struct seminfo *bufs;   

};



void init_sem(int semid,int s[],int n)

{

       int i;

       union semun myun;

       for(i = 0;i < n;i++)

       {

              myun.val = s[i];

              semctl(semid,i,SETVAL,myun);

       }

}



void pv(int semid,int num,int op)

{

       struct sembuf buf;

       buf.sem_num = num;

       buf.sem_op = op;

       buf.sem_flg = 0;

       semop(semid,&buf,1);

}



int main()

{

       int shmid,semid,s[] = {0,1};

       pid_t pid;

       key_t key;        

       char *shmaddr;



       if((key = ftok(".",'s')) == -1)

       {

              perror("ftok");

              exit(-1);

       }

       if((shmid = shmget(key,N,IPC_CREAT | 0666)) < 0)

       {

              perror("shmget");

              exit(-1);

       }

       if((semid = semget(key,2,IPC_CREAT | 0666)) < 0)

       {

              perror("semget");

              goto _error1;

       }

       init_sem(semid,s,2);

       if((shmaddr = (char *)shmat(shmid,NULL,0)) == (char *)-1)

       {

              perror("shmaddr");

              goto _error2;

       }

       if((pid = fork()) < 0)

       {

              perror("fork");

              goto _error2;

       }

       else if(pid == 0)

       {

              char *p,*q;

              while(1)

              {

                     pv(semid,READ,-1);

                     p = q = shmaddr;

                     while(*q)

                     {

                            if(*q != ' ')     

                            {

                                   *p++ = *q;   

                            }            

                            q++;

                     }

                     *p = '\0';

                     printf("%s",shmaddr);

                     pv(semid,WRITE,1);

              }     

       }

       else

       {

              while(1)

              {

                     pv(semid,WRITE,-1);    

                     printf("input > ");

                     fgets(shmaddr,N,stdin);

                     if(strcmp(shmaddr,"quit\n") == 0)break;

                     pv(semid,READ,1);

              }     

              kill(pid,SIGUSR1);

       }

_error1:

       shmctl(shmid,IPC_RMID,NULL);

_error2:

       semctl(semid,0,IPC_RMID);

       return 0; 

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点灯师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值