进程通信(管道,消息队列,共享内存,信号)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

了解进程后,进程之间的通信必须要去学习,本文就是围绕管道,消息队列,共享内存去介绍单机进程通信

提示:以下是本篇文章正文内容,下面案例可供参考

一、进程通信是什么?

本文介绍的进程通信英文缩写是IPC,是指两个不同进程之间传播或交换信息。

二、管道

1.什么是管道?

管道一般指无名管道,虽然也有有名管道。
a.特点
1.它是半双工的,数据只能往一个方向去流动,具有固定的读端和写端。
2.它只能用于具有亲缘关系的进程之间(即父子进程和兄弟进程)。
3.它可以看做成一个特殊的文件,可以用读写文件read和write对它进行操作,但是它不是普通文件,不属于任何文件系统,并且只存在内存中。
b.原型:

#include <unistd.h>
int pipe(int filedes[2]);//返回成功返回0,返回失败返回-1

当管道建立时,它会创建两个文件描述符;filedes[0]为读而打开,filedes[1]为写而打开。关闭管道关闭这两个文件描述符即可。
在这里插入图片描述

2.无名管道示列

代码示列:

#include <unistd.h>
#include <sys/types.h>
#include<stdio.h>

int main()
{
    int fd[2];
    char buf[128];
    pid_t pid;

    if(pipe(fd)==-1)
    {
        printf("create pipe failed\n");
    }

    pid=fork();

    if(pid==0)
    {
       printf("这是子进程我要给父进程发送:爹,我是子进程是你儿\n");
       close(fd[0]);
       write(fd[1],"爹,我是子进程是你儿",sizeof("爹,我是子进程是你儿"));
    }
    if(pid>0)
    {
        close(fd[1]);
        read(fd[0],buf,128);
        printf("这是父进程,我要接受子进程发的消息:%s\n",buf);
    }

  return 0;
}

结果展示:

xsn@xsn-virtual-machine:~/桌面/test$ ./a.out
这是子进程我要给父进程发送:爹,我是子进程是你儿
这是父进程,我要接受子进程发的消息:爹,我是子进程是你儿
xsn@xsn-virtual-machine:~/桌面/test$ 

3.有名管道(FIFO)

它就是一种文件类型
原型:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);//其中错误返回-1,成功返回0

其中mode参数和open中的参数一样,一般创建一个FIFO,就可以用一般文件IO函数去操作它。
当open一个FIFO时,是否设置非阻塞标志(O_NONBOCK)的区别:

  • 若没有指定O_NONBOCK(默认),只读open要阻塞到某个其他进程为写而打开此FIFO。类似的,只写open要阻塞到其他进程为读而打开它。
  • 若指定了O_NONBOCK,则只读立即返回。而只写open将出错返回-1如果没有进程已经为读而打开FIFO,其errno置ENXIO。
    write代码:
#include <sys/types.h>
#include <sys/stat.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>
#include <unistd.h>

//int mkfifo(const char *pathname, mode_t mode);

int main()
{
   char buf[128]="老子给你发消息能不能收到";

   if(mkfifo("fifo",0600)==-1)
   {
      printf("creative mkfif error");
      perror("why");
   }
   int fd=open("fifo",O_WRONLY);

   printf("我要发送的这些信息是%s,字节数是%ld\n",buf,strlen(buf));
   write(fd,buf,strlen(buf));

return 0;
}

read代码

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

int main()
{
   char buf[128];
   int fd=open("./fifo",O_RDONLY);
   read(fd,buf,sizeof(buf));

   printf("来自有名通道的消息:%s\n",buf);

return 0;
}

当然结果一定是read先阻塞,然后再有数据显示。
write部分

xsn@xsn-virtual-machine:~/桌面/test$ ./write
我要发送的这些信息是:老子给你发消息能不能收到,字节数是36

read部分

xsn@xsn-virtual-machine:~/桌面/test$ ./read
来自有名通道的消息:老子给你发消息能不能收到

