寒假学习 第15天 (linux 高级编程) 笔记 总结
接着昨天
一、进程
2.创建进程
(1) int system(const char *command);
(2) FILE *popen(const char *command, const char *type);
(3) exec系列函数
int execl( const char *path, const char *arg, ...);
//第一个参数:替换的进程,第二个参数..... 命令行
//命令行格式:命令名 选项参数 并且命令行必须、以0结尾
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[]);
替换当前进程的代码空间的代码数据,函数本生不会创建进程 见例子1
execl与execlp的区别
execl必须是绝对路径,execlp不用 见例子2
例子1:
test.c
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("%d\n",getpid()); sleep(5); return 0; }
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("%d\n",getpid()); int r=execl("test","mytest",NULL);//结尾必须是0 printf("end %d\n",r); return 0; }
运行时 printf(“end %d\n”,r); 不会执行
并且打印的两个pid相同
例子2:
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("%d\n",getpid()); int r=execl("/bin/ls","ls","-l",NULL); printf("end %d\n",r); return 0; }
1. 克隆父进程的代码跟执行位置,重fork开始就分两个进程进行,例子1
2. fork创建的进程,父子进程是同时进行的
子进程创建好了以后,父进程马上把时间片交给系统,然后系统在调度下一个时间片由那个执行。 例子2
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("before creat process!\n"); int pid=fork(); printf("after creat process:%d\n",pid); return 0; } 结果: before creat process! after creat process:2651 after creat process:0 //为什么为0?因为fork不光克隆父进程的代码,还克隆了父进程的执行位置,所以子进程中pid=fork() 是没有执行的,pid为0.
例子2:
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("before creat process!\n"); int pid=fork(); while(1) { if(pid==0){ printf("AAAAA\n"); sleep(1); }else{ printf("BBBBB\n"); sleep(1); } } return 0; } 结果:AAAAA 跟BBBBB交叉出现
3.进程的应用
1. fork 的用处? 使用fork实现多任务(Unix本身是不支持多线程,有时就要用fork创建多进程)
2. 实现多任务的方式
1.线程
2.进程
3.信号
4.异步
5.进程池与线程池
例子:使用进程创建多任务(让屏幕同时显示时间跟随机数)#include <curses.h> #include <unistd.h> #include <stdlib.h> #include <time.h> WINDOW *wtime,*wnumb; int main(int argc, const char *argv[]) { initscr(); box(stdscr,0,0); wtime=derwin(stdscr,3,10,0,(COLS-10)); //右上角显示时间 wnumb=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2); //中间显示随机数 box(wtime,0,0); box(wnumb,0,0); refresh(); wrefresh(wtime); wrefresh(wnumb); if(fork()){ //父进程 time_t tt; struct tm *t; while(1) { tt=time(0); t=localtime(&tt); mvwprintw(wtime,1,1,"%02d:%02d:%02d",t->tm_hour,t->tm_min,t->tm_sec); refresh(); wrefresh(wtime); wrefresh(wnumb); sleep(1); } }else{ //子进程 while(1){ mvwprintw(wnumb,1,1,"%8d",rand()%100000000); refresh(); wrefresh(wtime); wrefresh(wnumb); usleep(100000); } } endwin(); return 0; }
4.理解进程
(1).父子进程的关系
是独立的两个进程,有各自的pid
从进程树可以看出两个独立但不平行是父子节点关系(pstree可以查看进程树)
(2) 父进程先结束
子进程就依托根进程init (孤儿进程)
理论上孤儿进程对系统没有任何的危害。
子进程先结束
子进程会成为僵尸进程。
僵尸进程不占用内存,CPU但在我们的进程任务管理树上占用一个节点。
僵尸进程会造成进程名额资源的浪费,所以要处理僵尸进程
(3). 僵尸进程的回收(使用wait回收)
例子:
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, const char *argv[]) { if(fork()){ int status; printf("parent\n"); wait(&status); printf("%d\n",WEXITSTATUS(status)); sleep(1000); }else{ printf("child\n"); sleep(10); exit(34); } return 0; }运行10秒后 子进程回收,不会变成僵尸进程,pstree查看看不到子进程
4. 父进程怎么知道子进程退出
子进程结束通常会向父进程发送 一个SIGCHLD信号(kill -l 可以查看所有信号)
5.父进程处理子进程退出信号
signal(int sig,void(*fun)(int));
向系统注册:告诉系统只要sig信号发生,系统就停止进程,并调用fun函数。
当函数执行完毕,继续原来的进程 (中断)
1 实现处理函数
2使用signal来绑定信号与函数
例子:僵尸进程回收
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> void deal(int s) { int status; wait(&status); printf("回收中!...\n"); //这个函数不执行完毕程序就不会进行下去 sleep(10); printf("回收完毕:%d\n",WEXITSTATUS(status)); } int main(int argc, const char *argv[]) { if(fork()){ signal(17,deal); //向系统注册 SIGCHLD 就是 17 while(1) { printf("parent\n"); sleep(1); } }else{ printf("child\n"); sleep(10); printf("child out\n"); exit(34); } return 0; }
6. 父子进程的资源访问
(1).内存资源
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { int a = 20; if(fork()){ printf("patent a:%d\n",a); printf("patent a:%p\n",&a); a=33; sleep(3); }else{ printf("child a:%d\n",a); printf("child a:%p\n",&a); sleep(2); printf("child a:%d\n",a); printf("child a:%p\n",&a); } return 0; } 结果: patent a:20 patent a:0x7fffbd3e533c child a:20 child a:0x7fffbd3e533c child a:20 child a:0x7fffbd3e533c
#include <stdio.h> #include <unistd.h> int main(int argc, const char *argv[]) { int *a = (int *)malloc(4); *a = 20; if(fork()){ printf("patent a:%d\n",*a); printf("patent a:%p\n",a); a=33; sleep(3); }else{ printf("child a:%d\n",*a); printf("child a:%p\n",a); sleep(2); printf("child a:%d\n",*a); printf("child a:%p\n",a); } return 0; } 结果: patent a:20 patent a:0x1d2f010 child a:20 child a:0x1d2f010 child a:20 child a:0x1d2f010
由上面两例子可得到
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> int main(int argc, const char *argv[]) { int *a = (int *)mmap(0,4,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_SHARED,0,0); *a = 20; if(fork()){ printf("patent a:%d\n",*a); printf("patent a:%p\n",a); *a = 33; sleep(3); }else{ printf("child a:%d\n",*a); printf("child a:%p\n",a); sleep(2); printf("child a:%d\n",*a); printf("child a:%p\n",a); } return 0; } 结果: patent a:20 patent a:0x7f76f73f1000 child a:20 child a:0x7f76f73f1000 child a:33 //改变了, child a:0x7f76f73f1000
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> int main(int argc, const char *argv[]) { //跟上面的例子的区别只是把PROT_SHARE 改成MAP_PRIVATE int *a = (int *)mmap(0,4,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,0,0); *a = 20; if(fork()){ printf("patent a:%d\n",*a); printf("patent a:%p\n",a); *a = 33; sleep(3); }else{ printf("child a:%d\n",*a); printf("child a:%p\n",a); sleep(2); printf("child a:%d\n",*a); printf("child a:%p\n",a); } return 0; } 结果: patent a:20 patent a:0x7fe9bb8ab000 child a:20 child a:0x7fe9bb8ab000 child a:20 //没有改变 child a:0x7fe9bb8ab000映射内存
#include <stdio.h> #include <unistd.h> #include <sys/mman.h> int main(int argc, const char *argv[]) { int *a=sbrk(4); *a = 20; if(fork()){ printf("patent a:%d\n",*a); printf("patent a:%p\n",a); *a = 33; sleep(3); }else{ printf("child a:%d\n",*a); printf("child a:%p\n",a); sleep(2); printf("child a:%d\n",*a); printf("child a:%p\n",a); } return 0; } 结果: patent a:20 patent a:0x12a1000 child a:20 child a:0x12a1000 child a:20 child a:0x12a1000
sbrk映射的是MAP_PRIVATE的
(2).文件资源
#include <stdio.h> #include <fcntl.h> int main(int argc, const char *argv[]) { int fd=open("test.txt",O_RDWR); if(fork()){ printf("patent\n"); char buf[1024]={0}; read(fd,buf,1024); printf("%s\n",buf); }else{ printf("child\n"); char buf[1024]={0}; read(fd,buf,1024); printf("%s\n",buf); } close(fd); return 0; } test.txt 内容 123456 结果: patent 123456 child
文件是独立的,close() ,每个进程都要有
#include <stdio.h> #include <fcntl.h> int main(int argc, const char *argv[]) { int fd=open("test.txt",O_RDWR); if(fork()){ printf("patent\n"); char buf[1024]={0}; lseek(fd,0,SEEK_SET); read(fd,buf,1024); printf("%s\n",buf); }else{ printf("child\n"); char buf[1024]={0}; lseek(fd,0,SEEK_SET); read(fd,buf,1024); printf("%s\n",buf); } close(fd); return 0; } 结果: patent 123456 child 123456
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> main() { int fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666); if(fork()) { write(fd,"Hello",5); close(fd); }else{ write(fd,"Word",5); close(fd); } } cat test.txt结果: HelloWord
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> main() { if(fork()) { int fd=open("test.txt",O_RDWR); write(fd,"Hello",5); close(fd); }else{ int fd=open("test.txt",O_RDWR); write(fd,"Word",5); close(fd); } } cat test.txt 结果: Word
结论: