Linux进程间通信

目录

  • 1、通信技术IPC(InterProcess Communication)
  • 2、无名管道
  • 3、命名管道(FIFO)
  • 4、消息队列
    • 4.1.
    • 4.2.API
    • 4.3.ftok函数(通常用于消息队列)
    • 4.4例:
  • 5、共享内存
  • 6、信号
  • 7、信号量(锁)

1、通信技术IPC(InterProcess Communication)

1.IPC方式有:
*管道(包括无名管道和命名管道)
*消息队列
*信号量
*共享存储
*Socket
*Streams
Socket和Streams支持多机通信

2、无名管道

1.半双工,只在父子进程之间,管道可以看成特殊文件用read write读写只存在于内存不生成文件。
2.原型

int pipe(int fd[2])

返回值:成功0,失败-1
管道建立成功生成2个文件描述符,fd[0]读,fd[1]写
3.用法举例
建立管道:pipe(fd),判断是否成功
创建子进程:fork,判断是否成功
在父子进程读写:父进程写,子进程读
父:close(fd[0]),write(fd[1],内容,个数)
子:close(fd[1]),read(fd[0],,)
注:写时要关闭读管道,读时要关闭写管道
读写方向一但固定就无法改变。

#include 
#include 
#include 
#include 
int main ()
{
     
        pid_t pid;
        int fd[2];
        char a[10] = {
     0};
        if(pipe(fd) == -1)
        {
     
                perror("pipe");
                exit(-1);
        }
        pid = fork();
        if(pid < 0)
        {
     
                perror("fork");
                exit(-1);
        }
        else if(pid == 0)
        {
     
                close(fd[1]);
                read(fd[0],a,strlen("xiao wei"));
                printf("%s\n",a);
        }
        else
        {
     
                close(fd[0]);
                write(fd[1],"xiao wei",strlen("xiao wei"));
        }
        return 0;
}

3、命名管道(FIFO)

1.半双工,可以无关进程之间,以特殊文件形式存在文件系统
2.原型

int mkfifo(const char* pathname,mode_t mode)

返回值:成功0 ,失败-1,可以用perror查看失败原因
参数:和open一样,文件路径名,0600(读写)
参数1:可设置是否阻塞O_NONBLOCK,默认是阻塞。
3.例:
*进程1
open打开管道
read阻塞等待write(要用while)
输出读的数据
close关闭管道
*进程2
open打开管道
write写数据
close关闭管道
注:管道文件数据读后立马清空,先进先出

/*读代码*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main ()
{
     
        int cnt = 3;
        int fd;
        char a[14] = {
     0};
        if(mkfifo("./fifo",0600) == -1 && errno != EEXIST)
        {
     
                perror("pipe");
                exit(-1);
        }
        fd = open("./fifo",O_RDONLY);
        if(fd == -1)
        {
     
                perror("open");
        }
        while(cnt)
        {
     
                read(fd,a,strlen(a));
                printf("read:%s\n",a);
                cnt--;
        }
        close(fd);
        return 0;
}
/*写代码*/
int main ()
{
     
        int cnt = 3;
        int fd;
        char *a = "xiao wei ...";
        fd = open("./fifo",O_WRONLY);
        if(fd == -1)
        {
     
                perror("open");
                exit(-1);
        }
        while(cnt)
        {
     
                write(fd,a,strlen(a));
                sleep(3);
                cnt--;
        }
        close(fd);
        return 0;
}
/*运行结果*/
/*
读进程那边输出:
read:xiao wei ...
read:xiao wei ...
read:xiao wei ...
写进程没3秒发数据一次,发3次后退出程序
*/

4、消息队列

4.1.

全双工,链表在内核中有标识符(队列ID),随机查询按类型读取,进程终止内容不会删除

4.2.API

int msgget(key_t key,int flag)

创建消息队列,成功返回0,失败-1
参数1:key可以用ftok函数,也可以直接写0x1234
参数2:创建队列IPC_CREAT,打开方式可读可写可执行0777

int msgsnd(int msqid,const void*ptr,size_t size,int flag)

添加消息,成功0失败-1
参数1:队列号key
参数2:结构体指针

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

参数3:个数用strlen算
参数4:0阻塞

int msgrcv(int msqid,void*ptr,size_t size,long type,int flag)

读取消息,成功返回数据长度
参数1:队列号
参数2:结构体指针
参数3:个数
参数4:消息类型,结构体的mtype
参数5:0阻塞等待

int msgctl(int msqid,int cmd,struct msqid_ds*buf)

控制消息队列,释放移除队列
参数1:队列号
参数2:常用IPC_RMID,释放移除队列
参数3:通常NULL

4.3.ftok函数(通常用于消息队列)

key_t ftok(const char*fname,int id)

返回值:key,队列号
参数1:当前目录
参数2:id
例:key=ftok(".",‘a’)

4.4例:

创建结构体
创建队列
接受消息,发送消息
移除队列

/*读代码*/
#include 
#include 
#include 
#include 
#include 
#include 
struct msgbuf {
     
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
     
