进程间通信

目录

一,总体概况图

二,方法详述

1.有名管道

2.无名管道    

3.共享内存 

4,信号量

4.1,posix_sem

4.2,sistem_v_sem

5,信号

 5.1 发送信号

5.2,改变信号的处理方式


一,总体概况图

二,方法详述

1.有名管道

        mkfifo用来在文件系统中创建一个有名管道(fifo),但是这个文件只存在于内存中,
    只不过在文件系统中有一个路径而已。
       int mkfifo(const char *pathname, mode_t mode);
            @pathname: 要创建的FIFO文件(带路径)
            @mode: 创建的FIFO文件的权限,如 0664
        返回值: 成功返回0,失败返回-1,errno被设置

2.无名管道    

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

int main()
{
	int pipefd[2];
	int r = pipe(pipefd);
	if(r == -1)
	{
		perror("pipe failed");
		return -1;
	}
	
	pid_t pid = fork();
	if(pid > 0)//是父进程返回
	{
		while(1)
		{
			char str[32];
			read(pipefd[0],str,32);
			printf("read buf: %s\n",str);
			if(strcmp(str,"over")==0)
			{
				kill(pid,9);
				exit(0);
			}
		}
	}
	else //是子进程返回
	{
		while(1)
		{
			char str[32];
			gets(str);
			write(pipefd[1],str,strlen(str)+1);
		}
	}

}	

3.共享内存 

        

         1, 申请一个 system_v_ipc 的key

                key_t ftok(const char *pathname, int proj_id);  

         2,创建或打开一个 system v 共享内存区

                int shmget(key_t key, size_t size, int shmflg);

         3,shmat 用来把共享内存区映射到进程的地址空间

                 void *shmat(int shmid, const void *shmaddr, int shmflg);

         4,shmdt用来把映射的共享内存区解映射

                 int shmdt(const void *shmaddr);

         5, shmctl 对共享内存区进行控制操作

                  int shmctl(int shmid, int cmd, struct shmid_ds *buf);

                @shmid: 共享内存区的ID,表示要操作哪个共享内存区
                @cmd: 命令号,常用的有三个
                        IPC_RMID 用来删除该共享内存,此时第三个参数buf填为NULL
                        IPC_STAT 用来获取共享内存的状态,通过buf指向用来保存该内存区状态信息的
                                          结构体变量。
                        IPC_SET  用来设置共享内存区的状态信息,通过buf指向用来保存该共享内存区
                                         状态信息的结构体变量。

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

#define KEY_PATH  "/home/china"
#define PRO_ID    9527

int main()
{
	/*step1: 申请一个 system_v_ipc 的key*/
	key_t key = ftok(KEY_PATH, PRO_ID);
	if(key == -1)
	{
		perror("ftok failed");
		return -1;
	}

	/*step2: 创建一个 system v 共享内存区*/
	int shmid = shmget(key, 4096, IPC_CREAT | 0664);
	if(shmid == -1)
	{
		perror("shmget failed");
		return -1;
	}

	/*step3: 映射*/
	char *shm_p = shmat(shmid, NULL, 0);
	if(shm_p == NULL)
	{
		perror("shmat failed");
		return -1;
	}

	while(1)
	{
		char str[64];
		gets(str);
		strcpy(shm_p,str);
		
		if(strcmp(str,"quit") == 0)
			break;
	}
	/*step4: 解映射*/
	shmdt(shm_p);

	/*step5: 销毁共享内存*/
	 shmctl(shmid, IPC_RMID, NULL);
}

4,信号量

 

4.1,posix_sem

POSIX概念:
    POSIX是可移植性操作系统接口,即用POSIX标准写的代码,在POSIX操作系统上都能编译执行。
    POSIX不局限于UNIX/Linux,比如DEC,Open VMS等。
    