三、消息队列

1.什么是消息队列?

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。(就是说创建一个消息队列必须有其ID号,然后它独立存在在于Linux内核之中)
a.特点
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
消息队列独立于发送与接收进程。进程终止时消息队列及其内容不会被删除
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
b.原型

1 #include <sys/msg.h>
2 // 创建或打开消息队列:成功返回队列ID,失败返回-1(其中key是关键字代表这消息队列的序号,flag是标志位)
3 int msgget(key_t key, int flag);
4 // 添加消息:成功返回0,失败返回-1
5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
6 // 读取消息:成功返回消息数据的长度,失败返回-1
7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
8 // 控制消息队列:成功返回0,失败返回-1
9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

在以下两种情况下,msgget将创建一个新的消息队列:
如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
key参数IPC_PRIVATE

函数msgrcv在读取消息队列时,type参数有下面几种情况:
type == 0,返回队列中的第一个消息;
type > 0,返回队列中消息类型为 type 的第一个消息;
type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。
c.补充消息队列的ftok
key_t键和ftok函数
函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键值(也称IPC key键值)。ftok函数原型及说明如下:ftok(把一个已存在的路径名和一个整数标识符转换成IPC键值)

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id)

函数说明
把从pathname导出的信息与id的低序8位组合成一个整数IPC键
函数传入值
pathname:指定的文件,此文件必须存在且可存取
proj_id:计划代号(project ID)
函数返回值
成功:返回key_t值(即IPC 键值)
出错:-1,错误原因存于error中
附加说明
key_t一般为32位的int型的重定义

2.消息队列代码实列

msg1.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<stdio.h>
//int msgget(key_t key, int msgflg);


struct msg_from{
        long byte;
        char buf[128];

};

int main()
{
        int msg_id;
        struct msg_from Sendbuf= {888,"我是来自天空岛的消息"},Revbuf;

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

        msg_id=msgget(0x1890,IPC_CREAT|0777);//创建一个消息队列,若本来就存在就去打开
        int aid=msgsnd(msg_id,&Sendbuf,sizeof(Sendbuf.buf),IPC_NOWAIT);

        if(msg_id==-1)
        {
                printf("creative msg error\n");

        }
        //int msgrcv(int msqid, void *ptr, size_t size, long type,int flag); 
        int id=msgrcv(msg_id,&Revbuf,sizeof(Revbuf.buf),999,0);//0表示着获取阻塞,只有获取到序号为888的消息对列才不阻塞
        if(id==-1) printf("creative msggrv error\n");
        printf("这是来自序列号为%ld的消息:%s\n",Revbuf.byte,Revbuf.buf);

        return 0;
}
~                                                                                                                                                                                    
~                                                                                                                                                                                    
~                

msg2.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<stdio.h>
//int msgget(key_t key, int msgflg);


struct msg_from{
        long byte;
        char buf[128];

};

int main()
{
        int msg_id;
        struct msg_from Sendbuf;
        msg_id=msgget(0x1890,IPC_CREAT|0777);//创建一个消息队列,若本来就存在就去打开
        struct msg_from Sendbuf2= {999,"这里是来自蓝海的消息"};

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

        int aid=msgsnd(msg_id,&Sendbuf2,sizeof(Sendbuf.buf),IPC_NOWAIT);

        if(msg_id==-1)
        {
                printf("creative msg error\n");

        }
        //int msgrcv(int msqid, void *ptr, size_t size, long type,int flag); 
        int id=msgrcv(msg_id,&Sendbuf,sizeof(Sendbuf.buf),888,0);//0表示着获取阻塞,只有获取到序号为888的消息对列才不阻塞
        if(id==-1) printf("creative msggrv error\n");
        printf("这是来自序列号为%ld的消息:%s\n",Sendbuf.byte,Sendbuf.buf);

        return 0;
}

结果

xsn@xsn-virtual-machine:~/桌面/test$ ./msgw
这是来自序列号为999的消息:这里是来自蓝海的消息
xsn@xsn-virtual-machine:~/桌面/test$ ./msgr
这是来自序列号为888的消息:我是来自天空岛的消息

四,共享内存

1,共享内存的理解

简单来说,共享内存就是一块拥有独立内存的一块地址,读写就是对物理地址来进行操作。其一些查看指令是ipcs -m查看共享内存 ipcrm -m shmpid删除指定的共享内存。
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。

1.特点
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
2.原型

 1 #include <sys/shm.h>
2 // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
3 int shmget(key_t key, size_t size, int flag);
4 // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
5 void *shmat(int shm_id, const void *addr, int flag);
6 // 断开与共享内存的连接:成功返回0,失败返回-1
7 int shmdt(void *addr); 
8 // 控制共享内存的相关信息:成功返回0,失败返回-1
9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

当用shmget函数创建一段共享内存时,必须指定其 size(其中size必须是1024的整倍数);而如果引用一个已存在的共享内存,则将 size 指定为0 。

当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。

shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。

shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是IPC_RMID(从系统中删除该共享内存)。

过程就是,先创建一个共享内存用shmget函数,再用shmat来给共享内存一个地址之后就可以用字符串函数strcpy随便对该地址进行操作。

2.代码实列

写入部分:

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

int main()
{
      //1.创建int shmget(key_t key, size_t size, int shmflg);
      int pid;
      char *shmaddr;
      char buf[128]="这里是共享内存的消息:我😍";
      key_t key=ftok(".",1);
     // key_t ftok(const char *pathname, int proj_id);

     pid=shmget(key,1024*4, IPC_CREAT|0666);
     if(pid==-1)
     {
        printf("创建shmget错误\n");
     }

     //2.void *shmat(int shm_id, const void *addr, int flag);此函数返回值为共享内存的地址
     shmaddr=shmat(pid,0,  0);
//     if(shmaddr==-1) printf("shmat返回地址出错,shmaddr=%s\n",shmaddr);

     //3.操作共享内存往里面写东西就行了
     strcpy(shmaddr,buf);
     sleep(5);
     //4.断开与共享内存的连接int shmdt(void *addr); 
     if(shmdt(shmaddr)==-1) printf("断开共享内存失败\n");
     shmctl(pid,IPC_RMID,0);

return 0;
}

读取部分:

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


int main()
{
      //1.创建int shmget(key_t key, size_t size, int shmflg);
      int pid;
      char *shmaddr;
      char buf[128]="这里是共享内存的消息:我😍";
      key_t key=ftok(".",1);
     // key_t ftok(const char *pathname, int proj_id);
     pid=shmget(key,0, 0);
     if(pid==-1)
     {
        printf("创建shmget错误\n");
     }

     //2.void *shmat(int shm_id, const void *addr, int flag);此函数返回值为共享内存的地址
     shmaddr=shmat(pid,0,0);
     //3.操作共享内存往里面写东西就行了
     printf("读到的共享内存的消息是:%s\n",shmaddr);

     //4.断开与共享内存的连接int shmdt(void *addr); 
     if(shmdt(shmaddr)==-1) printf("断开共享内存失败\n");

return 0;

结果实列:xsn@xsn-virtual-machine:~/桌面/test$ ./shmr 读到的共享内存的消息是:这里是共享内存的消息:我😍

五、信号

1,信号的理解

简单理解信号有点类似与单片机的中断,可以称为软件中断。信号提供了一种处理异步事件的方法。比如ctrl+c或者ctra+z中断进程的效果。

2,信号的概念

每个信号都有自己的名字,这些 名字都以SIG开头。且每个信号都有自己的编号,类如 ctrl+c是SIGINT编号是2,当然没有编号为0的信号。kill -l 查看信号的及其序列号,kill -序列号 -进程号就是对这个进程进行信号操作。

3,信号的代码

a,信号的代码signal函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
如果信号信号被传递到进程,则以下
发生以下情况:
*如果配置设置为SIG_IGN,则忽略该信号。
*如果处置设置为SIG_DFL,则默认操作asso-
与信号相关联(参见信号(7))。

ps -aux|grep a.out查看进程号
其中SIGKILL和SIGSTOP不能被忽略。
CTRL+c就是代表着信号
如果在手册中看到see signal(7)) occurs.就是用man手册来查 man 7 signal

signal.c

#include <signal.h>

//typedef void (*sighandler_t)(int);

//sighandler_t signal(int signum, sighandler_t handler);
#include <unistd.h>
#include<stdio.h>

void main()
{
      
     signal(2,SIG_IGN);
     while(1)
     {
             printf("No\n");

	     sleep(1);
     
     }     


}

还一串代码就是不加忽略参数,结果如下。
在这里插入图片描述

b,信号的代码sigactionl函数和sigqueue函数

因为int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);函数更加健壮,所有这里主要说明下sigsction()的参数
sig:需要捕获的信号类型,即上文信号中的展示;
act:指定新的信号处理方式;
oact:输出先前的信号处理方式(不为NULL时)。
1.函数中的actoactsigaction结构体类型指针,sigaction结构体内容如下:

/* Structure describing the action to be taken when a signal arrives.  */
struct sigaction
  {
    /* Signal handler.  */
#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
    union
      {
    /* Used if SA_SIGINFO is not set.  */
    __sighandler_t sa_handler;
    /* Used if SA_SIGINFO is set.  */
    void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler __sigaction_handler.sa_handler
# define sa_sigaction   __sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif
    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;
    /* Special flags.  */
    int sa_flags;
    /* Restore handler.  */
    void (*sa_restorer) (void);
  };

成员说明:
sa_handler:指定的信号处理回调函数指针
sa_mask:设置进程的信号掩码
sa_flags:设置程序收到信号时的行为,可选值如下:
sa_restorer:已过时,不使用

其中sa_flags的参数为以下
在这里插入图片描述

其中回调函数sa_handler函数原型是

handler(int sig, siginfo_t *info, void *ucon text)

sig:就是信号的值
info:是一个联合体
text:只要不为NULL的话info就能直接调用
其中info联合体原型为
英文大家自行翻译即可

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 Linux 2.6.32) */
               void    *si_lower;     /* Lower bound when address violation
                                         occurred (since Linux 3.19) */
               void    *si_upper;     /* Upper bound when address violation
                                         occurred (since Linux 3.19) */
               int      si_pkey;      /* Protection key on PTE that caused
                                         fault (since Linux 4.6) */
               void    *si_call_addr; /* Address of system call instruction
                                         (since Linux 3.5) */
               int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
           }

接收信号函数sigaction

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

