进程间通信

16 篇文章 0 订阅
14 篇文章 0 订阅

学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。


   Linux下进程通信概述:

传统的进程间通信方式:无名管道(pipe),有名管道(fifo),信号(signal)

System V IPC对象:共享内存,消息队列,信号灯

BSD:套接字(socket

1.无名管道

特点:只能用于具有亲缘关系的进程之间通信。

半双工的通信模式,具有固定的读端和写端。

可以看成一种特殊的文件,对他的读写可以使用文件IO(write,read,不可用lseek)。

1.1无名管道的创建与关闭

管道是基于文件描述符的通信方式。当一个管道建立时,他会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,fd[1]固定用于写管道。构成了一个半双工的通道。

管道创建
头文件#include<unistd.h>
原型int pipe(int fd[2])
参数fd:包含2个元素的整型数组
返回值成功:0
出错:-1

管道读写注意点:

只有当管道的写端关闭,读端才会返回0,否则读端阻塞。

当管道中无数据时,读操作会操作会阻塞。

如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。

只有在管道读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号。导致进程死亡。

当读端关闭,写端还在继续写入数据,则会管道破裂,进程直接结束。


#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    int fd[2];
    if( 0 > pipe(fd))
    {
        perror("pipe");
        exit(-1);
    }

    printf("fd: %d %d\n",fd[0],fd[1]);
    close(fd[0]);
    close(fd[1]);
    return 0;
}


/**************一个管道读一个管道写**************************/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    int fd[2];
    if( 0 > pipe(fd))
    {
        perror("pipe");
        exit(-1);
    }

    pid_t pid;
    char buf[100];
    if( 0 > (pid=fork()))
    {
        perror("fork");
        exit(-1);
    }
    else if(pid == 0)//子进程,写
    {
        close(fd[0]);//关闭读端
        while(1)
        {
            fgets(buf,sizeof(buf),stdin);
            write(fd[1],buf,sizeof(buf));
            if(strncmp(buf,"quit",4) == 0)//比较buf和“qui”的前4个字符
            {
                break;
            }
        }
    }
    else//父进程,读
    {
        close(fd[1]);//关闭写端
        int ret;
        while(1)
        {
            ret = read(fd[0],buf,sizeof(buf));
            if(ret < 0)
            {
                perror("read");
                break;
            }
            else if( 0 == ret)
            {
                printf("write colse\nd");//只有当写端关闭,读端才会返回0,否则读端阻塞
                break;
            }
            fputs(buf,stdout);
        
        }
        close(fd[0]);
    
    }

    return 0;
}


/***************实现copy功能*************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
int main(int argc,char *argv[])
{
    if( argc < 3)
    {
        printf("把filename1中的内容复制到filename2中去\n");
        fprintf(stdout,"usage:%s filename1 filename2\n",argv[0]);
        exit(-1);
    }


    
    int fd[2];//建立管道
    if( 0 > pipe(fd))
    {
        perror("pipe");
        exit(-1);
    }
    
    int ret;
    char buf[100];
    pid_t pid;

    if(0 > (pid = fork()))//建立进程
    {
        perror("fork");
        exit(-1);
    }


/***********************************************************************/

    else if(pid == 0)//***子进程中,文件的读操作,管道的写操作**************
    {
        close(fd[0]);//关闭读管道

        int fd1 = open(argv[1],O_RDONLY);//filemame1以只读方式打开
        if(0 > fd1)//判断文件是否打开成功
        {
            perror("open filename1");
            exit(-1);
        }
        
        while(1)
        {
            if(0 > (ret = read(fd1,buf,sizeof(buf))))//读文件,写入buf
            {
                perror("read");
                exit(-1);
            }
            else if(ret == 0)
            {
                printf("read file end\n");
                close(fd1);//关闭文件1
                break;
            }

            if(0 > write(fd[1],buf,ret))//把buf写入管道
            {
                perror("write");
                exit(-1);
            }
               
        }
        close(fd[1]);//关闭写管道
    }