Posix Semaphore 又分为有名信号量和无名信号量
    有名信号量:在文件系统中有一个文件名,但不存在文件系统中,而是存在与内存中
    无名信号量: 没有文件名,只适用于线程间或者由亲缘关系的进程间使用
    
                         有名信号量                              无名信号量 
创建/初始化      sem_open()                               sem_init()
P操作                            sem_wait()/sem_trywait()
V操作                            sem_post()
获取信号量的值            sem_getvalue()
销毁信号量      sem_unlink()                              sem_destroy()

#include <stdio.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

#define SEM_NAME  "/test.sem"

int main()
{
	int sval;
	/*step1: 创建一个信号量*/
	 sem_t *sem = sem_open(SEM_NAME, O_CREAT, 0664, 1);
	 if(sem == SEM_FAILED)
	 {
		perror("sem_open failed");
	 }
	/*step2: 操作信号量*/

	sem_wait(sem);//P操作
	sem_getvalue(sem, &sval);
	printf("i get the sem, the sem value is %d\n",sval);
	getchar();

	
	sem_post(sem);//V操作
	sem_getvalue(sem, &sval);
	printf("i release the sem!,the sem value is %d\n",sval);

	/*step3: 销毁信号量*/
	sem_unlink(SEM_NAME);
}

4.2,sistem_v_sem

        1.申请key

                key_t ftok(const char *pathname, int proj_id);

        2.调用semget来创建或打开一个system_v 的信号量

                int semget(key_t key, int nsems, int semflg);

                 @nsems: 你要创建的信号量集中信号量的个数 
                如果我们不创建一个新的信号量集,而是访问一个已经存在的信号量集,此处参数应该                  指定为0,一旦创建完一个信号量集后,我们就不能改变其中的信号量的个数了。

                @semflg:标志位
                1) 创建  PC_CREAT | 权限位,如 IPC_CREAT | 0664
                2) 打开 0

        3,调用semctl 给信号量赋初值

                int semctl(int semid, int semnum, int cmd, ...);

                @cmd: 操作命令号,常用的有
                GETVAL: 获取某一个信号量的值
                GETALL: 获取该信号量集的所有信号量的值
                SETVAL: 设置某一个信号量的值
                SETALL: 设置该信号量集中所有信号量的值
                IPC_RMID: 删除该信号量集
                IPC_SET: 设置该信号量集的状态信息
                IPC_STAT: 获取该信号量集的状态信息 
            @...arg: 可选的参数,根据cmd是什么情况,它的值是不一样的
                if cmd == GETVAL,arg则不需要,semctl的返回值就是该信号量的值
                if cmd == GETALL,arg应该为一个short数组的首地址,用来保存所有信号量的值
                if cmd == SETVAL,arg应该为一个short的整数,用来指定该信号量的值
                if cmd == SETALL,arg应该为一个short数组的首地址,用来保存所有信号量的值

        4,调用semop 对信号量进行pv操作

                    int semop(int semid, struct sembuf *sops, size_t nsops);
                    @semid: 你要操作的信号量集的ID
                    @sops: struct sembuf的数组的首地址,因为你可以同时对多个信号量进行操作
                    @nsops: 第二个参数 struct sembuf 数组的元素个数   

        struct sembuf
        {
            unsigned short sem_num; //表示你对哪个信号量进行操作(0,1,2,3...nsems-1)
            short          sem_op;  //表示你对该信号量进行何种操作。
                                    //无论是P操作还是V操作,实质就是对信号量的值进行操作
                            /*
                                sem_op < 0 ,表示P操作
                                sem_op > 0 , 表示V操作
                                sem_op ==0 , 表示不操作
                                最后,semval的值等于==> 原semval + sem_op 
                            */
            short          sem_flg;  //操作标志位
                            /*
                                0: 一般情况。 P操作时如果 semval<=0,则wait
                                IPC_NOWAIT: 非阻塞(不等待)。P操作时如果 semval<=0,则不等待,直接返回-1
                                SEM_UNDO: 撤销。在进程退出时,操作系统会还原该进程对该信号量所做的操作。为了防止进程带锁死亡而造成死锁。
                            */

        };

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

