Linux进程间通信

管道:匿名管道

没有名字,因此只能通过子进程复制父进程的方式(复制文件描述符)实现通信------只能用于具有亲缘关系的进程间通信

管道原理:操作系统在内核提供一块缓冲区(只要进程可以访问这块缓冲区就可以实现通信)

匿名管道读写特性:

  1. 管道中若没有数据,则read会被阻塞,直到读取到数据为止(有数据写入管道)
  2. 管道中若数据满了,则write会被阻塞,直到有空闲空间(有数据被读取走了)
  3. 若有读端关闭,则写端会触发异常---------SIGPIPE(导致进程退出)
  4. 若所有写端关闭,则read读取完数据之后不会阻塞,而是返回 0
  5. 进程在操作管道的时候,最好如果没有用到那一端就把那一端关掉 
/*  匿名管道的基本使用
 *  int pipe(int pipefd[2]);
 *      pipefd: 输出型参数---用于获取管道操作句柄
 *      pipefd[0]   管道的读取端
 *      pipefd[1]   管道的写入端
 *  返回值:0   失败:-1
 * */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
    int pipefd[2];

    int ret = pipe(pipefd);
    if (ret < 0) {
        perror("pipe error");
        return -1;
    }
    //使用匿名管道实现子进程与父进程之间的通信
    //父进程-写     子进程-读
    int pid = fork();
    if (pid < 0) {
        return -1;
    }else if (pid == 0) {
        close(pipefd[1]);
        //close(pipefd[0]);
        //child
        sleep(5);
        char buf[1024] = {0};
        ret = read(pipefd[0], buf, 1023);
        if (ret < 0) {
            perror("read error");
        }else {
            printf("buf:[%s]\n", buf);
            printf("%d",ret);
        }
    }else {
        close(pipefd[0]);
        //close(pipefd[1]);
        //sleep(3);
        //parent
        int i = 0;
        while(1) {
            char *ptr = "一周又要完了";
            write(pipefd[1], ptr, strlen(ptr));
            printf("ret:%d\n", i);
        }

    }
    return 0;
}

 管道符的实现

#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
int main(){
    int pipefd[2];
    //创建管道
    int ret=pipe(pipefd);
    if(ret<0){
        perror("pipe failure");
        return -1;
    }
    //创建子进程
    int pid1=fork();
    if(pid1<0){
        perror("fork failure");
        return -1;
    }else if(pid1==0){
        //child 1------------ps  -ef
        close(pipefd[0]);
        dup2(pipefd[1],1);
        execlp("ps","ps","-ef",NULL);
        exit(0);
    }
    int pid2=fork();
    if(pid2<0){
        perror("fork error");
        return -1;
    }else if(pid2==0){
        //child 2 -----------grep pipe
        close(pipefd[1]);
        dup2(pipefd[0],0);
        //grep是从标准输入中读取数据
        //将从标准输入改为管道的读取端
        execlp("grep","grep","pipe",NULL);
        exit(0);
    }
    close(pipefd[0]);
    close(pipefd[1]);
    waitpid(pid1,NULL,0);
    printf("child1 exit\n");
    waitpid(pid2,NULL,0);
    printf("child2 exit\n");
    return 0;
}

管道:命名管道

为管道创建一个管道文件,这个管道文件就是管道的名字(有名字体现在文件系统的可见性),命名管道可以用于任意进程间通信

命名管道的读写特性:

  1. 若管道文件没有被写的方式打开,则只读打开会阻塞
  2. 若管道文件没有被读的方式打开,则只写打开会被阻塞
  3. 读写特性雷同于匿名管道

写端 :

