LINUX进程间通信

目录

目录

1.无名管道:

命名管道 FIFO:

消息队列:

共享内存:

 信号:

信号量:



1.无名管道:

无名管道是半双工的 (数据单向流动),具有固定的读数据端和写数据端。

管道中不存储数据,数据读走后就没了。

无名管道只能用于具有亲缘关系的进程间通信(如父子进程 或者 兄弟进程)。

 可以看成是特殊文件,但不属于任何文件系统,只存在与内存当中,它可以用read,write 来进行读写。

原型:

#include <unistd.h>

int pipe(int pipefd[2]);   //成功返回0 失败返回-1

当建立无名管道时,它会创建两个文件描述符:fd[0] 为读 ,fd[1]为写

当要关闭时只需要把fd给关闭就行 : close(fd[0]) / close(fd[1])

例子:

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

int main()
{
        int fd[2];
        char buf[128]={0};
        if(pipe(fd)==-1){
                printf("creat pipe failed\n");
        }

        int pid=fork();                                            //开辟进程

        if(pid<0){
                printf("creat child fauled\n");
        }
        else if(pid >0){
                sleep(3);
                printf("this is father \n");
                close(fd[0]);
                write(fd[1],"from father",strlen("from father"));    //写时要把读给关掉
                wait();
        }
        else{
                printf("this is child\n");
                close(fd[1]);
                read(fd[0],buf,128);                                //读时要把写给关掉
                printf("read: %s\n",buf);
                exit(0);
        }

        return 0;
}

命名管道 FIFO:

FIFO可以在无关的进程间通信。

FIFO有路径名与之相关联,以一种特殊的设备文件存在与文件系统中(即可用一般的文件I/O函数来操作它)。

原型:

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

int mkfifo(const char *pathname, mode_t mode);   //成功返回0 失败返回-1
//                      文件名  ,什么类型的文件     

例子:

读数据端:

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include<errno.h>

int main()
{
        char buf[30];
        if(mkfifo("./fiel",0600)==-1 && errno!=EEXIST){ //忽略掉EEXIST这个错误原因:文件已存在
                printf("mkfifo ERROR\n");
                perror("why");                           //把错误原因显示出来
        }
        int fd=open("./fiel",O_RDONLY);
        int n=read(fd,buf,30);
        printf("byte: %d  %s\n",n,buf);

        close(fd);


        return 0;
}

写数据端:

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

int main()
{

        int fd=open("./fiel",O_WRONLY);
        write(fd,"form to write",strlen("form to write"));
        printf("write ok\n");

        close(fd);


        return 0;
}

消息队列:

消息队列的本质就是链表,存放在内核中,且每个队列都有一个ID号来进行标识。

进程终止时消息队列及其内容并不会被删除。

消息队列可实现信息的随机查询,不一定要以先进先出的方式读取。

如何运用消息队列进行通讯:

                进程A:

                                1.获取队列/创建队列

                                2.把消息放入队列

                                3.移走队列

                进程B:

                                1.获取队列/创建队列

                                2.读取队列信息

                                3.移走队列

原型:

// 获取/创建队列
int msgget(key_t key, int msgflg);
// 写入队列
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// 读出队列
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
// 移除队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

例子:

进程A:

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>


struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};

int main()
{
         key_t key;
        key=ftok(".",1);
        printf("key=%x\n",key);

        struct msgbuf writebuf={88,"form to write"};

        int msgid=msgget(key,IPC_CREAT|0777);            //  获取/创建队列
        if(msgid == -1){
                printf("mmsgget error\n");
        }

        msgsnd(msgid,&writebuf,strlen(writebuf.mtext),0);        //把消息放到队列中
        printf("send finish\n");

        msgctl(msgid,IPC_RMID,NULL);                        //移走队列


        return 0;
}

进程B:

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>



struct msgbuf
{
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};

int main()
{
        key_t key;
        key=ftok(".",1);
        struct msgbuf readbuf;
        printf("key=%x\n",key);
        int msgid=msgget(key,IPC_CREAT|0777);     //获取/创建队列
        if(msgid == -1){
                printf("mmsgget error\n");
        }

        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),88,0);          //读取队列消息
        printf("read:%s\n",readbuf.mtext);

         msgctl(msgid,IPC_RMID,NULL);                        //移走队列


        return 0;
}

共享内存:

共享内存就是允许两个或多个进程共享一定的存储区。

如何运用共享内存进行通讯:

                        1.创建/打开共享内存

                        2.把共享内存映射到进程上

                        3.数据交换

                        4.断开共享内存连接

                        5.删除内存空间   

原型:

// 创建/打开 共享内存
int shmget(key_t key, size_t size, int shmflg);
// 映射共享内存到进程
void *shmat(int shmid, const void *shmaddr, int shmflg);
//断开共享内存连接
int shmdt(const void *shmaddr);
//删除内存空间
int shmctl(int shmid, int cmd, struct shmid_ds *buf);


