同一主机上进程通信
Unix进程间通信方式:PIPE(无名管道),FIFO(有名管道),Signal(x信号)
System V进程间通信方式:消息队列,信号量,共享内存
不同主机进程间通信
RPC:远程过程调用
Socket:网络通信方式
#include
#include
#include
#include
#include
int main(){
int fd[2];
pipe(fd);
if(fork()==0){
char buff[20];
sleep(2);
memset(buff,'\0',sizeof(buff));
close(fd[1]);
read(fd[0],buff,sizeof(buff));
printf("%s",buff);
}{
close(fd[0]);
write(fd[1],"fuck",5);
wait(NULL);
}
}
Posix2 提供连个使用管道机制实现简单进程间通信的函数
#include
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
#include
#include
int main(){
FILE * f = popen("ls","r");
char buff[256];
while(fgets(buff,256,f))
printf("%s",buff);
pclose(f);
}
#include
#include
#include
#include
#include
#include
int main(){
// if( fd=mkfifo("./fifo",0666) <0){
// perror("make fifio error");
//}
int fd = open("fifo",O_WRONLY);
if(fd<0){
perror("open error");
}
int ret=write(fd,"hello fifio",strlen("hello fifio"));
if(ret<0){
perror("write error");
}
}
因为管道是一种特殊的文件,read,write方法同样适用。
cat < fifo同样可以查看管道内容。
使用管道的时候一定要有两个进程同时打开读端和写端
#include
#include
int kill(pid_t pid, int sig);//给指定进程传送kill函数,这不能见名知意了...
#include
int raise(int sig); //给当前进程发送信号
#include
unsigned int alarm(unsigned int seconds);//唤醒一个进程和设置定时
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
#include
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
#define SHM_SIZE 10000
#define SHM_MODE 0677
int main()
{
int pid, status;
int shmid;
char *ptr,*shmptr;
char buff[1000];
char line[100];
key_t key;
struct shmid_ds state;
if((key = ftok("/",123))<0){
perror("ftok");
}
memset(buff,'\0',sizeof(buff));
//1、先获得一个共享内存的id,多个进程要使用同一块内存必须使用相同id
//key不是必须被ftok生成,但是必须要有所区别
if( ( shmid=shmget(key,SHM_SIZE,SHM_MODE|IPC_CREAT))<0)
perror("shmget error");
// 2、获得id对应的共享内存地址
if ( ( shmptr = shmat(shmid,0 ,0)) == (void *)-1)
perror("shmat error");
//3、接下来就是不同进程对同一块内存进行操作
pid = fork();
if(pid<0){
perror("fork error");
}
if(pid==0){
printf("input the string and press a more (enter) to exit\n:");
while(fgets(line,100,stdin)){
if(strlen(line)<2){
break;
}
strncat(buff,line,strlen(line));
}
memcpy(shmptr,buff,1000);
if(shmdt(shmptr)<0)
perror("shmctl");
}else{
wait(NULL);
memcpy(buff,shmptr,1000);
fputs(buff,stdout);
shmctl(shmid, IPC_STAT ,&state);
printf("%d shares the memory size %u\n",getpid(),state.shm_segsz);
if(shmdt(shmptr)<0)
perror("shmctl");
}
}
这段代码的意思是fork一个子进程,父进程通过共享内存获得子进程输入的字符串。
int shmget(key_t key, size_t size, int shmflg);
key:指定一个标识,用来获得同一块共享内存。 shmflag是标识共享内存权限,创建要使用IPC_CREAT参数。
void *shmat(int shmid, const void *shmaddr, int shmflg);
用来获得共享内存的地址,与当前进程的地址空间建立练习。shmaddr指定共享内存映射地址,最好设为0。
int shmdt(const void *shmaddr);
用来分离共享内存和当前进程
#define SHM_SIZE 10000
#define SHM_MODE 0600
#define WRITE 1
#define READ 0
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int init_sem(int id, int num ,int val)
{
union semun u;
u.val = val;
//num是第几个信号量
if( semctl( id, num ,SETVAL, u) == -1){
perror("inital semaphore");
return 0;
}
return 1;
}
int del_sem(int id, int v)
{
union semun u;
if( semctl(id, 0, IPC_RMID, u) ==-1){
perror("delete semaphore");
return -1;
}
return 0;
}
int do_p(int id,int num)
{
struct sembuf b;
b.sem_num=num, b.sem_op = -1, b.sem_flg = SEM_UNDO;
if (semop( id , &b, 1)==-1){
perror("p error");
return 0;
}
return -1;
}
int do_v(int id ,int num)
{
struct sembuf b;
b.sem_num = num, b.sem_op = 1, b.sem_flg = SEM_UNDO;
if (semop( id , &b, 1)==-1){
perror("v error");
return 0;
}
return -1;
}
int main()
{
pid_t pid;
key_t key;
int shmid;
char *shmptr;
int id;
int food=0;
int semid;
if((key = ftok("/",123))<0){
perror("ftok");
}
//创建共享内存
if( ( shmid=shmget(key,SHM_SIZE,SHM_MODE|IPC_CREAT))<0)
perror("shmget error");
if ( ( shmptr = shmat(shmid,0 ,0)) == (void *)-1)
perror("shmat error");
//1、创建两个信号量,第二个参数是创建信号量集的个数
id = semget(IPC_PRIVATE, 2, 0666|IPC_CREAT);
if(id<0){
perror("semget error");
}
//初始 可写不可读
//Posix提供的是对信号量集的操作,我们再封装一下,可以对特定信号操作
init_sem(id,READ,0);
init_sem(id,WRITE,1);
pid = fork();
if (pid ==0){
while(1)
{
do_p(id,READ);
food = *(int*)(shmptr);
printf("消费了食物%d\n",food);
do_v(id,WRITE);
sleep(2);
}
}else{
while(1)
{
food = rand();
do_p(id,WRITE);
*(int*)(shmptr)=food;
printf("生产了食物%d\n",food);
do_v(id,READ);
sleep(1);
}
}
}
XSI信号量比较难用的原因是因为 它只提供了对一组信号量集合的操作。可以跟Demo一样做一次封装比较容易使用,使得可以操作单个信号量。
值得庆幸的是,信号量和共享内存,消息队列一样提供了一套相似的api。
int semget(key_t key, int nsems, int semflg);
获得一个指向有nsems个信号的集合的标识符
在Linux平台上设置信号量的时候,要自己定义一个Union。
union semun
{
int val; //信号量的值 SETVAL设置
struct semid_ds *buf;
unsigned short *array;
};
int semctl(int semid, int semnum, int cmd, ...);
通过semctl来设置信号量值,semnum来标识信号量集合中的第几个信号量。cmd来表示操作。使用方法可以看demo。
通过semop来进行pv操作,通过sembuf结构体来传值。
sem_op <0 消耗 信号量 ,sem_op>0 增加信号量。
sem_num域来标识集合中第几个信号量。
#include
#include
#include
#include
#include
#include
char buff[1024];
sem_t sem;
void *func(void *a){
sem_wait(&sem);
while (strncmp("end", buff, 3) != 0)
{
printf("length:%d\n", strlen(buff)-1);
sem_wait(&sem);
}
pthread_exit(NULL);
}
int main(){
int status;
pthread_t pid;
void* ret;
status = pthread_create(&pid,NULL,func,NULL);
sem_init(&sem,0,0);
if(status<0){
perror("create thread");
}
while( fgets(buff,1024,stdin)){
sem_post(&sem);
int length =strlen(buff);
if('e'==buff[length-4]&&'n'==buff[length-3]&&'d'==buff[length-2])
break;
}
pthread_join(pid,NULL);
}
Posix信号量是解决是XSI信号量的一些问题。简化了接口,没有信号量集。操作是原子的。(想一下,XSI的PV操作是怎么完成的。)
信号量分两种,有名和无名。
有名在多进程中使用,无名在单进程多线程下使用。
打开一个有名信号量
sem_t *sem_open(const char *name, int oflag);
使用无名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value)
value是初始值,pshared非0表示在多个进程下使用信号量。
关闭一个有名信号量
int sem_close(sem_t *sem);
销毁一个有名信号量
int sem_unlink(const char *name);
销毁一个无名信号量
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);//阻塞等待
int sem_trywait(sem_t *sem);//非阻塞,如果不能获得信号量,返回-1并设置errno为EAGAIN
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem)//用来提升信号量
struct message{
long int message_type;
char buff[100];
};//设置消息的格式, 第一个参数一定要是报文的类型,接受者可以根据类型选择是否接收。
int main(){
int msgid;
struct message data;
if ((msgid =msgget(876378,0666|IPC_CREAT))<0){
perror("msget error");
exit(0);
}
while(1){
printf("input string:");
fgets(data.buff,100,stdin);
if(msgsnd(msgid,&data,100,0)<0){
perror("msgsnd");
exit(0);
}
if ( strncmp(data.buff,"end",3)==0 ){
printf("end");
break;
}
}
}
struct message{
long int message_type;
char buff[100];
};
int main(){
int msgid;
struct message data;
if ((msgid =msgget(876378,0666|IPC_CREAT))<0){
perror("msget error");
exit(0);
}
while(1){
if(msgrcv(msgid,&data,100,0,0)<1){
perror("msgsnd");
exit(0);
}
if ( strncmp(data.buff,"end",3)==0 ){
printf("end");
break;
}
printf("receive from sender:%s\n",data.buff);
}
}
消息队列实在无需多言,看demo就行。具体就man手册。