/*  命名管道的基本使用
 *  命名管道可见于文件系统,会 创建一个管道文件(文件只是名字)
 *  管道通信的本质还是内核的那块缓冲区
 *  int mkfifo(const char *pathname, mode_t mode);
 *      pathname:  管道文件的路径名
 *      mode:       创建文件的权限
 *  返回值:0   失败:-1
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main()
{
    char *file = "./tmp.fifo";
    umask(0);
    int ret = mkfifo(file, 0664);
    if (ret < 0) {
        //如果文件不是因为已经存在而报错,则退出
        if (errno != EEXIST) {
            perror("mkfifo error");
            return -1;
        }
    }
    printf("create fifo success!!\n");
    int fd = open(file, O_WRONLY);
    if (fd < 0) {
        perror("open error");
        return -1;
    }
    printf("open fifo success!!\n");
    while(1) {
        char buf[1024] = {0};
        scanf("%s", buf);
        write(fd, buf, strlen(buf));
    }
    close(fd);
    return 0;
}

读端:

/*  命名管道的基本使用
 *  命名管道可见于文件系统,会 创建一个管道文件(文件只是名字)
 *  管道通信的本质还是内核的那块缓冲区
 *  int mkfifo(const char *pathname, mode_t mode);
 *      pathname:  管道文件的路径名
 *      mode:       创建文件的权限
 *  返回值:0   失败:-1
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main()
{
    char *file = "./tmp.fifo";
    umask(0);
    int ret = mkfifo(file, 0664);
    if (ret < 0) {
        //如果文件不是因为已经存在而报错,则退出
        if (errno != EEXIST) {
            perror("mkfifo error");
            return -1;
        }
    }
    printf("create fifo success!!\n");
    int fd = open(file, O_RDONLY);
    if (fd < 0) {
        perror("open error");
        return -1;
    }
    printf("open fifo success!!\n");
    while(1) {
        sleep(20);
        char buf[1024] = {0};
        int ret = read(fd, buf, 1023);
        if (ret < 0) {
            perror("read error");
            return -1;
        }else if (ret == 0) {
            printf("write closed\n");
            return -1;
        }
        printf("buf:[%s]\n", buf);
    }
    close(fd);
    return 0;
}

共享内存:进程间最快的通信方式

因为共享内存是将一块物理内存映射到虚拟地址空间,通过虚拟地址空间直接对区域进行修改,因此相较于其他的进程间通信方式,少了两次用户态与内核态之间的数据拷贝过程,所以最快

操作步骤:

1.创建共享内存   shmget()

2.将共享内存映射到虚拟地址空间  shmat()

3.直接对内存进行操作  memcpy()...........

4,接触映射关系  shmdt()

5,删除共享内存     shmctl

写端:

/*************************************************************************
*> File Name: shm.c
*> Author: san
*> Created Time: 2019年04月17日 星期三 19时26分17秒
*> Describe: 共享内存的基本操作
    1. 创建
    2. 建立映射
    3. 内存操作
    4. 解除映射
    5. 删除
*************************************************************************/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
#define PROJ_ID 0X112233
int main(int argc, char *argv[])
{
    //1. 创建
    //key_t ftok(const char *pathname, int proj_id);
    //  通过文件inode节点号与proj_id生成一个key值
    //int shmget(key_t key, size_t size, int shmflg);
    //  key:    共享内存标识符
    //  size:  共享内存大小
    //  shmflg:选项标志
    //      IPC_CREAT   共享内存不存在则创建,存在则打开
    //      IPC_EXCL    与IPC_CREAT同用,则共享内存存在时报错
    //      shm_mode    权限
    //  返回值:标识符(代码中的操作句柄)      失败:-1
    //key_t key = ftok(".", PROJ_ID);
    int shmid = shmget(IPC_KEY, 32, IPC_CREAT | 0664);
    if (shmid < 0) {
    perror("shmget error");
    return -1;
    }
    //建立映射
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //  shmid:  创建共享内存,返回的句柄
    //  shmaddr:置空-映射首地址由操作系统分配
    //  shmflg: 映射成功后的操作权限
    //      SHM_RDONLY  只读
    //      0       默认-可读可写
    //  返回值:映射首地址  失败:(void*)-1
    char *shm_start = shmat(shmid, NULL, 0);
    if (shm_start == (void*)-1) {
    perror("shmat error");
    return -1;
    }
    int i = 0;
    while(1) {
    sprintf(shm_start, "hello world+%d\n", i++);
    sleep(1);
    }
    //解除映射
    //int shmdt(const void *shmaddr);
    //  shmaddr:   映射首地址
    shmdt(shm_start);
    //删除
    //int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    //  shmid:  操作句柄
    //  cmd:   操作类型
    //      IPC_RMID    删除共享内存
    //  buf:   设置/获取共享内存信息
    //共享内存不会被直接删除,而是判断映射连接数是否为0
    //  为0:   直接删除
    //  不为0: 拒绝后续其它进程的映射连接,当映射连接数为0时自动删除
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

读端:

/*************************************************************************
*> File Name: shm.c
*> Author: san
*> Created Time: 2019年04月17日 星期三 19时26分17秒
*> Describe: 共享内存的基本操作
    1. 创建
    2. 建立映射
    3. 内存操作
    4. 解除映射
    5. 删除
*************************************************************************/

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
#define PROJ_ID 0X112233
int main(int argc, char *argv[])
{
    //1. 创建
    //key_t ftok(const char *pathname, int proj_id);
    //  通过文件inode节点号与proj_id生成一个key值
    //int shmget(key_t key, size_t size, int shmflg);
    //  key:    共享内存标识符
    //  size:  共享内存大小
    //  shmflg:选项标志
    //      IPC_CREAT   共享内存不存在则创建,存在则打开
    //      IPC_EXCL    与IPC_CREAT同用,则共享内存存在时报错
    //      shm_mode    权限
    //  返回值:标识符(代码中的操作句柄)      失败:-1
    //key_t key = ftok(".", PROJ_ID);
    int shmid = shmget(IPC_KEY, 32, IPC_CREAT | 0664);
    if (shmid < 0) {
    perror("shmget error");
    return -1;
    }
    //建立映射
    //void *shmat(int shmid, const void *shmaddr, int shmflg);
    //  shmid:  创建共享内存,返回的句柄
    //  shmaddr:置空-映射首地址由操作系统分配
    //  shmflg: 映射成功后的操作权限
    //      SHM_RDONLY  只读
    //      0       默认-可读可写
    //  返回值:映射首地址  失败:(void*)-1
    char *shm_start = shmat(shmid, NULL, 0);
    if (shm_start == (void*)-1) {
    perror("shmat error");
    return -1;
    }
    int i = 0;
    while(1) {
    printf("%s", shm_start);
    sleep(1);
    }
    //解除映射
    //int shmdt(const void *shmaddr);
    //  shmaddr:   映射首地址
    shmdt(shm_start);
    //删除
    //int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    //  shmid:  操作句柄
    //  cmd:   操作类型
    //      IPC_RMID    删除共享内存
    //  buf:   设置/获取共享内存信息
    //
    shmctl(shmid, IPC_RMID, NULL);
    return 0;
}

消息队列   内核中的一个优先级队列

写端:

​
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
 
struct my_msg_st
{
    long int my_msg_type;
	char some_text[MAX_TEXT];
};
 
int main(void)
{
    //int msgget(key_t key, int msgflg)
    //返回消息队列操作句柄
    //include <sys/msg.h>
    //添加消息
    //int msgsnd ( int msqid, const void *prt, size_t nbytes, int flags);
    //        msqid 消息队列操作句柄
    //        prt  指向下一个my_msg_st的结构
    //        nbytes 消息的长度
    //        flags 函数的行为
    //接受消息        
    //int msgrcv(int msgid,void* ptr,size_t nbytes,long type,int flags);
    //     msgid:  指定要读的队列
    //     ptr:    要接受数据的缓冲区
    //     nbytes: 要接受数据的长度
    //     flags : 指明了函数的行为
    //     type:
    //         0:     返回队列最上面的消息(根据先进先出原则)
    //         大于0:返回的消息类型与type相等的第一条消息
    //         小于0:返回消息类型小于等于绝对值的最小值得第一条消息

    int running=1;
	struct my_msg_st some_data;
	int msgid;
	char buffer[BUFSIZ];
 
	/*创建消息队列*/
	msgid=msgget((key_t)1234,0666 | IPC_CREAT);
	if(msgid==-1)
	{
	    fprintf(stderr,"msgget failed with error:%d\n",errno);
		exit(EXIT_FAILURE);
    }
 
	/*循环向消息队列中添加消息*/
	while(running)
	{
	    printf("Enter some text:");
		fgets(buffer,BUFSIZ,stdin);
		some_data.my_msg_type=1;
		strcpy(some_data.some_text,buffer);
 
		/*添加消息*/
		if(msgsnd(msgid,(void *)&some_data,MAX_TEXT,0)==-1)
		{
		    fprintf(stderr,"msgsed failed\n");
			exit(EXIT_FAILURE);
		}
 
		/*用户输入的为“end”时结束循环*/
		if(strncmp(buffer,"end",3)==0)
		{
		    running=0;
		}
	}
	exit(EXIT_SUCCESS);
}

​

读端:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
 
struct my_msg_st
{
    long int my_msg_type;
	char some_text[BUFSIZ];
};
 
int main(void)
{
    //int msgget(key_t key, int msgflg)
    //返回消息队列操作句柄
    //include <sys/msg.h>
    //添加消息
    //int msgsnd ( int msqid, const void *prt, size_t nbytes, int flags);
    //        msqid 消息队列操作句柄
    //        prt  指向下一个my_msg_st的结构
    //        nbytes 消息的长度
    //        flags 函数的行为
    //接受消息        
    //int msgrcv(int msgid,void* ptr,size_t nbytes,long type,int flags);
    //     msgid:  指定要读的队列
    //     ptr:    要接受数据的缓冲区
    //     nbytes: 要接受数据的长度
    //     flags : 指明了函数的行为
    //     type:
    //         0:     返回队列最上面的消息(根据先进先出原则)
    //         大于0:返回的消息类型与type相等的第一条消息
    //         小于0:返回消息类型小于等于绝对值的最小值得第一条消息
    int running=1;
	int msgid;
	struct my_msg_st some_data;
	long int msg_to_receive=0;
 
	/*创建消息队列*/
	msgid=msgget((key_t)1234,0666 | IPC_CREAT);
	if(msgid==-1)
	{
	    fprintf(stderr,"msgget failed with error: %d\n",errno);
		exit(EXIT_FAILURE);
	}
	
	/*循环从消息队列中接收消息*/
	while(running)
	{
		/*读取消息*/
	    if(msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_receive,0)==-1)
		{
		    fprintf(stderr,"msgrcv failed with error: %d\n",errno);
			exit(EXIT_FAILURE);
		}
 
		printf("You wrote: %s",some_data.some_text);
 
		/*接收到的消息为“end”时结束循环*/
		if(strncmp(some_data.some_text,"end",3)==0)
		{
		    running=0;
		}
	}
 
	/*从系统内核中移走消息队列*/
	if(msgctl(msgid,IPC_RMID,0)==-1)
	{
	    fprintf(stderr,"msgctl(IPC_RMID) failed\n");
		exit(EXIT_FAILURE);
	}
	exit(EXIT_SUCCESS);
}
 