例子:

进程A:

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


int main()
{
        int shmid;
        char *shmaddr;
        key_t key;
        key=ftok(".",1);

        shmid=shmget(key,1024,IPC_CREAT|0666);     // 创建/打开共享内存
        if(shmid==-1){
                printf("shmget failed\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);                    //连接共享内存
        printf("shmat ok\n");
        strcpy(shmaddr,"from to write\n");        //写入数据
  
        shmdt(shmaddr);                        //断开共享内存连接
      


        return 0;
}

进程B:

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


int main()
{
        int shmid;
        char *shmaddr;
        key_t key;
        key=ftok(".",1);

        shmid=shmget(key,1024,0);                    //打开共享内存
        if(shmid==-1){
                printf("shmget failed\n");
                exit(-1);
        }

        shmaddr=shmat(shmid,0,0);                    //连接共享内存
        printf("shmat ok\n");
        printf("read:%s",shmaddr);                  //读取消息

        shmdt(shmaddr);                            //断开共享内存连接

        shmctl(shmid,IPC_RMID,0);             // 删除内存空间



        return 0;
}

查看共享内存:  ipcs -m

删除共享内存:ipcrm -m  +   shmid

 信号:

信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。

信号名称可以使用kill -l来查看信号的名字以及序号:

 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

信号的三种处理方式:忽略,捕捉,系统默认

信号处理函数分为:入门版 和高级版

入门版:

        signal( );            

        kill( );

例子:

进程A:

#include<stdio.h>

void handler(int signum)
{
        printf("get signum:%d\n",signum);
        switch(signum){

                case 2:
                        printf("SIGINT\n");
                        break;

                case 9:
                        printf("SIGKILL\n");
                        break;

                case 10:
                        printf("SIGUSR1\n");
                        break;

        }

}

int main()
{
        signal(SIGINT,handler);
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);
        while(1);

        return 0;
}

我们可以通过signal来捕捉信号 并且修改信号的默认动作来让信号执行我想要执行的函数。如ctrl+c的信号是SIGINT 它的默认动作是终止进程。我们通过signal 来捕捉到SIGINT 并修改它之后在按ctrl+c时是不会中止进程的如下:

 SIGINT的默认动作已经被忽略掉(系统大部分信号都可以被忽略掉,除了SIGKILL和SIGSTOP),此时我们需要通过SIGKILL这个信号来杀死进程:

 高级版:

                sigaction( );

                sigqueue( );

高级版的函数可以让信号携带消息,那么信号又是怎么携带信息的:

通过sigqueue把信号以及携带的信息发送给sigaction,sigaction收到信号后把信号以及携带的内容读取出来。

读数据端:

#include <signal.h>
#include<stdio.h>

void handler(int signum,siginfo_t *info,void *context)
{
        printf("get signum %d\n",signum);
        if(context!=NULL){
                printf("get date=%d\n",info->si_int);
                printf("get date=%d\n",info->si_value.sival_int);
        }

}
int main()
{
        struct sigaction act;

        act.sa_sigaction=handler;
        act. sa_flags=SA_SIGINFO;

        sigaction(SIGUSR1,&act,NULL);
        while(1);

        return 0;
}

发数据端

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


int main(int argc,char** argv)
{
        int signum=atoi(argv[1]);
        int pid=atoi(argv[2]);

        union sigval value;
        value.sival_int=100;


        sigqueue(pid,signum,value);
        printf("pid:%d",getpid());
        return 0;
}

信号量:

信号量相当于一把锁,P操作相当于拿钥匙去开锁,V操作相当于把钥匙放到原来的位置。

原型:

// 获取信号量 (造锁)
int semget(key_t key, int nsems, int semflg);
// 对信号量进行操作(拿钥匙开锁/关锁)
int semop(int semid, struct sembuf *sops, unsigned nsops);
// 初始化/删除信号量
int semctl(int semid, int semnum, int cmd, ...);

例子1:

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.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) */
};

void pGetKey(int semid)                           
{
        struct sembuf set;
        set.sem_num = 0;        /* Operate on semaphore 0 */
        set.sem_op = -1;         /* Wait for value to equal 0 */
        set.sem_flg = SEM_UNDO;

        semop(semid,&set,1);
}

void vPutKey(int semid)                            
{
        struct sembuf set;
        set.sem_num = 0;        /* Operate on semaphore 0 */
        set.sem_op = 1;         /* Wait for value to equal 0 */
        set.sem_flg = SEM_UNDO;

        semop(semid,&set,1);
}