        struct msgbuf readbuf;
        memset(&readbuf,0,sizeof(struct msgbuf));
        key_t key;
        key = ftok(".",'z');
        int msgid = msgget(key,IPC_CREAT|0777);
        if(msgid == -1)
        {
     
                printf("get que failuer\n");
        }
        msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),666,0);
        printf("readbuf:%s\n",readbuf.mtext);
        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}
/*写代码*/
struct msgbuf {
     
        long mtype;       /* message type, must be > 0 */
        char mtext[128];    /* message data */
};
int main()
{
     
        struct msgbuf writebuf = {
     666,"xiao wei..."};
        key_t key;
        key = ftok(".",'z');
        int msgid = msgget(key,IPC_CREAT|0777);
        if(msgid == -1)
        {
     
                printf("get que failuer\n");
        }
        msgsnd(msgid,&writebuf,strlen(writebuf.mtext),0);
        msgctl(msgid,IPC_RMID,NULL);
        return 0;
}
/*运行结果*/
/*
先运行读
在运行写
读端输出
readbuf:xiao wei...
后结束程序
*/

5、共享内存

1.共用一个内存,写读用指针指向这个内存
一读一写,前面内容销毁
2.API

int shmget(key_t key,size_t size,int flag)

创建打开一个共享内存
返回值:成功返回id,失败返回-1
参数1:key,用ftok
参数2:创建内存大小,必须以m为单位
参数3:创建共享内存IPC_CREAT,可读写0666
例:shmip=shmget(key,1024*4,IPC_CREAT|0666)

void *shmat(int shm_id,const void* addr,int flag)

连接共享内存地址空间
返回值:成功返回地址,失败-1
参数1:共享内存id
参数2:0
参数3:0
注:这个指针不需要free释放

int shmdt(void* addr)

断开连接
返回值:成功0失败-1
参数:id

int shmctl(int shm_id,int cmd,struct shmid_ds* buf)

控制共享内存,释放
返回值:成功0失败-1
参数1:id
参数2:IPC_RMID
参数3:0
3.例:
创建打开共享内存
连接映射共享内存
读写数据(用printf)
断开连接
释放干掉共享内存

//写
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
     
        int shm_id;
        char *shmatadd;
        key_t key;
        key = ftok(".",'a');
        shm_id = shmget(key,1024*4,IPC_CREAT|0666);
        if(shm_id == -1){
     
                printf("found fail\n");
                exit(-1);
        }
        shmatadd = shmat(shm_id,0,0);
        printf("shmat ok\n");
        strcpy(shmatadd,"xiaowei");
        sleep(3);

        shmdt(shmatadd);
        shmctl(shm_id,IPC_RMID,0);
        return 0;
}

//读
int main()
{
     
        int shm_id;
        char *shmatadd;
        key_t key;
        key = ftok(".",'a');
        shm_id = shmget(key,1024*4,0);
        if(shm_id == -1){
     
                printf("found fail\n");
                exit(-1);
        }
        shmatadd = shmat(shm_id,0,0);
        printf("shmat ok\n");

        printf("data:%s\n",shmatadd);

        shmdt(shmatadd);
        printf("quit\n");
        return 0;
}

/*判断共享内存内容,是否和上次内容一样
不一样做什么*/
if(strcmp(cmd1,shmatadd)){
     
	strcpy(cmd1,shmatadd);
	printf("cmd=%s\n",cmd1);
}
/*
strcpy(cmd,shmatadd);
if(strcmp(cmd,cmd1)){
	strcpy(cmd1,cmd);
	printf("cmd1= %s\n",cmd1);
}
*/              
if((strcmp(cmd1,"quit") == 0) || (a == 60)){
     
	printf("quit\n");
	break;
}

6、信号

一、一些命令
1.用指令kill -l,查看信号名称和编号
2.忽略,捕捉,系统默认信号
3.使用
例:杀死进程
kill -信号编号 进程pid
二、信号注册,捕捉,忽略(入门)
1.API
函数:

tpyedef void(*sighandler_t)(int)

函数指针

sighandler_t signal(int signum,sighandler_t handler)

参数1:信号名称
参数2:函数指针
高级:sigaction()

2.用signal捕捉信号,然后用函数指针更改系统默认信号,改成自己要操作的内容。
例:
捕捉信号
进入函数指针里,操作要干什么
注:9 SIGKILL,系统信号改变不了

#include 
#include 
#include 

void handler(int signum)
{
     
        printf("get signum:%d\n",signum);
        switch(signum){
     
                case 2:
                        printf("SIGINI\n");
                        break;
                case 9:
                        printf("SIGKILL\n");
                        break;
                case 10:
                        printf("SIGUSR1\n");
                        break;
        }
        printf("never quit\n");
}
int main()
{
     
        signal(SIGINT,handler);
        signal(SIGKILL,handler);
        signal(SIGUSR1,handler);
        while(1);
}
/*
用kill -9 进程pid,杀死进程
用kill-2 pid ,输出SIGINI(还可以用CTRL+c)
用kill -10 pid 输出SIGUSR1
*/

