Linux进程间的五种通信方式介绍 3

电子说

1.3w人已加入

描述

3、For Example

下面写了一个简单的使用消息队列进行IPC的例子,服务端程序一直在等待特定类型的消息,当收到该类型的消息以后,发送另一种特定类型的消息作为反馈,客户端读取该反馈并打印出来。

msg_server.c

#include#include#include#include
// 用于创建一个唯一的key#define MSG_FILE "/etc/passwd"
// 消息结构struct msg_form { long mtype; char mtext[256];};
int main(){ int msqid; key_t key; struct msg_form msg; // 获取key值 if((key = ftok(MSG_FILE,'z')) < 0) { perror("ftok error"); exit(1); }
// 打印key值 printf("Message Queue - Server key is: %d.\\n", key);
// 创建消息队列 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) { perror("msgget error"); exit(1); } // 打印消息队列ID及进程ID printf("My msqid is: %d.\\n", msqid); printf("My pid is: %d.\\n", getpid());
// 循环读取消息 for(;;) { msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息 printf("Server: receive msg.mtext is: %s.\\n", msg.mtext); printf("Server: receive msg.mtype is: %d.\\n", msg.mtype);
msg.mtype = 999; // 客户端接收的消息类型 sprintf(msg.mtext, "hello, I'm server %d", getpid()); msgsnd(msqid, &msg, sizeof(msg.mtext), 0); } return 0; }

msg_client.c

#include#include#include#include// 用于创建一个唯一的key#define MSG_FILE "/etc/passwd"
// 消息结构struct msg_form { long mtype; char mtext[256];};
int main(){ int msqid; key_t key; struct msg_form msg;
// 获取key值 if ((key = ftok(MSG_FILE, 'z')) < 0) { perror("ftok error"); exit(1); }
// 打印key值 printf("Message Queue - Client key is: %d.\\n", key);
// 打开消息队列 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) { perror("msgget error"); exit(1); }
// 打印消息队列ID及进程ID printf("My msqid is: %d.\\n", msqid); printf("My pid is: %d.\\n", getpid());
// 添加消息,类型为888 msg.mtype = 888; sprintf(msg.mtext, "hello, I'm client %d", getpid()); msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
// 读取类型为777的消息 msgrcv(msqid, &msg, 256, 999, 0); printf("Client: receive msg.mtext is: %s.\\n", msg.mtext); printf("Client: receive msg.mtype is: %d.\\n", msg.mtype); return 0;}

四、信号量

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

1、特点

  1. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
  2. 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
  3. 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
  4. 支持信号量组。

2、原型

最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

#include// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1intsemget(key_t key, int num_sems, int sem_flags);// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1intsemop(int semid, struct sembuf semoparray[], size_t numops);  // 控制信号量的相关信息intsemctl(int semid, int sem_num, int cmd, ...);

当semget创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems),通常为1;如果是引用一个现有的集合,则将num_sems指定为 0 。

在semop函数中,sembuf结构的定义如下:

struct sembuf {    short sem_num; // 信号量组中对应的序号,0~sem_nums-1    short sem_op;  // 信号量值在一次操作中的改变量    short sem_flg; // IPC_NOWAIT, SEM_UNDO}

其中 sem_op 是一次操作中的信号量的改变量:

  • 若sem_op > 0,表示进程释放相应的资源数,将 sem_op 的值加到信号量的值上。如果有进程正在休眠等待此信号量,则换行它们。
  • 若sem_op < 0,请求 sem_op 的绝对值的资源。
    • sem_flg 指定IPC_NOWAIT,则semop函数出错返回EAGAIN
    • sem_flg 没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
    • 当相应的资源数可以满足请求,此信号量的semncnt值减1,该信号量的值减去sem_op的绝对值。成功返回;
    • 此信号量被删除,函数smeop出错返回EIDRM;
    • 进程捕捉到信号,并从信号处理函数返回,此情况下将此信号量的semncnt值减1,函数semop出错返回EINTR
    • 如果相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。
    • 当相应的资源数不能满足请求时,这个操作与sem_flg有关。
  • 若sem_op == 0,进程阻塞直到信号量的相应值为0:
    • sem_flg指定IPC_NOWAIT,则出错返回EAGAIN。
    • sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
    • 信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回;
    • 此信号量被删除,函数smeop出错返回EIDRM;
    • 进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回EINTR
    • 当信号量已经为0,函数立即返回。
    • 如果信号量的值不为0,则依据sem_flg决定函数动作:

在semctl函数中的命令有多种,这里就说两个常用的:

  • SETVAL:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。
  • IPC_RMID:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分