/***********************************************************************/

    else//*******父进程中,管道读操作,文件写操作***************************
    {
        close(fd[1]);//关闭写管道
        int fd2 = open(argv[2],O_WRONLY | O_CREAT | O_TRUNC,0666);//打开文件2
        if(0 > fd2)//判断是否打开成功
        {
            perror("open filename2");
            exit(-1);
        }

        while(1)
        {
            ret = read(fd[0],buf,sizeof(buf));//读管道,写入buf
            if( 0 > ret )
            {
                perror("read fd");
                exit(-1);
            }
            else if( 0 == ret)
            {
                printf("管道内容读取完毕\n");
                close(fd[0]);//关闭读管道
                break;   
            }

            if(0 > write(fd2,buf,ret))//把管道内容(已结传给buf了),写入文件2
            {
                perror("write file2");
                exit(-1);
            }

        }
        close(fd2);//;关闭文件2
    }

    return 0;
}



2.有名管道
有名管道可以使互不相关的两个进程互相通信。进程通过文件io来操作有名管道,文件内容存在于内核之中。不支持lseek()操作。

有名管道创建
头文件#include<unistd.h>
#include<fcntl.h>
#include<ss/types.h>
原型int mkfifo(const char*filename,mode_t mode);
参数filename:要创建的管道名字
mode:管道的权限,用8进制表示
返回值成功:0
出错:-1

/****建立fifo****************************************************/

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/stat.h>
int main()
{
    if( 0 > mkfifo("myfifo",0777))
    {
        perror("mkfifo");
        exit(-1);
    }
    printf("mkfifo success\n");
    return 0;

}

//2个进程实现一个读一个写

/***********************fifo_read*******************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
	if (argc < 2) {
		fprintf(stderr, "Usage: %s <fifo> \n", argv[0]);
		exit(-1);
	}

	int fd;
	if (0 > (fd = open(argv[1], O_RDONLY))) {
		perror("open");
		exit(-1);
	}

	char buf[100];
	int ret;
	while (1) {
		ret = read(fd, buf, sizeof(buf));
		if (ret < 0) {
			perror("read");
			break;
		}else if (ret == 0) {
			printf("write close\n");
			break;
		}
		fputs(buf, stdout);
	}
	close(fd);

	return 0;
}
/***********************fifo_write*******************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

void handler(int sigo)
{
	printf("I got a SIGPIPE\n");
}

int main()
{
	int fd;
	if (0 > (fd = open("myfifo", O_WRONLY))) {
		perror("open");
		exit(-1);
	}

	signal(SIGPIPE, handler);

	char buf[100];
	int ret;
	while (1) {
		fgets(buf, sizeof(buf), stdin);
		if (0> (ret = write(fd, buf, sizeof(buf)))) {
			perror("write");
			break;
		}
		if (strncmp(buf, "quit", 4) == 0)
			break;
	}
	close(fd);

	return 0;
}




//************************************************************************************************

//*******************************实现copy功能*************************************************

//************************************************************************************************

/*******建立fifo************************************/

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

int main()
{	
	if(0 > (mkfifo("myfifo",0666)))
	{
		perror("mkfifo");
		exit(-1);
		
	}
	printf("mkdido success\n");

	return 0;
}

