言之者无罪,闻之者足以戒。 ——《诗序》
信号灯:
命令:ipcs -s查看内核空间的信号灯
下面看一下信号量和信号灯的对比:
功能 | 信号量(POSOX) | 信号量灯(IPC) |
定义信号变量 | sem_t sem1 | semget |
初始化信号量 | sem_init | semctl |
P操作 | sem_wait | semop |
V操作 | sem_post | semop |
信号灯:信号灯集合(可以包含多个信号灯),IPC对象是一个信号灯集(多个信号量)
所有的函数是对一个集合的操作:
1、int semget(key_t key,int nsems,int semflg)创建信号灯函数
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函数原型 | int semget(key_t key, int nsems, int semflg); |
函数参数 | key:和信号灯集关联的key值,可以用宏定义IPC_PRIVATE,也可以用ftok()函数 |
| nsems: 信号灯集中包含的信号灯数目 |
| semflg:信号灯集的访问权限 |
函数返回值 | 成功:信号灯集ID |
| 出错:-1 |
2、int semctl(int semid ,int semnum ,int cmd ,......union semun arg(不是地址))信号灯的获取、修改、删除函数
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> | |
函数原型 | int semctl ( int semid, int semnum, int cmd,…union semun arg(不是地址)); | |
函数参数 | semid:信号灯集ID
semnum: 要修改的信号灯编号 | |
| cmd: | GETVAL:获取信号灯的值 SETVAL:设置信号灯的值 IPC_RMID:从系统中删除信号灯集合 |
|
| |
函数返回值 | 成功:0 | |
| 出错:-1 |
下面我们对比一下三个函数参数的差别:
semctl:cmd: | GETVAL:获取信号灯的值 SETVAL:设置信号灯的值 IPC_RMID:从系统中删除信号灯集合 |
msgctl:cmd shmctl:cmd | IPC_STAT (获取对象属性) IPC_SET (设置对象属性) |
SETVAL:设置信号灯的值 信号灯的初始化,类似sem_init;
在这里直接给出程序学习上面两个函数:
#include "sys/types.h"
#include "sys/sem.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int semid;
semid=semget(IPC_PRIVATE,3,0777);
if(semid < 0)
{
printf("creat semaphore failure\n");
return -1;
}
printf("creat semaphore sucess\n");
system("ipcs -s");
semctl(semid,0,IPC_RMID,NULL);
system("ipcs -s");
return 0;
}
3、int semop(int semid , struct sembuf *opsptr , size_t nops)信号灯的操作函数
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函数原型 | int semop ( int semid, struct sembuf *opsptr, size_t nops); |
函数参数 | semid:信号灯集ID
struct sembuf { short sem_num; // 要操作的信号灯的编号 short sem_op; // 0 : 等待,直到信号灯的值变成0 // 1 : 释放资源,V操作 // -1 : 分配资源,P操作 short sem_flg; // 0, IPC_NOWAIT, SEM_UNDO }; nops: 要操作的信号灯的个数 |
|
|
函数返回值 | 成功:0 |
| 出错:-1 |
上面我们说到了semctl函数,它的第三个参数有三个可选值:GETVAL,SETVAL,IPC_RMID;上次用的是删除也就是IPC_RMID,这次我们用一下设置信号灯值(SETVAL),想要设置信号灯的值就要知道第四个参数union semun arg。
union semun
{
int val;
//SETVAL:设置信号灯的值
struct semid_ds *buf;
// IPC_STAT (获取对象属性)
//IPC_SET (设置对象属性)
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
直接给出代码:
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "sys/ipc.h"
#include "sys/sem.h"
#include <pthread.h>
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) */
};
int semid;
union semun mysemun;
struct sembuf mysembuf;
void *fun(void *var)//child thread code
{
int j;
mysembuf.sem_op=-1;
semop(semid,&mysembuf,1);
for(j=0;j<10;j++)//second
{
usleep(100);
printf("this is fun j=%d\n",j);
}
}
int main()//main thread code
{
int i;
char str[]="hello linux\n";
pthread_t tid;
int ret;
semid=semget(IPC_PRIVATE,3,0777);
if(semid < 0)
{
printf("creat semaphore failure\n");
return -1;
}
printf("creat semaphore sucess,semid=%d\n",semid);
system("ipcs -s");
mysemun.val=0;
semctl(semid,0,SETVAL,mysemun);
//sem_init(&sem,0,0);//sem=0
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
ret=pthread_create(&tid,NULL,fun,(void *)str);
if(ret<0)
{
printf("creat thread failure\n");
return -2;
}
for(i=0;i<10;i++)//first
{
usleep(100);
printf("this is main fun i=%d\n",i);
}
mysembuf.sem_op=1;
semop(semid,&mysembuf,1);
while(1);
return 0;
}
上面的程序编译的时候需要注意的是,它的编译要加上一个后缀-lpthread;命令为:gcc -o sem sem.c -lpthread;如果你没有使用这样的命令编译,那就会有一个错误:undefined reference to `pthread_create';这时候你就用上面我提供的编译命令错误就会消失,然后运行sem就可以看到程序运行的结果。
上一个函数实现的是有亲缘关系的通信,下面给出一个函数实现无亲缘关系的通信,也是进程通信的一个收尾。
实现功能:
(1)创建两个进程server 和client;client进程先运行,server进程后运行
(2)client中的输出信息在server输出信息输出之后才输出,也就是先运行的进程后输出
server函数代码:
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "sys/ipc.h"
#include "sys/sem.h"
#include <pthread.h>
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) */
};
int semid;
union semun mysemun;
struct sembuf mysembuf;
int main()//main thread code
{
int i;
int key;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key scuess\n");
semid=semget(key,3,IPC_CREAT | 0777);
if(semid < 0)
{
printf("creat semaphore failure\n");
return -2;
}
printf("creat semaphore sucess,semid=%d\n",semid);
system("ipcs -s");
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
for(i=0;i<10;i++)//first
{
usleep(100);
printf("this is main fun i=%d\n",i);
}
//v
mysembuf.sem_op=1;
semop(semid,&mysembuf,1);
while(1);
return 0;
}
client函数的代码:
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "sys/ipc.h"
#include "sys/sem.h"
#include <pthread.h>
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) */
};
int semid;
union semun mysemun;
struct sembuf mysembuf;
int main()//main thread code
{
int i;
int key;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key scuess\n");
semid=semget(key,3,IPC_CREAT | 0777);
if(semid < 0)
{
printf("creat semaphore failure\n");
return -2;
}
printf("creat semaphore sucess,semid=%d\n",semid);
system("ipcs -s");
//init
mysemun.val=0;
semctl(semid,0,SETVAL,mysemun);
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
//P wait
mysembuf.sem_op=-1;
semop(semid,&mysembuf,1);
for(i=0;i<10;i++)//second
{
usleep(100);
printf("this is second fun i=%d\n",i);
}
while(1);
return 0;
}
到这里为止进程通信的相关知识就全部说完了。