8.进程
基本相关概念
-
ps aux
查看进程信息linuxps ajx
查看信息更全面kill -9 pid
杀死进程:给进程发送9的信号,也可以写kill -SIGKILL pid
-
概念:
- 程序:编译好的二进制文件
- 进程:运行着的程序,运行指令的过程,分配系统资源的基本单位。
-
MMU作用:
- 虚拟内存和物理内存的映射
- 修改内存访问级别
-
进程控制块PCB
-
ulimit -a 查看所有资源的上限
-
环境变量:
- 写法:
- key=value 中间不能有空格
- 获取环境变量:
- 头文件
#include <stdlib.h>
cahr* getenv(const char*name);
getenv("HOME");
- 头文件
- 写法:
fork()进程控制
-
进程api–fork()
-
fork
创建一个新的进程- 头文件
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
- 返回值:
- 一个进程返回子进程的pid,并进入本进程
- 一个进程返回0,代表进入子进程
- 头文件
-
getpid()
获得pid,进程id,获得当前进程pid_t getpid(void);
-
getppid
获得当前进程的父进程idpid_t getppid(void);
-
例子
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { int n = 5; int i =0; pid_t pid = 0; for(i = 0;i<5;i++){//父进程循环结束 pid = fork(); if(pid == 0){ printf("i am child,pid=%d,ppid=%d\n",getpid(),getppid()); break;//防止子进程再产生子进程 }else if(pid>0){ //father printf("i am father,pid=%d,ppid=%d",getpid(),getppid()); } } sleep(i); if(i<5){ printf("i am child,pid=%d,ppid=%d",getpid(),getppid()); }else{ printf("i am father"); } }
-
父进程与子进程在空间使用上,读时共享,写时复制(开辟新空间)
-
exce函数,执行程序
-
execl函数
-
头文件
#include <unistd.h>
-
excel
-
int excel(const char* path, const char* arg,.../*(char * )NULL */)
-
需要添加路径
-
excel("/bin/ls","ls","-l","--color=auto",NULL)
-
-
excelp
- 不用添加路径
int excelp(const char * file,....)
- file 要执行的程序
- arg参数列表
- 最后的NULL是作为哨兵
- 返回值,只有失败才返回,-1
-
进程回收
-
孤儿进程和僵尸进程
- 孤儿进程:父进程被kill,子进程被init领养
- 僵尸进程:子进程被kill,
- init不直接回收僵尸进程,为了让父进程知道子进程为何被kill
- 解决僵尸进程:kill掉父进程,僵尸进程会被init领养回收
-
wait函数回收子进程
-
作用
- 阻塞等待
- 回收子进程资源
- 查看死亡原因
-
头文件
#include <sys/wait.h>
#include <sys/types.h>
-
pid_t wait(int *status)
-
status 传出参数
-
成功返回终止的子进程ID
-
失败返回-1
-
pid_t wpid = wait(NULL);
-
正常死亡WIFEXITED,WEXITSTATUS查看状态
-
非正常死亡WIFSIGNALED,WTERSIG查看状态
-
int status; pid_t wpid = wait(&status); if(WIFEXITED(status)){ printf(WEXITSTATUS(status)); } if(WIFSIGNALED(status)){ printf(WTERSIG(status)); }
-
-
pid_t waitpid(pid_t pid,int* status,int options);
- pid
- <-1 组id
- -1 回收任意
- 0 回收和调用组id相同组内的子进程
>0
回收指定pid的子进程
- options
- 0与wait相同,也会阻塞
- WNOHANG 不会阻塞,如果没有子进程退出,立即返回
- pid
-
进程间通信:IPC
概念
- IPC:InterProcess Communication 进程间通信,通过内核提供的缓冲区进行数据交换的机制
- 方式主要有
- pipe 管道,父子进程之间,关系的进程之间,最简单
- fifo 有名管道,各个进程之间
- mmap 文件映射IO -速度最快
- 本地socket 最稳定
- 信号 携带信息最少
- 共享内存
- 消息队列
- 常见通信:
- 单工
- 半双工
- 全双工
PIPE通信
-
int pipe(int pipfd[2]);
-
pipfd 文件描述符,0表示读端,1表示写段
-
返回值;失败返回-1,成功返回0
-
父子进程实现pipe通信,实现 ps aux | grep bash功能
#include <stdio.h> #include <unistd.g> int main() { int fd[2]; pipe(fd); pid_t pid = fork(); if(pid == 0){//子进程 //关闭读端 close(fd[0]); //先重定向 dup2(fd[1],STDOUT_FILENO); //execlp execlp("ps","ps","aux",NULL); }else if(pid > 0){ //父进程 //关闭写段 close(fd[1]); //重定向 dup2(fd[0],STDIN_FILENO); //execlp execlp("grep","grep","bash",NULL); } return 0; }
-
读写管道
- 读管道:
- 写端全部关闭–read读到0,相当于读到文件末尾
- 写段没有全部关闭
- 有数据 – read读到数据
- 没有数据 --read阻塞 fcntl函数可以更改非阻塞
- 写管道:
- 读端全部关闭 ---- 产生一个信号SIGPIPE,程序异常终止
- 读端未全部关闭
- 管道已满 --write阻塞
- 管道未满 – write正常写入
- 读管道:
-
管道大小:默认512*8
long fpathconf(int fd,int name);
ulimit -a
-
FIFO通信
-
有名通道,实现无血缘关系的进程通信
- 创建一个管道的伪文件
mkfifo myfifo
命令创建- 也可以用函数
int mkfifo(const char *pathname,mode_t mode);
- 内核会针对fifo开辟一个缓存区,操作fifo文件,可以操作缓存区,实现进程通信,实际上是文件读写
- 创建一个管道的伪文件
-
FIFOs
-
注意事项:打开fifo文件时,read端会阻塞等待write端open,write端同里也会等待另一端打开
-
w端代码
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stats.h> #include <fcntl.h> int main(int argc,char *argv) { if(argc !=2){ printf("./a.out fifoname\n"); return -1; } int fd = open(argv[1],O_WRONLY); //write char buf[256]; int num = 1; while(1){ memset(buf,0x00,sizeof(buf));//清空缓存区 sprintf(buf,"xiaoming%04d",num++); write(fd,buf,strlen(buf)); sleep(1); } close(fd); return 0; }
-
r端代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stats.h>
#include <fcntl.h>
int main(int argc,char *argv)
{
if(argc !=2){
printf("./a.out fifoname\n");
return -1;
}
int fd = open(argv[1],O_WRONLY);
//write
char buf[256];
int num = 1;
while(1){
num = read(fd,buf,sizseof(buf));
if(num>0){
printf("read:%s\n",buf);
}
}
close(fd);
return 0;
}
mmap 和 munmap
头文件#include <sys/mman.h>
mmap 创建映射区
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
- addr 传NULL
- length 映射区长度
- prot
- PROT_READ 可读
- PROT_WRITE 可写
- flags
- MAP_SHARED 共享的,对内存的修改会影响到源文件
- MAP_PRIVATE 私有的
- fd 文件描述符
- offset 偏移量,4k的倍数
- 返回值
- 成功,返回可用的内存地址
- 失败,返回MAP_FAILED
munmap 释放映射区
int munmap(void *addr,size_t length);
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
int main()
{
int fd = open("mem.txt",O_RDWR);
//创建映射区
char *mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(mem == MAP_FAILED){
perror("mmap error.\n");
return -1;
}
strcpy(mem,"hello");
//释放mmp
munmap(mem,8);
close(fd);
return 0;
}
-
常见问题
- 更改mem变量地址,将不能成功释放
- 对mem越界操作,文件大小对映射区有影响,尽量避免
- 文件描述符先关闭,对mmap映射没有影响
- 可以用open新创建一个文件来创建映射嘛,可以,但不能是空文件
- open文件选择O_WRONLY可以吗,不可以,权限不够
-
匿名映射–仅限于有关系的进程之间
-
使用宏:MAP_ANON MAP_ANONYMOUS,在unix可以使用下面两个文件
dev/zero
可以随便映射,聚宝盆dev/null
无底洞
-
父子进程案例-
#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <sys/mman.h> #include <sys/wait.h> #include <fcntl.h> int main() { int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0); if(mem == MAP_FAILED){ perror("map error"); return -1; } pid_t pid = fork(); if(pid == 0){ //子进程 *mem = 101; printf("child,*mem = %d\n",*mem); sleep(3); printf("child, *mem = %d\n",*mem); }else if(pid > 0){ sleep(1); printf("parent,*mem = %d\n",*mem); *mem = 201; printf("parent,*mem = %d\n",*mem); //等待回收 wait(NULL); } munmap(*mem,4); return 0; }
-