/*********read-fifo******************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

int main(int argc,char *argv[])
{
	if(argc < 2)
	{
		fprintf(stderr,"Usage: %s <filename>\n",argv[0]);
		exit(-1);
	}
	int fin,fout;
	char buf[100] = {0};
	if( 0 > (fin = open(argv[1],O_RDONLY))) //源文件
	{
		perror("open_fin");
		exit(-1);
	}
	if(0 > (fout = open("myfifo",O_WRONLY)))
	{
		perror("open_fout");
		exit(-1);
	}
	int ret;
	while(1)
	{
		if(0 > (ret = read(fin,buf,sizeof(buf))))
		{
			perror("read");
			break;
		}
		else if(ret == 0)
		{
			printf("write end\n");
			break;
		}
		write(1, buf, ret);  //从源文件读到的内容写入终端
		if(0 > write(fout,buf,ret))
		{
			perror("write");
			break;
		}
	}
	close(fin);
	close(fout);
	return 0;
}
/***********write-fifo*****************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
	if(3 > argc)
	{
		fprintf(stderr,"Usage: %s <fifo><destfile>\n",argv[0]);
			exit(-1);
	}
	int fin, fout;
	if(0 > (fin = (open(argv[1],O_RDONLY)))) //管道文件	
	{
		perror("open_fin");
		exit(-1);
	}
	if(0 > (fout = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666)))//目标文件
	{
		perror("open_fout");
		exit(-1);
	
	}
	int ret;
	char buf[100] = {0};
	while(1)
	{
		if(0 > (ret = read(fin,buf,sizeof(buf))))
		{
			perror("read");
			break;
		}
		else if(ret == 0)
		{
			printf("write close\n");
			break;

		}
		if(0 > write(fout,buf,ret))
		{
			perror("write");
			break;
		}
	}
	close(fin);
	close(fout);
	return 0;
}




3.信号通信

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。

信号的生存周期:1.信号产生---》2.信号注册---》3.信号注销---》4.信号处理

   1是在内核进程中,23是在用户进程中


  用户进程对信号的处理方式:

  忽略信号:对信号不做任何处理,但是有两个信号不能忽略,即SIGKILL 和 SIGSTOP

  捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数

  执行缺省操作:Linux对每种信号都规定了默认操作


信号名                  含义                  默认操作


SIGHUP 终止
SIGINT相当于ctrl c终止
SIGQUIT相当于ctrl \终止
SIGILL 终止
SIGFPE 终止
SIGKILL立即结束程序的运行,不能被
终止,阻塞处理和忽略
终止
SIGALRM当一个定时器到时的时候发出终止
SIGSTOP 暂停进程
SIGTSTP 暂停进程
SIGCHLD子进程改变状态时,父进程会
收到这个信号
忽略
SIGABORT 终止

3.1kill()和raise()

kill -l命令查看系统支持的信号列表

raise函数进程只能自己发送信号

kill
头文件#include<signal.h>
#include<sys/types.h>
原型int kill(pid_t pie,int sig);
参数pid:正数:要接受信号的进程号
0:信号被发送到所有和pid进程在同一个进
  程组的进程
-1:信号发送给所有的进程表中的进程
(除了进程号最大的进程外(即init))
返回值成功;0
出错:-1

raise
头文件#include<signal.h>
#include<sys/types.h>
原型int raise(int sig);(等同于kill(getpid,sig))
参数sig:信号
返回值成功:0
出错:-1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	pid_t pid;
	if (0 > (pid = fork())) {
		perror("fork");
		exit(-1);
	}else if (pid == 0) {
		printf("child!\n");
		sleep(2);
#if 0
		if (0 != raise(SIGKILL)) {
			printf("raise error\n");
			exit(-1);
		}
#else
		if (0 > kill(getpid(), SIGKILL)) {
			perror("kill");
			exit(-1);
		}
#endif
		printf("child!\n");
		exit(0);
	}
	printf("parent!\n");
/*	
	//if (0 > kill(pid, SIGKILL)) {
	if (0 > kill(pid, 9)) {
		perror("kill");
		exit(-1);
	}
*/
	int status;
	if (0 > wait(&status)) {
		perror("wait");
		exit(-1);
	}

	if (WIFEXITED(status)) {
		printf("child exit normally!\n");	
	}else
		printf("child exit abnormally!\n");

	return 0;
}

3.2alarm()和psuse()

alarm()也称为闹钟函数,他可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALARM信号

多个alarm()后一个会把前一个覆盖掉

psuse()函数使用于将调用的进程挂起直到收到信号为止。(挂起一个进程,直到收到一个信号为止)

alarm
头文件#include<unistd.h>
原型unsigned int alarm(unsiged int seconds)
参数seconds:指定秒数
返回值成功:如果调用此alarm()前,进程中已经设置
了闹钟时间,则返回上一个闹钟时间的剩余时间
,否则返回0
出错:-1

