进程间通过信号灯的通信。
信号灯的创建函数semget():
信号灯的删除函数semctl():
例子1:信号灯的创建和删除,通过semget函数创建,调用semctl函数删除。
#include"sys/types.h"
#include"signal.h"
#include"unistd.h"
#include"stdio.h"
#include"stdlib.h"
#include"sys/sem.h"
int main()
{
int semid;
semid=semget(IPC_PRIVATE,3,0777);
if(semid<0)
{
printf("creat semaphore failure\n");
return -1;
}
printf("creat semaphore success\n");
system("ipcs -s");
//delete semaphore
semctl(semid,0,IPC_RMID,NULL);
system("ipcs -s");
return 0;
}
运行结果如图:
成功创建3个信号灯后,又成功删除了。
例子2:一般我们可以用全局变量来实现父子线程之间的同步,如下thread.c的内容:通过定义一个全局变量thread_inter来实现父子线程的同步问题。
#include<pthread.h>
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
int thread_inter=0;
void *fun(void *var)
{
int j;
while(thread_inter==0);
for(j=0;j<10;j++)
{
usleep(100);
printf("this is fun j=%d\n",j);
}
}
int main()
{
int i=0;
char str[]="hello linux\n";
pthread_t tid;
int ret;
ret=pthread_create(&tid,NULL,fun,(void*)str);
if(ret<0)
{
printf("create thread failure\n");
return -1;
}
while(i<10){
usleep(100);
printf("create thread success\n");
++i;
}
thread_inter =1;
while(1);
return 0;
}
运行结果如下:
先运行父线程再运行子线程。
基于例子2,我们进行改进,采用posi信号量来实现父子线程之间的同步问题,即让父线程代码先运行,再让子线程代码运行;建立posix.c文件其实现代码如下
#include<pthread.h>
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
#include"semaphore.h"//信号量的头文件
sem_t sem;//d定义一个全局信号量
void *fun(void *var)
{
int j;
//p wait
sem_wait(&sem);//子线程到这里为阻塞状态sleep
for(j=0;j<10;j++)
{
usleep(100);
printf("this is fun j=%d\n",j);
}
}
int main()
{
int i=0;
char str[]="hello linux\n";
pthread_t tid;
int ret;
sem_init(&sem,0,0);//第一个参数为对哪个信号量初始化,第二个为0则表示线程间通信,非零为进程间通信,第三个为初始化值
ret=pthread_create(&tid,NULL,fun,(void*)str);
if(ret<0)
{
printf("create thread failure\n");
return -1;
}
while(i<10){
usleep(100);
printf("create thread success\n");
++i;
}
//v signal
sem_post(&sem);//父线程运行完循环后,唤醒子线程
while(1);
return 0;
}
编译poix.c文件:gcc -o posix posix.c, 运行./posix;结果如下:
结果和例子2一样,实现了主线程和子线程之间的同步通信。
信号灯的p操作函数semop解析:
实现同步的步骤:1)定义信号量。 2)初始化信号量。3)p或v操作
同理按照例子3一样的思路,我们将信号量换成信号灯的函数来实现父子线程之间的同步;如下例子4:用信号灯来实现多线程之间的通信。
建立sem.c文件,代码如下:
#include"pthread.h"
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
#include"stdlib.h"
#include"sys/ipc.h"//信号灯的头文件
#include"sys/sem.h"
#include"sys/types.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;//semop函数的第二个为结构体的参数的对象
void *fun(void *var)
{
int j;
//p wait
//sem_wait(&sem);//子线程到这里为阻塞状态sleep
//信号灯的p操作函数
mysembuf.sem_op=-1;//初始化为-1表示p操作
semop(semid,&mysembuf,1);
for(j=0;j<10;j++)
{
usleep(100);
printf("this is fun j=%d\n",j);
}
}
int main()
{
int i=0;
char str[]="hello linux\n";
pthread_t tid;
int ret;
semid=semget(IPC_PRIVATE,3,0777);//获取一个信号灯id
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("creat semaphore success ,semid=%d\n",semid);
system("ipcs -s");//check out semaphore
mysemun.val=0;//将其值初始化为0
//通过semctl函数来初始化
semctl(semid,0,SETVAL,mysemun);//信号量的初始化
//sem_init(&sem,0,0);//第一个参数为对哪个信号量初始化,第二个为0则表示线程间通信,非零为进程间通信,第三个为初始化值
mysembuf.sem_num=0;//要操作的信号灯的编号初始化为0
mysembuf.sem_flg=0;//设置信号灯的标志为0,即阻塞操作
ret=pthread_create(&tid,NULL,fun,(void*)str);
if(ret<0)
{
printf("create thread failure\n");
return -1;
}
while(i<10){
usleep(100);
printf("create thread success\n");
++i;
}
//v 操作
//sem_post(&sem);//父线程运行完循环后,唤醒子线程
mysembuf.sem_op=1;//1表示v操作
semop(semid,&mysembuf,1);//第一个参数为信号灯id,第二个为我们初始化的结构体对象的地址,第三个为信号灯的个数
while(1);
return 0;
}
编译sem.c文件;命令:gcc -o sem sem.c -pthread
运行./sem结果入图,可见通过信号灯实现了例子3 的主线程和子线程之间的同步通信。
例子4:利用IPC的信号灯实现非亲缘关系A进程和B进程之间的同步通信;
我们规定要让client先运行对信号灯进行初始化,从而server后运行就可以不要对信号灯初始化了,即信号灯由client创建(定义)和初始化,对于server而言执行打开信号灯操作,即server打印完十条语句打印后执行v操作唤醒client的p操作,从而client进行打印。就是server先运行打印十条语句,然后client再运行打印十条语句,即A进行v操作,client进行p(等待)操作。
建立进程A的代码文件server.c;内容如下:
#include"pthread.h"
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
#include"stdlib.h"
#include"sys/ipc.h"//信号灯的头文件
#include"sys/sem.h"
#include"sys/types.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;//semop函数的第二个为结构体的参数的对象
int main()
{
int i=0;
int key;
key=ftok("./a.c",'a');
if(key<0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key failure\n");
semid=semget(key,3,IPC_CREAT | 0777);//获取一个信号灯id
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("creat semaphore success ,semid=%d\n",semid);
system("ipcs -s");//check out semaphore
//mysemun.val=0;//将其值初始化为0
//通过semctl函数来初始化
//semctl(semid,0,SETVAL,mysemun);//信号量的初始化
mysembuf.sem_num=0;//要操作的信号灯的编号初始化为0
mysembuf.sem_flg=0;//设置信号灯的标志为0,即阻塞操作
while(i<10){
usleep(100);
printf("create server process success\n");
++i;
}
//v 操作
mysembuf.sem_op=1;//1表示v操作
semop(semid,&mysembuf,1);//第一个参数为信号灯id,第二个为我们初始化的结构体对象的地址,第三个为信号灯的个数
while(1);
return 0;
}
建立进程A的代码文件server.c;内容如下:
#include"pthread.h"
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"
#include"stdlib.h"
#include"sys/ipc.h"//信号灯的头文件
#include"sys/sem.h"
#include"sys/types.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;//semop函数的第二个为结构体的参数的对象
int main()
{
int i=0;
int key;
key=ftok("./a.c",'a');
if(key<0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key failure\n");
semid=semget(key,3,IPC_CREAT | 0777);//获取一个信号灯id
if(semid<0)
{
printf("create semaphore failure\n");
return -1;
}
printf("creat semaphore success ,semid=%d\n",semid);
system("ipcs -s");//check out semaphore
mysemun.val=0;//将其值初始化为0
//通过semctl函数来初始化
semctl(semid,0,SETVAL,mysemun);//信号量的初始化
mysembuf.sem_num=0;//要操作的信号灯的编号初始化为0
mysembuf.sem_flg=0;//设置信号灯的标志为0,即阻塞操作
//p wait
mysembuf.sem_op=-1;
semop(semid,&mysembuf,1);
while(i<10){
usleep(100);
printf("create client process success\n");
++i;
}
//v 操作
//mysembuf.sem_op=1;//1表示v操作
//semop(semid,&mysembuf,1);//第一个参数为信号灯id,第二个为我们初始化的结构体对象的地址,第三个为信号灯的个数
while(1);
return 0;
}
编译server.c和client.c生成执行文件server和client;
1,首先运行./client
此时client处于p操作的阻塞状态,等待server运行完后的v操作唤醒。
2,然后运行./server
此时Server打印出十条语句,然后运行v操作唤醒client的p操作,然后client开始打印,打印结果如下图:
以上便是进程间通信的IPC信号灯通信的简单总结。