int main()
{
        key_t key;
        int semid;
        key=ftok(".",1);
 
        // 获取/创建信号量(相当于创建了一把锁)
        semid=semget(key,1,IPC_CREAT|0666);      

        //初始化信号量
        union semun valu;
        valu.val=0;                        //刚开始没有钥匙
        semctl(semid,0,SETVAL,valu);

        int pid=fork();                    //创建子进程
        if(pid>0){
                pGetKey(semid);                //拿钥匙开锁
                printf("this is father\n");
                vPutKey(semid);                //关锁放钥匙
        }
        else if(pid==0){
                printf("this is child\n");
                vPutKey(semid);                //放钥匙
        }
        else{
                printf("fork error\n");
        }

        return 0;
}

例子2:

读数据端:

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

struct msgbuf 
{
    long mtype;       /* message type, must be > 0 */
    char mtext[10];    /* message data */
};

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) */
};

//P操作
void Phandle(int semid)
{
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op  = -1;
	op.sem_flg = SEM_UNDO;
	semop(semid,&op,1);
}

//V操作
void Vhandle(int semid)
{
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op  = 1;
	op.sem_flg = SEM_UNDO;
	semop(semid,&op,1);
}

int main(int argc, char const *argv[])
{
	key_t key;
	int shmid, msgid, semid;
	char* addr;
	key = ftok(".",1);

	//创建/打开共享内存
	shmid = shmget(key,1024,IPC_CREAT|0666);
	if(shmid == -1){
		printf("shmget error\n");
		exit(-1);
	}
	addr = shmat(shmid,0,0);	//连接共享内存

	//获取/创建消息队列
	msgid = msgget(key,IPC_CREAT|0666);
	if(shmid == -1){
		printf("msgget error\n");
		exit(-1);
	}

	//打开/创建建号量
	semid = semget(key,1,IPC_CREAT|0666);
	if(semid == -1){
		printf("semget error\n");
		exit(-1);
	}

	//初始化信号量
	union semun set;
	set.val = 1;
	semctl(semid,0,SETVAL,set);

	struct msgbuf buf = {88,"\0"};		//初始化buf

	while(1){

		msgrcv(msgid,&buf,sizeof(buf.mtext),88,0);	//读取队列数据

		if(strstr(buf.mtext,"r") != NULL){

			Phandle(semid);                //访问资源
			printf("read: %s\n",addr);	   //读取共享内存数据
			Vhandle(semid);                //释放资源

		}else if(strstr(buf.mtext,"q") != NULL){

			shmdt(addr);				//断开共享内存连接
			shmctl(shmid,IPC_RMID,0);	//删除内存空间
			msgctl(msgid,IPC_RMID,0);	//删除消息队列
			semctl(semid,IPC_RMID,0);	//删除信号量
			return 0;
		}
	}
	
}

写数据端:

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

struct msgbuf 
{
    long mtype;       /* message type, must be > 0 */
    char mtext[10];    /* message data */
};


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) */
};

//P操作
void Phandle(int semid)
{
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op  = -1;
	op.sem_flg = SEM_UNDO;
	semop(semid,&op,1);
}

//V操作
void Vhandle(int semid)
{
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op  = 1;
	op.sem_flg = SEM_UNDO;
	semop(semid,&op,1);
}

int main(int argc, char const *argv[])
{
	key_t key;
	int N = 0;
	int shmid, msgid, semid;
	char* addr;
	char ret[10];
	struct msgbuf buf;

	key = ftok(".",1);

	//创建/打开共享内存
	shmid = shmget(key,1024,IPC_CREAT|0666);
	if(shmid == -1){
		printf("shmget error\n");
		exit(-1);
	}
	addr = shmat(shmid,0,0);	//连接共享内存

	//获取/创建消息队列
	msgid = msgget(key,IPC_CREAT|0666);
	if(shmid == -1){
		printf("msgget error\n");
		exit(-1);
	}

	//打开/创建建号量
	semid = semget(key,1,IPC_CREAT|0666);
	if(semid == -1){
		printf("semget error\n");
		exit(-1);
	}

	//初始化信号量
	union semun set;
	set.val = 1;
	semctl(semid,0,SETVAL,set);		


	while(1){

		puts("输入send/quit");
		scanf("%s",ret);
		buf.mtype = 88;

		if(strstr(ret,"send") != NULL){

			Phandle(semid);								//访问资源
			strcpy(buf.mtext,"r");						
			msgsnd(msgid,&buf,strlen(buf.mtext),0);		//发送命令给Server
			printf("snd:%s\n",buf.mtext);
			puts("请写入数据");	
			scanf("%s",addr);							//写入数据
			getchar();									//吸收\n
			Vhandle(semid);								//释放资源

		}else if(strstr(ret,"quit") != NULL){

			strcpy(buf.mtext,"q");
			msgsnd(msgid,&buf,strlen(buf.mtext),0);		//发送命令给Server
			printf("snd:%s\n",buf.mtext);
			shmdt(addr);							//断开共享内存连接
			break;

		}else{
			printf("重新开始");
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值