进程间的通信(IPC)及相关概念

常见的进程通信的5个问题

问1. 什么是程序,什么是进程,有什么区别?

  • 程序是静态的概念,gcc xxx.c -o pro 磁盘中生成pro文件,叫做程序
  • 进程是程序的一次运行活动,通俗讲就是程序跑起来,系统中就多了一个进程

问2. 如何查看系统中有哪些进程?

  • ps命令查看 实际工作中,配合grep来查找程序中是否存在某一个进程 如:
  • ps -aux 展示所有进程
  • ps -aux|grep xxx 只查看xxx的相关进程

② 使用top指令查看,类似windows任务管理器

作用:评估程序的占用率、内存的消耗情况 问3. 什么是进程标识符?

问3. 什么是进程标识符?

每个进程都有一个非负整数表示的唯一i叫pid,类似身份证
在操作系统中,默认 Pid=0:称为交换进程(swapper)
作用: 进程调度
默认 Pid=1:init进程
作用:系统初始化

getpid 获取进程id号

问4. 什么叫父进程,什么叫子进程?
进程A创建了进程B,那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系

问5. C程序的存储空间是如何分配?

在这里插入图片描述
正文 代码段

int var = 10 ; 初始化后的数据——数据段
int b;
int arry[100]:未初始化的数据——bss段

堆:malloc申请的空间

栈空间:
保存函数返回的地址
局部自动变量都保存在栈中

如何创建一个字进程

  • fork()函数*
    头文件:#include <unistd.h>
    函数原型:pid_t fork(void);
    创建一个子进程。
    函数原型:pid_t fork(void);
    fork()大于0,则是父进程,小于0则是子进程。
    失败返回-1
    在这里插入图片描述
  • vfork()函数与fork()函数区别
    关键区别一:
    vfork 直接使用父进程存储空间,不拷贝。
    关键区别二:
    vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

父进程等待子进程退出 并收集子进程的退出状态 ,子进程退出状态不被收集,变成僵死进程(僵尸进程)

孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程

Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程

exec族函数(execl, execlp, execle, execv, execvp, execvpe)

exec族函数函数的作用:

我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

使用比较多的是加l、p、v的

函数原型:

	#include <unistd.h>
	extern char **environ;
	
	int execl(const char *path, const char *arg, ...);
	int execlp(const char *file, const char *arg, ...);
	int execle(const char *path, const char *arg,..., char * const envp[]);
	int execv(const char *path, char *const argv[]);
	int execvp(const char *file, char *const argv[]);
	int execvpe(const char *file, char *const argv[],char *const envp[]);

参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
l : 使用参数列表
p:使用文件名,并从PATH环境进行寻找可执行文件
v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

system函数

函数原型:
int system(const char * string);

例子:

		#include <stdio.h>
		#include <unistd.h>
		#include <stdlib.h>
		
	 	int main()
		   {
		       printf("before execl : \n");
		       if(system("top")==-1){		//直接使用输入想要的实现的功能,入top(查看任务管理)
		        	printf("execl failed\n");
		        	 perror("why");
		      }
		      printf("after execl\n");
		      return 0;
		  }

进程间的通信(IPC)

一.管道

1.无名管道pipe
特点:

1.半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

2.只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

3.可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

函数原型:

#include <unistd.h>
int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

**当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。
**
使用管道实现文件拷贝:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <string.h>
  8 
  9 int main(int argc,char **argv)
 10 {
 11     int fd[2];
 12     int fd_src;
 13     int fd_des;
 14     int size;
 15     char *buf;
 16     char *buf2;
 17     int pid;
 18 
 19 ....
 20     if(pipe(fd) == -1){
 21         printf("pipe creat failed!\n");
        perror("pipe");
 23     }
 24 ....
 25     pid=fork();
 26 
 27     if(pid < 0 ){
 28         printf("creat child failed!\n");
 29     }
 30     else if(pid > 0 )
 31     {
 32         close(fd[0]);
 33         fd_src=open(argv[1],O_RDWR);
 34         if(fd_src == -1){
 35             perror("fd_src");
 36         }
 37 
 38         size=lseek(fd_src,0,SEEK_END);
 39         buf=(char *)malloc(size);
 40         lseek(fd_src,0,SEEK_SET);
 read(fd_src,buf,size);
 42 ........
 43         //printf("befor:%s\n",buf);
 44         write(fd[1],buf,strlen(buf));
 45         close(fd_src);
 46         wait();
 47     }
 48     else
 49     {
 50         fd_des=open(argv[2],O_RDWR|O_CREAT,0600);	
 51         close(fd[1]);
 52         buf2=(char *)malloc(128);
 53         read(fd[0],buf2,1024);
 54 
 55         //printf("after:%s\n",buf2);
 56 
 57         write(fd_des,buf2,strlen(buf2));
 58         close(fd_des);
       exit(0);
 60     }
 61 
 62     return 0;
 63 }