3.用函数main传参杀死进程
*API

int kill(pid_t pid,int sig)

发信号,杀死进程
参数:1.进程pid,2.信号编号加-

atoi()//char转int

参数:二级指针,mian形参
sprintf()
做字符串
参数:比printf前面多一个参数,char是指针型,做出来的字符串放到char*里面
*例:
main完整有形参
把2个字符型形参用atoi转整型(pid,signum)
2种方法
用kill杀死进程
用sprintf做字符串,在用system杀死进程

#include 
#include 
#include 

int main(int argc,char **argv)
{
     
        int signum;
        int pid;
        char cmd[128] = {
     0};
        signum = atoi(argv[1]);
        pid = atol(argv[2]);
        sprintf(cmd,"kill %d %d",signum,pid);
        system(cmd);
        printf("send signal ok\n");
        return 0;
}
//使用:./a -9 2313

4.忽略信号
signal第二个参数写宏,SIG_ICN
注:忽略不了信号9
三、信号携带信息,收发(高级)
1.sigaction(捕捉信号)
参数1:信号编号
★参数2:结构体指针
参数3:备份,NULL
一般用法:都配置
2.struct sigaction
成员1:和signal的函数指针一样
★成员2:多个形参的函数指针
成员3:是否阻塞,默认阻塞
成员4:SA_SIGINFO
成员5:一般不用
一般用法:配成员2,4
3.函数指针handler()
参数1:int signum接受到的信号类型编号
参数2:结构体siginfo_t,读数据:si_int,pid,发消息:一个联合体,可以发整型和字符。
参数3:void*context指针,判断是否有数据传过来,无数据指向空NULL
例:
sigaction函数
配置结构体2个成员
写handler函数
函数里判断有数据打印数据
发送
1.sigqueue
参数1:给谁发pid
参数2:发什么信号
参数3:数据,联合体union sigval(整型,字符)
例:
用完整main,2个参数转int
函数sigqueue
写数据联合体(特别注意段错误,共用一个内存,写字符的时候要用指针)

#include 
#include 
#include 

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

int main()
{
     
        struct sigaction buf;

        buf.sa_sigaction = handler;
        buf.sa_flags = SA_SIGINFO;

        sigaction(SIGUSR1,&buf,NULL);

        while(1);
        return 0;
}
/*输出
get signum 10
get data:10
get data:10
from:48116
*/

#include 
#include 
#include 
#include 
#include 
int main(int argc,char **argv)
{
     
        int signum;
        int pid;
        union sigval value;
        value.sival_int = 10;
        signum = atoi(argv[1]);
        pid = atol(argv[2]);
        sigqueue(pid,signum,value);
        printf("%ddone\n",getpid());
        return 0;
}

7、信号量(锁)

1.控制谁先运行
2.API

int semget(key_t key,int num_sems,int sem_flags)

创建,获取信号组
返回值:semid
参数:key,用flok
参数:信号组个数,一般
参数:IPC_CREAT|0666

int semop(int semid,struct sembuf *sops,size_t numops)

操作信号量,改变值
参数1:id
参数2:结构体指针可以有多个也可以是一个,
成员:sem_num,信号量编号0
成员:sem_op,操作信号量,-1,1
成员:sem_flg,SEM_UNDO
参数3:几个信号量

int semctl(int semid,int sem_num,int cmd,...)

控制信号量组相关信息
参数:id
参数:操作第几个信号量,0开始
参数:很多宏,常用SETVAL设置信号量值
IPC_RMID销毁
参数:联合体,union semun,开头要定义
初始化设置信号量值为1,联合体val=1
例:
创建信号量组
初始化信号量
创建父子进程
写2个函数,一个信号量加,一个减
子进程运行完信号量加
父进程先减然后运行在加
销毁锁

#include 
#include 
#include 
#include 
#include 
#include 
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 id)
{
     
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = -1;
        set.sem_flg = SEM_UNDO;
        semop(id,&set,1);
        printf("getkey\n");
}

void vPutBackKey(int id)
{
     
        struct sembuf set;
        set.sem_num = 0;
        set.sem_op = 1;
        set.sem_flg = SEM_UNDO;
        semop(id,&set,1);
        printf("put back the key\n");
}
int main()
{
     
        key_t key;
        int sem_id;
        key = ftok(".",'a');
        union semun initsem;
        initsem.val = 0;//锁初始没有钥匙

        sem_id = semget(key,1,IPC_CREAT|0666);
        semctl(sem_id,0,SETVAL,initsem);

        int pid = fork();
        if(pid > 0){
     
                pGetKey(sem_id);//父进程那不到,因为没有钥匙
                printf("this is father\n");
                vPutBackKey(sem_id);
        }else if(pid == 0){
     
                printf("this is child\n");
                vPutBackKey(sem_id);//子进程执行完,放钥匙进去
        }else
        {
     
                printf("fork error\n");
                exit(-1);
        }

        semctl(sem_id,0,IPC_RMID);
        return 0;
}

你可能感兴趣的:(笔记,Linux,linux,队列)