pause
头文件#include<unistd.h>
原型int pause(void)
返回值-1,并且把error值设置为EINTR

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main()
{
	alarm(5);
	while (1) {
		alarm(2);
		printf("hello world\n");
		sleep(1);
	}

	return 0;
}

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>

int main()
{
	alarm(5);
	printf("hello world!\n");
	sleep(2);
	pause();
	printf("hello world\n");

	return 0;
}


4.信号的处理

信号处理的主要方法有两种:1使用简单的signal()函数,2使用信号集函数族sigaction()

  使用signal()函数处理时,需指定要处理的信号和处理函数,使用简单,便于理解。


signal()
头文件#include<signal.h>
原型viod *(signal(int signum,void(*hander)(int)))(int);
参数signum:指定信号
hander:SIG_IGN忽略该信号
SIG_DFL采用系统默认方式处理信号
自定义的信号处理函数指针
返回值成功:设置之前的信号处理方式
出错:-1
signal(SIGINT,SIG_IGN)//忽略ctrl c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
int main()
{
	signal(SIGINT, SIG_IGN); //忽略ctrl+c
	signal(SIGQUIT, SIG_IGN); //忽略ctrl+\

	while (1) {
		sleep(1);
		printf("hello world\n");
	}

	return 0;
}

void handler(int sigo)
{
	if (sigo == SIGCHLD)
	{
		wait(NULL);
		printf("Got a child body\n");
	}
}

int main()
{
	signal(SIGCHLD, handler);
	pid_t pid;
	if (0 > (pid = fork())) {
		perror("fork");
		exit(-1);
	}else if (pid == 0) {
		sleep(1);
		printf("child!\n");
		exit(-1);
	}else {
		printf("parent\n");
		sleep(3);
	}

	return 0;
}



4.共享内存

   共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据形式的拷贝。

为了在多个进程间交换信息,内核专门流出了一块内存区,可以由需要访问的进程将其映射到自己私有的地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高了效率。

共享内存查看命令:ipcs -m,删除命令:ipcrm -m shmid

共享内存的使用包括以下步骤:

1.创建/打开共享内存

2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

3.撤销共享内存映射

4.删除共享内存对象


创建/打开共享内存
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
原型int shmget(key_t key,int size,int shmflg);
参数key:IPC_PRIVATE或fork的返回值
size:共享内存区大小
shmflg::同open函数权限位,也可用8进制表示
返回值成功:共享内存段标识符
出错:-1

(说明:ftok()打开文件对应的路径生成唯一的一个key值,原型key_t ftok(const char*name,int projoid),后一个参数是子序号0-255)

(shmflg,用IPC_CREAT创建,例如IPC_CREAT | 0777)

映射共享内存
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
原型void *shmat(int shmid,const void*shmaddr,int shmflg);
参数shmid:要映射的共享内存区标识符
shmadr:将共享内存映射到指定地址(NULL
则由系统自动完成映射)
shmflg:默认0,共享内存可读写
SHM_RDONLY共享内存只读
返回值成功:映射后的地址
出错:-1

解除共享内存映射
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
原型int shmid(const void *shmaddr)
参数shmaddr:共享内存映射后的地址
返回值成功:0
出错:-1
删除共享内存段
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
原型int  shmctl(int shmid,int cmd,struct shmid_ds *buf)
参数shmid:要操作的共享内存标识符
cmd:IPC_STAT获取对象属性
IPC_SET设置对象属性
IPC_RMID删除对象,第三个参数设NULL
返回值成功:0
出错:-1


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

//创建一个共享内存,在共享内存中存放数据