2.命名管道FIFO
特点:
可以在无关的进程之间交换数据
有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

函数原型:

#include <sys/stat.h>
// 返回值:成功返回0,出错返回-1
int mkfifo(const char *pathname, mode_t mode);

例子:

read.c	读demo
   	  1 #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <stdio.h>
      4 #include <errno.h>
      5 #include <fcntl.h>
      6 // int mkfifo(const char *pathname, mode_t mode);	函数原型
      7           
      8 int main()  
      9 {             
     10     int nread=0;
     11     char buf[30]={0}; 
     12     if(mkfifo("./file",0600)==-1 && errno != EEXIST){	//创建命名管道
     13         printf("fifo creat failed\n");
     14         perror("why");       
     15     }              
     16         int fd=open("./file",O_RDONLY);	//以只读的方式打开文件
     17         printf("open successful\n");	
     18         while(1){         
     19             nread=read(fd,buf,30);		//读数据
     20             printf("read %d byte from fifo,context:%s\n",nread,buf);
     21         }             
     22           close(fd);
     23     return 0;         
     24 }   

write.c	写demo
   	  #include <sys/types.h>
      2 #include <sys/stat.h>
      3 #include <stdio.h>
      4 #include <errno.h>
      5 #include <fcntl.h>
      6 #include <string.h>
      7 
      8 // int mkfifo(const char *pathname, mode_t mode);函数原型
      9 
     10 int main()
     11 {
     12     int cnt=0;
     13     char *buf="hello mkfifo!";
     14     int fd=open("./file",O_WRONLY);	//以只写的方式打开文件
     15     printf("open successful\n");
     16     while(1){
     17         write(fd,buf,strlen(buf));	//写操作
     18         sleep(1);	//每秒
     19         if(cnt == 5)		//写5次
     20         {
     21             break;
   	 22         }   
     23     }.....
     24     close(fd);
     25     return 0;
     26 }   

3.消息队列
实际用的不多

4.信号量
函数原型:

#include <sys/sem.h> 
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
int semget(key_t key, int num_sems, int sem_flags);
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
 int semop(int semid, struct sembuf semoparray[], size_t numops);  
 // 控制信号量的相关信息
7 int semctl(int semid, int sem_num, int cmd, ...);

例子:

#include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/ipc.h>
  4 #include <sys/sem.h>
  5 
  6 //int semget(key_t key, int nsems, int semflg);
  7 //int semctl(int semid, int semnum, int cmd, ...);
  8 //int semop(int semid, struct sembuf *sops, unsigned nsops);
  9 
 10 union semun {
 11     int              val;    /* Value for SETVAL */
 12     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
 13     unsigned short  *array;  /* Array for GETALL, SETALL */
 14     struct seminfo  *__buf;  /* Buffer for IPC_INFO
 15                                 (Linux-specific) */
 16 };
 17 
 18 void pGetkey(int id)	//获取钥匙
 19 {
 20     struct sembuf set;
 21 ....
     	 set.sem_num = 0;
 23     set.sem_op = -1;
 24     set.sem_flg = SEM_UNDO;
 25 ....
 26     semop(id,&set,1);
 27     
 28     printf("getkey\n");
 29 }   
 30 
 31 void vPutbackkey(int id)		//放回钥匙
 32 {
 33     struct sembuf set;
 34 ....
 35     set.sem_num = 0;
 36     set.sem_op = 1;
 37     set.sem_flg = SEM_UNDO;
 38 ....
 39     semop(id,&set,1);
 40 
     printf("put back the key\n");
 42 }
 43 
 44 int main()
 45 {   
 46     int semid;
 47     key_t key;
 48     key = ftok(".",1);
 49     semid = semget(key,1,IPC_CREAT|0666);   //获取/创建信号量
 50     
 51     union semun initsem;
 52     initsem.val = 0;
 53     semctl(semid,0,SETVAL,initsem); //初始化信号量
 54     
 55     int pid = fork();.......//创建子进程
 56     if(pid < 0){
 57         printf("child creat failed\n");
 58     }
 59     else if(pid > 0){		父进程
 60         pGetkey(semid); //拿钥匙
        printf("this is father\n");
 62         vPutbackkey(semid); //放回钥匙
 63         semctl(semid,0, IPC_RMID );
 64     }else{			//子进程
 65         printf("this is child\n");
 66         vPutbackkey(semid);
 67     }
 68 
 69     return 0;
 70 }

