进程间的几种通信方式

    科技2024-06-12  69

    详细介绍 无名管道 int pipe(int pipefd[2]); 例:

    #include<sys/types.h> #include<unistd.h> #include<string.h> #include<stdio.h> #include<sys/wait.h> #include<stdlib.h> int main(){ int fd[2]; char buf[128]; pid_t pid; if(pipe(fd) == -1){ printf("创建失败"); } pid=fork(); if(pid<0){ printf("进程创建失败"); }else if(pid>0){ printf("这是父进程"); close(fd[0]); write(fd[1],"lin sui hen shuai",strlen("lin sui hen shuai")); wait(NULL); }else{ printf("这是子进程\n"); close(fd[1]); read(fd[0],buf,128); printf("%s\n",buf); exit(0); } return 0; }

    如上示例:pipe(fd)创建无名管道用于父子进程间的通信,fd[0]为管道读取的缓冲去,fd[1]为写入

    fifo命名管道 mkfifo属于一种文件类型,无关进程间的通信 程序1

    #include <sys/types.h> #include <sys/stat.h> #include<stdio.h> #include<errno.h> #include<string.h> #include <fcntl.h> #include <unistd.h> //int mkfifo(const char *pathname, mode_t mode); int main(){ char buf[268]; if(mkfifo("./file",0600)==-1&& errno!=EEXIST){ printf("创建管道失败!"); perror("why"); } int fd=open("./file",O_RDONLY); printf("输出:\n"); while(1){ read(fd,buf,128); printf("buf:%s",buf); } close(fd); return 0; }

    程序2

    #include <sys/types.h> #include <sys/stat.h> #include<stdio.h> #include<errno.h> #include<string.h> #include <fcntl.h> #include <unistd.h> //int mkfifo(const char *pathname, mode_t mode); int main(){ int i=0; char *sd="林穗"; int fd=open("./file",O_WRONLY); while(1){ write(fd,sd,strlen(sd)); sleep(1); i++; if(i==5){ break; } } close(fd); return 0; }

    程序1执行到只读打开文件的时候就会阻塞,必须有另一个进程程序2只写打开文件的时候,程序才会继续执行下去

    消息队列 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

    1、特点 1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

    2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

    3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

    2、原型

    1 #include <sys/msg.h> 2 // 创建或打开消息队列:成功返回队列ID,失败返回-1 3 int msgget(key_t key, int flag); 4 // 添加消息:成功返回0,失败返回-1 5 int msgsnd(int msqid, const void *ptr, size_t size, int flag); 6 // 读取消息:成功返回消息数据的长度,失败返回-1 7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag); 8 // 控制消息队列:成功返回0,失败返回-1 9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

    示例: 程序1

    #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include<stdio.h> #include<string.h> // 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); struct msgbuf{ long mtype; char next[128]; }; int main(){ key_t key=ftok(".",3); printf("key=%x\n",key); struct msgbuf ret; int msgid=msgget(key,IPC_CREAT|0777); if(msgid==-1){ printf("打开失败!"); } msgrcv(msgid,&ret,sizeof(ret.next),888,0); printf("********************\n"); printf("%s\n",ret.next); struct msgbuf send={889,"我收到了!\n"}; msgsnd(msgid,&send,strlen(send.next),0); printf("发送完毕!\n"); return 0; }

    程序2

    #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include<string.h> #include<stdio.h> // 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); struct msgbuf{ long mtype; char next[128]; }; int main(){ key_t key=ftok(".",3); printf("key=%x\n",key); struct msgbuf rcv; struct msgbuf sen={888,"lin sui hen you xiu!"}; int msgid=msgget(key,IPC_CREAT|0777); if(msgid==-1){ printf("打开失败!"); } msgsnd(msgid,&sen,strlen(sen.next),0); printf("发送完毕!\n"); msgrcv(msgid,&rcv,sizeof(rcv.next),889,0); printf("%s\n",rcv.next); return 0; }

    消息队列相比于管道通信可以双向通信,如上示例 共享内存 共享内存的使得 与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h中。

    1、shmget函数 该函数用来创建共享内存,它的原型为: int shmget(key_t key, size_t size, int shmflg); 第一个参数 ,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.

    不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。

    第二个参数,size以字节为单位指定需要共享的内存容量

    第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。

    2、shmat函数 第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下: void *shmat(int shm_id, const void *shm_addr, int shmflg); 第一个参数,shm_id是由shmget函数返回的共享内存标识。 第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。 第三个参数,shm_flg是一组标志位,通常为0。

    调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.

    3、shmdt函数 该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下: int shmdt(const void *shmaddr); 参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.

    4、shmctl函数 与信号量的semctl函数一样,用来控制共享内存,它的原型如下: int shmctl(int shm_id, int command, struct shmid_ds *buf); 第一个参数 ,shm_id是shmget函数返回的共享内存标识符。

    第二个参数,command是要采取的操作,它可以取下面的三个值 : IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。 IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID:删除共享内存段

    第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员: struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; };

    例: 程序1

    #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include <unistd.h> // int shmget(key_t key, size_t size, int shmflg); int main(){ char *dizhi; key_t key=ftok(".",1); int shmid=shmget(key,1024*4,IPC_CREAT|0666); if(shmid==-1){ printf("打开失败!\n"); exit(-1); } dizhi=shmat(shmid,0,0); printf("打开成功!\n"); strcpy(dizhi,"lin sui cheng gong le!"); sleep(5); shmdt(dizhi); shmctl(shmid,IPC_RMID,0); }

    程序2

    #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include <unistd.h> // int shmget(key_t key, size_t size, int shmflg); int main(){ char *dizhi; key_t key=ftok(".",1); int shmid=shmget(key,1024*4,0); if(shmid==-1){ printf("打开失败!\n"); exit(-1); } dizhi=shmat(shmid,0,0); printf("%s\n",dizhi); shmdt(dizhi); }

    信号 信号

    信号处理函数的注册 信号处理函数的注册不只一种方法,分为入门版和高级版

    入门版:函数signal 高级版:函数sigaction 信号处理发送函数

    信号发送函数也不止一个,同样分为入门版和高级版 1.入门版:kill 2.高级版:sigqueue 如下示例: 入门版 程序1:

    #include <stdio.h> #include <unistd.h> #include <signal.h> // typedef void (*sighandler_t)(int); // sighandler_t signal(int signum, sighandler_t handler); void handler(int asd){ switch(asd){ case 2: printf("SIGINT\n"); break; case 9: printf("SIGKILL\n"); } } int main(){ signal(SIGINT,handler); signal(SIGKILL,handler); while(1){ printf("a shuang!\n"); sleep(1); } return 0; }

    程序2:

    #include <stdio.h> #include <unistd.h> #include <signal.h> #include <sys/types.h> #include <stdlib.h> int main(int sigid,char **asd){ char aus[128]={0}; int signum=atoi(asd[1]); int pid=atoi(asd[2]); // kill(pid,signum); sprintf(aus,"kill -%d %d",signum,pid); system(aus); printf("signum:%d,pid:%d\n",signum,pid); return 0; }

    signal为信号处理函数(接受),kill为信号发送函数 sprintf函数与printf的区别是,printf是输出到标准输出设备,sprintf是输出到字符串 kill(pid,signum); 和 sprintf(aus,“kill -%d %d”,signum,pid); system(aus); 在上面程序中都可达到相同的效果 高级版 程序1:

    #include <signal.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> // int sigaction(int signum, const struct sigaction *act, // struct sigaction *oldact); void handler(int signum,siginfo_t *info,void *context){ printf("执行命令:%d\n",signum); if(context!=NULL){ printf("get data:%d\n",info->si_int); printf("get data:%d\n",info->si_value.sival_int); /*关于void (*sa_sigaction)(int, siginfo_t *, void *);处理函数来说还 需要有一些说明。void* 是接收到信号所携带的额外数据;而struct siginfo 这个结构体主要适用于记录接收信号的一些相关信息。关于发送过来的数据是存 在两个地方的,sigval_t si_value这个成员中有保存了发送过来的信息;同 时,在si_int或者si_ptr成员中也保存了对应的数据。*/ } } int main(){ printf("pid=%d\n",getpid()); struct sigaction act; //sigaction 是一个系统调用,结构体不需要自己声明 act.sa_sigaction=handler; act.sa_flags=SA_SIGINFO; sigaction(SIGUSR1,&act,NULL); while(1); return 0; }

    程序2:

    #include <signal.h> #include <stdio.h> #include <stdlib.h> int main(int argc,char **argv){ if(4!=argc){ printf("输入格式错误!\n"); return -1; } int sig=atoi(argv[1]); pid_t pid=atoi(argv[2]); //atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数 if(sig>0&&pid>0){ union sigval val; //union sigval是一个系统调用,结构体不需要自己声明 val.sival_int=atoi(argv[3]); printf("seng=%d\n",atoi(argv[3])); sigqueue(pid,sig,val); }else{ printf("输入信息错误!\n"); } return 0; }

    信号量 信号量函数详细介绍 信号量函数由 semget semop semctl 三个函数组成

    #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> #include <unistd.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 id){ struct sembuf sops; sops.sem_num = 0; /* Operate on semaphore 0 */ sops.sem_op = -1; /* Wait for value to equal 0 */ sops.sem_flg = SEM_UNDO; semop(id,&sops,1); printf("get key!\n"); } void vputkey(int id){ struct sembuf sops; sops.sem_num = 0; /* Operate on semaphore 0 */ sops.sem_op = 1; /* Wait for value to equal 0 */ sops.sem_flg = SEM_UNDO; semop(id,&sops,1); printf("put key!\n"); } int main(){ //int semget(key_t key, int nsems, int semflg); key_t key=ftok(".",1); int semid; semid=semget(key,1,IPC_CREAT|0666); // int semctl(int semid, int semnum, int cmd, ...); union semun seminit; seminit.val=0; semctl(semid,0,SETVAL,seminit); int pid=fork(); if(pid>0){ sleep(1); pgetkey(semid); printf("这是父进程!\n"); vputkey(semid); semctl(semid,0,IPC_RMID); }else if(pid==0){ // pgetkey(semid); printf("这是子进程!\n"); vputkey(semid); }else{ printf("失败!\n"); } sleep(1); return 0; }

    运行结果如下

    这是子进程! put key! get key! 这是父进程! put key!
    Processed: 0.018, SQL: 8