int main()
{
    system("ipcs -m");//相当于在终端直接输入命令ipcs -m,调用系统命令

    key_t key = ftok(".",'a');//得到key值
    if((key_t)-1 == key)//强转-1的类型为key_t
    {
        perror("ftok");
        exit(-1);
    }

//创建或者获取共享内存段ID

    int shmid = shmget(key,100,IPC_CREAT|0666);//使用IPC_CREAT创建,并且设置权限
    if(-1 == shmid)
    {
        perror("shmget");
        exit(-1);
    }

//把共享内存映射到用户空间
    void *ptr = shmat(shmid,NULL,0);
    if((void *)-1 == ptr)
    {
        perror("shmat");
        exit(-1);
    }

//往映射的空间存放数据
    strcpy((char *)ptr,"hello world!");

    system("ipcs -m");
    return 0;
}


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

//创建一个共享内存,在共享内存中获取存放数据

int main()
{
    system("ipcs -m");//相当于在终端直接输入命令ipcs -m,调用系统命令

    key_t key = ftok(".",'a');//得到key值
    if((key_t)-1 == key)//强转-1的类型为key_t
    {
        perror("ftok");
        exit(-1);
    }

//创建或者获取共享内存段ID

    int shmid = shmget(key,100,IPC_CREAT|0666);//使用IPC_CREAT创建,并且设置权限
    if(-1 == shmid)
    {
        perror("shmget");
        exit(-1);
    }

//把共享内存映射到用户空间
    void *ptr = shmat(shmid,NULL,0);
    if((void *)-1 == ptr)
    {
        perror("shmat");
        exit(-1);
    }

//从映射的空间取出数据
    printf("ptr:%s\n",(char *)ptr);
    system("ipcs -m");

//解除映射
    if( 0 > shmdt(ptr))
    {
        perror("shmdt");
        exit(-1);
    }

//删除共享内存段
    if( 0 > shmctl(shmid,IPC_RMID,NULL))
    {
        perror("shmctl");
        exit(-1);
    }

    system("ipcs -m");
    return 0;
}


5.消息队列
消息队列是IPC对象的一种,消息队列由消息队列ID来唯一标识。
消息队列就是一个消息的列表,用户可以再消息队列中添加消息,读取消息等。消息队列可以按照类型来发送/接收消息

创建或打开消息队列使用的函数是msgget,这里创建的消息队列的数量会受到系统消息队列数量的限制
添加消息使用的函数是msgsnd,按照类型把消息添加到已代开的消息队列末尾
读取消息队列使用的函数是msgrcv,可以按照类型把消息从消息队列中取走
控制消息队列使用的函数是msgctl,他可以完成多项功能

msgget创建/打开消息队列
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
原型int msgget(key_t key,int flag)
参数key:消息队列关联的key值
flag:消息队列的访问权限
返回值成功:消息队列ID
出错:-1


msgsnd添加消息
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
原型int msgsnd(int msqid,const coid* msgp,size_t size,int flag)
参数msqid:消息队列ID
msgp:指向消息的指针,常用消息结构体msgbu
struct msgbuf{
long mtype://消息类型
char mtex[N];//消息正文
}
size:发送的消息正文的字节数
flag:IPC_NOWAIT消息没有发送完成函数也会
立即返回
0:直到发送完成函数才会返回
返回值成功:0
出错:-1
msgrcv读取消息
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
原型int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int  flag)
参数 migid:x消息队列ID
msgp:接收消息的缓存区(就是结构体的地址)
size:要接收的正文消息字节数
msgtype:0:接收消息队列中第一个消息(
一个一个接收)
大于0:接收消息队列中第一个类型为
mstype的消息
小于0:接收消息队列中类型值不小于msgtype
的绝对值且类型又最小的消息
flag:0,若无纤细函数会一直阻塞
IPC_NOWAIT 若没有消息,进程会立即
返回ENOMSG
返回值成功:接收到消息的长度
出错;-1



msgctl控制消息队列
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
原型int msgctl(int msgid,int cmd,struct msgid_ds *buf)
参数msgid:消息队列的ID
cmd:IPC_STAT读取消息队列的属性,并将
其保存在buf指向的缓存区中
IPC_SET:设置消息队列的属性,这个值
自取buf参数
IPC_RMID从系统中删除消息队列(
此时第三个参数设为NULL)
返回值成功:0
出错:-1

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
//创建一个消息队列,添加消息到消息队列