5.共享内存
指两个或者多个共享一个给定的存储区
函数原型:
1.

   #include <sys/ipc.h>
   #include <sys/shm.h>
   int shmget(key_t key, size_t size, int shmflg)  创建共享内存
#include <sys/types.h>
void *shmat(int shmid, const void *shmaddr, int shmflg)	 	
// 共享内存映射,将共享内存空间挂载到进程中
 int shmdt(const void *shmaddr)  //共享内存分离,将进程与共享内存空间分离
 int shmctl(int shmid, int cmd, struct shmid_ds *buf)
  // 共享内存控制,对共享内存进程操作,删除

例子:

shmread.c
  1 #include <sys/shm.h>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 
  6 // int shmget(key_t key, size_t size, int shmflg)  创建共享内存
  7 // void *shmat(int shmid, const void *shmaddr, int shmflg)
  8 // 共享内存映射,将共享内存空间挂载到进程中
  9 // int shmdt(const void *shmaddr)  共享内存分离,将进程与共享内存空间分离
 10 //  int shmctl(int shmid, int cmd, struct shmid_ds *buf)
 11 // 共享内存控制,对共享内存进程操作,删除
 12 int main() {
 13     int shm_id;
 14     char *shmaddr;
 15     key_t key;
 16     key = ftok(".", 1);
 17 
 18     shm_id = shmget(key, 1024 * 4, 0);  //创建1024*4的空间的共享内存
 19     if (shm_id == -1) {
 20         printf("shmget failed\n");
 21         exit(-1);
 22     }
 23 
 24     shmaddr = shmat(shm_id, 0, 0);  //映射,将共享内存空间挂载到进程中
 25 
 26     printf("shmat ok!\n");
 27     printf("data:%s\n", shmaddr);
 28 
 29     shmdt(shmaddr);  //内存分离
 30 
 31     printf("quit\n");
 32     return 0;
 33 }

shmwrite.c
  1 #include <sys/shm.h>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <stdlib.h>
  5 
  6 // int shmget(key_t key, size_t size, int shmflg)  创建共享内存
  7 // void *shmat(int shmid, const void *shmaddr, int shmflg)
  8 // 共享内存映射,将共享内存空间挂载到进程中
  9 // int shmdt(const void *shmaddr)  共享内存分离,将进程与共享内存空间分离
 10 //  int shmctl(int shmid, int cmd, struct shmid_ds *buf)
 11 // 共享内存控制,对共享内存进程操作,删除
 12 int main() {
 13     int shm_id;
 14     char *shmaddr;
 15     key_t key;
 16     key = ftok(".", 1);	//获取pid号
 17 
 18     shm_id =
 19         shmget(key, 1024 * 4, IPC_CREAT | 0666);  //创建1024*4的空间的共享内存
 20     if (shm_id == -1) {
 21         printf("shmget failed\n");
 22         exit(-1);
 23     }
 24 
 25     shmaddr = shmat(shm_id, 0, 0);  //映射,将共享内存空间挂载到进程中
 26     printf("shmat ok!\n");
 27 
 28     strcpy(shmaddr, "hello!");  //写入内容
 29 
 30     sleep(5);
 31     shmdt(shmaddr);  //内存分离
 32 
 33     shmctl(shm_id,IPC_RMID, 0);  //释放共享内存空间
 34     printf("quit\n");
 35 
 36     return 0;
 37 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值