常见的进程通信的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 }