struct msgbuf{
    long mtype;
    char mtext[100];
};

int main()
{
    system("ipcs -q");

    key_t key = ftok(".",'a');
    if((key_t)-1 == key)
    {
        perror("ftok");
        exit(-1);
    }

//创建消息队列
    int msgid = msgget(key,IPC_CREAT | 0666);
    if(msgid < 0)
    {
        perror("msgget");
        exit(-1);
    }

    system("ipcs -q");
//添加消息到消息队列中

    struct msgbuf msg;//定义一个消息结构体变量
    msg.mtype = 100;
    strcpy(msg.mtext,"hello world");

    if( 0 > msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0))
    {
        perror("msgsnd");
        exit(-1);
    }
    system("ipcs -q");

    return 0;
}


#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
//创建一个消息队列,获取消息到消息队列


struct msgbuf{
    long mtype;
    char mtext[100];
};

int main()
{
    system("ipcs -q");

    key_t key = ftok(".",'a');
    if((key_t)-1 == key)
    {
        perror("ftok");
        exit(-1);
    }

//创建或获取消息队列
    int msgid = msgget(key,IPC_CREAT | 0666);
    if(msgid < 0)
    {
        perror("msgget");
        exit(-1);
    }

    system("ipcs -q");

//接收消息到消息队列中

    struct msgbuf msg;//定义一个消息结构体变量
    msg.mtype = 100;
    strcpy(msg.mtext,"hello world");
    int ret;
    if( 0 > (ret = msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),100,0)))
    {
        perror("msgrcv");
        exit(-1);
    }
    printf("text:%d,%s\n",ret,msg.mtext);
    system("ipcs -q");

//删除消息队列
    if(0>msgctl(msgid,IPC_RMID,NULL))
    {
        perror("msgctl");
        exit(-1);
    }
    system("ipcs -q");

    

    return 0;
}



6.信号灯(信号量)
信号灯也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制。
信号灯种类:posix有名信号灯,posix基于内存的信号灯(无名信号灯),system v信号灯(IPC对象)这个是一个灯集由几个信号灯组成。
二值信号灯:值为0或1.资源可用为1,不可用为0
计数信号灯:值在0-h之间,用来统计资源,其值代表可用资源数

等待操作是等待信号灯的值变为大于0,然后将其减少1;而释放操作则相反,用来唤醒等待资源的进程或者线程。



创建/获取信号灯
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
原型int semget(key_t key,int nsems,int semflg);
参数key:信号灯集关联的key值
nsems:信号灯集中包含的信号灯数量
semflg:信号灯的访问权限,通常为
IPC_CREAT|0666
返回值成功:信号灯集ID
出错:-1


操作信号灯
头文件#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
原型int semop(int semid,struct sembuf *opsptr,size_t  nops);
参数semid:信号灯集ID(从0开始)
struct sembuf{
short sem_num;//要操作的信号灯编号
short sem_op;//0:等待,直到信号灯的值
变为0  
//1:释放资源 ,V操作
//-1:分配资源,P操作
short sem_flg;//0,IPC_NOWAIT,SEM_UNDO
}
返回值成功:0
出错:-1




控制信号灯
头文件#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;设置信号灯的值
//有前面这2个是,有第四个参数(共用体)
IPC_RMID:从系统中删除信号灯集合
返回值成功:0
出错:-1


/********************************************************************************************************/
/******************************代码在实验手册上改天抄下来****************************************/
/********************************************************************************************************/



6.进程间通讯方式比较
pipo:具有亲缘关系的进程间,单工,数据在内存中
fifo:可用于任意进程间,双工,有文件名,数据在内存中
signal:唯一的异步通信方式
msg:常用于cs模式中,按消息类型访问,可有优先级
shm:效率最高(直接访问内存),需要同步互斥机制
sem:配合共享内存使用,用以实现同步和互斥


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值