//       int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
/*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)
{
   printf("该信号值为%d\n",signum);

   if(context!=NULL)
   {
       printf("get deta=%d\n",info->si_int);
       printf("get data=%d\n",info->si_value.sival_int);
       printf("sendpid is %d\n",info->si_pid);
   }

}

int main()
{
   struct sigaction act;

   act.sa_sigaction = handler;
   act.sa_flags = SA_SIGINFO;//可以得到消息
   sigaction(SIGUSR1,&act,NULL);
   printf("pid=%d\n",getpid());
   while(1);
   return 0;



return 0;
}
                                                                       

发送信号函数sigqueue

#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
//int sigqueue(pid_t pid, int sig, const union sigval value);
//atoi (表示 ascii to integer)是把字符串转换成整型数的>一个函数

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

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

        union sigval value;
        value.sival_int =100;
        printf("我的pid是%d\n",getpid());
        sigqueue(pid,signum,value);
        return 0;
}

六、信号

1.信号量是什么

一个列子:一个房间只有一扇门,对应着一把钥匙,当一个人进入房间时需要用这把钥匙打开,且当下一个人想进入时,必须要上一个人出来将钥匙放出来。这里钥匙就是对应着信号量房间对应着临界资源。这里想到共享内存,共享内存是一放在写共享内存时另一方必须只能看也就是读操作,这里可以放入信号量来进行操作。信号量集就是需要个信号量的集合对应就 p操作:拿锁 v操作:放回锁

2.semget创建信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

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

key:整数值唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget()函数并提供一个键,再由系统生成一个相应的信号标识符(semget()函数的返回值),只有semget()函数才直接使用信号量键,所有其他的信号量函数使用由semget()函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
num_sems: 指定需要的信号量数目,它的值几乎总是1。
sem_flags: 是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。

semget()函数成功返回一个相应信号标识符(非零),失败返回-1.

3.semctl初始化信号量

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

第一个参数是信号量集IPC标识符。
第二个参数是操作信号在信号集中的编号,第一个信号的编号是0。
第三个参数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设置信号量集中的一个单独的信号量的值。

第四个参数为联合体必须自己按照如下定义,一般情况用到val就行,详细看代码即可。

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

4.semop初始化信号量

Linux 下,PV 操作通过调用semop函数来实现

 int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);

第一个参数semid: 信号集的识别码,可通过semget获取。
第二个sops: 指向存储信号操作结构的数组指针,信号操作结构的原型如下

struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};

这三个字段的意义分别为:
sem_num: 操作信号在信号集中的编号,第一个信号的编号是0。
sem_op: 如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,如果没有设置IPC_NOWAIT,则调用该操作的进程或者线程将暂时睡眠,直到信号量的值为0;否则,进程或者线程不会睡眠,函数返回错误EAGAIN
sem_flg: 信号操作标志,可能的选择有两种
IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
第三个参数nsops: 信号操作结构的数量,恒大于或等于1。

成功执行时,两个系统调用都返回0。失败返回-1

5.代码示例(简单的信号量对父子进程之间的控制)

include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<errno.h>
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
//       int semget(key_t key, int nsems, int semflg);
//       int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops); 改变信号量的值
//int semctl(int sem_id, int sem_num, int command, ...);  

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
};

void pGetkey(int id)//p操作拿锁
{
   struct sembuf set;

   set.sem_num=0;
   set.sem_op=-1;
   set.sem_flg=SEM_UNDO;

   semop(id,&set,-1);

}

void vputkey(int id)//v操作放锁
{
   struct sembuf set;

   set.sem_num=0;
   set.sem_op=1;
   set.sem_flg=SEM_UNDO;

   semop(id,&set,1);

}
int main()
{      
       key_t key=0;
       int sem_id;

       //1.创建信号量
       key=ftok(".", 2);
       sem_id=semget(key, 1, 0666|IPC_CREAT);
       if(sem_id==-1)//创建一个信号量,赋予0666->即用户,组用户和其它用户具有读写权限
       {
             perror("semget error:");
       }

       //2.初始化信号量
       union semun itsem;
       itsem.val =0;//操作第0个信号,这就是一个初始化数据,为0的话必须进行一个p操作放锁的动作才行
       if(semctl(sem_id,0,SETVAL,itsem)==-1)//SETVAL设置信号量的值,设置为initsem
       {
             perror("semctl error:");
       }

       //3.p操作拿锁
             


       //4.v操作取锁
       
       
       int pid=fork();

       if(pid>0)
       {   
	   pGetkey(sem_id);
           printf("这是父进程\n\r");
	   vputkey(sem_id);
       }
       else if(pid==0)
       {   
	    
           printf("这是子进程\n\r");
	   vputkey(sem_id);
	  // vputkey(sem_id);
       }
       else
       {
           printf("调用frok函数失败\n");
       }



return 0;
}

总结

管道:无名管道只能在具有亲缘关系的进程之间通信。有名管道就是一种文件类型,用文件函数去操作它即可,有名管道是只要有数据读取就将数据清除。
消息队列:消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值