#define  PRO_PATH  "/home/china"
#define  PRO_ID    1234
int main()
{
	int r;
	/*step1: 用 ftok向内核申请一个system v ipc 对象的key */
	key_t key = ftok(PRO_PATH, PRO_ID);
	if(key == -1)
	{
		perror("ftok failed");
		return -1;
	}
	/*step2: 用 semget 来创建或打开一个 system v 信号量集 对象*/
	int semid = semget(key, 2, IPC_CREAT | 0664);
	if(semid == -1)
	{
		perror("semget failed");
		return -1;
	}
	/*step3: 用semctl给信号量赋初值*/
	r = semctl(semid, 0, SETVAL, 1);
	r = semctl(semid, 1, SETVAL, 1);
	if(r == -1)
	{
		perror("semctl failed");
		return -1;
	}
	/*step4: 用 semop 对 system v 信号量进行操作(主要是 P/V 操作)*/
	struct sembuf sops[2];
	sops[0].sem_num = 0;
	sops[0].sem_op = -1;//P操作
	sops[0].sem_flg=0;//阻塞等待
	sops[1].sem_num = 1;
	sops[1].sem_op = -1;//P操作
	sops[1].sem_flg=0;//阻塞等待
	
	r = semop(semid, sops, 2);
	printf("i have the sem0 and sem1\n");

	getchar();
	sops[0].sem_num = 0;
	sops[0].sem_op = 1;//V操作
	sops[0].sem_flg=0;//阻塞等待
	sops[1].sem_num = 1;
	sops[1].sem_op = 1;//V操作
	sops[1].sem_flg=0;//阻塞等待
	r = semop(semid, sops, 2);
	printf("i release the sem0 and sem1\n");

	/*step4: 销毁信号量*/
	r = semctl(semid, 0, IPC_RMID);
}

5,信号

 5.1 发送信号

         int kill(pid_t pid, int sig);
            @pid: 指定信号的接收者(可能是多个进程)
                pid > 0: 接收者的进程ID
                pid==0: 发送信号给调用进程同组的所有进程
                pid==-1: 发送信号给调用进程有权限发送的所有进程
                pid<-1: 发送信号给组ID等于pid的绝对值的所有进程
            @sig: 要发送的信号值

        alarm 用来给调用进程设置一个闹钟,闹钟在超时的时候,就会闹(发送一个SIGALRM信号
                    给调用进程),一个进程在同一时刻只能有一个闹钟。
              unsigned int alarm(unsigned int seconds);
                    @seconds: 设置闹钟的时间,以秒为单位

        pause用来等待一个信号的到来,导致调用进程(或线程)休眠(阻塞在那里),直到收到一个信号
    (被干掉/执行自己的信号处理函数)。
       int pause(void);

5.2,改变信号的处理方式

       sighandler_t signal(int signum, sighandler_t handler);

            @signum: 要改变处理方式的信号的值
            @handler: 指向信号处理函数的指针。有三种情况:
                    1) SIG_IGN: ignore 忽略
                    2) SIG_DFL: default采用操作系统默认的处理方式
                    3) 你自己写的信号处理函数指针       
        返回值:
            如果成功返回该信号的前任处理方式
            如果失败返回 SIG_ERR
    注意:
        信号处理方式,同样可以被fork克隆

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

int terminal_flag=0;


void signal_handler(int sigval)
{
	switch(sigval)
	{
		case SIGINT:
			terminal_flag=1;break;
		case SIGALRM:
			puts("haha");
			alarm(5);
			break;
		
	}
}

int main()
{
	int a=2,b=3;
	signal(SIGINT, signal_handler);
	signal(SIGALRM, signal_handler);

	alarm(5);
	
	
	while(1);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

amireux512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值