信号量:内核当中的计数器(数据资源计数器)

信号量(Saphore)由一个值和一个指针组成,指针指向等待该信号量的进程。信号量的值表示相应资源的使用情况。信号量S>=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个资源,因此S的值减1;当S<0时,表示已经没有可用资源,S的绝对值表示当前等待该资源的进程数。请求者必须等待其他进程释放该类资源,才能继续运行。而执行一个V操作意味着释放一个资源,因此S的值加1;若S<0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行。
 

获取数据时:先判断计数器是否>0

                    若计数器>0 :表示有资源,才可以获取数据,并且资源数-1

                    若计数器<=0:表示没有资源,则阻塞等待

产生数据时:计数+1,并且唤醒等待的进程

ipcs:提供所有ipc信息

     -q  查看消息队列信息

     -m 查看共享内存信息

     -s 查看系统信号量信息

ipcrm:

ipcrm -M shmkey

    移除用shmkey创建的共享内存段

  ipcrm -m shmid

    移除用shmid标识的共享内存段

  ipcrm -S semkey

    移除用semkey创建的信号量

  ipcrm -s semid

    移除用semid标识的信号量

  ipcrm -Q msgkey

    移除用msgkey创建的消息队列

  ipcrm -q msgid

    移除用msgid标识的消息队列

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值