Linux——IO与文件目录管理 进程 exec函数系列 fork 子进程

来源:互联网 发布:t26e4超级潘兴数据图 编辑:程序博客网 时间:2024/04/29 12:32

一.IO与文件目录管理

    1.pread=lseek+read
            pread读取以后不改变读写位置
    2.mmap映射:
            /proc/${pid}/mem 无法映射
    3.IO的有效用户与实际用户
        默认情况:实际用户与有效用户一直。        
        实际用户:执行用户
        有效用户:权限用户
        uid_t getuid()
        uid_t geteuid()
    4.目录相关函数
        chdir    切换目录
        mkdir    创建目录
        rmdir    删除目录
        unlink 删除文件
        umask    设置文件权限屏蔽位
        stat    文件目录状态
    5.目录的遍历
        opendir系列函数
        readdir
        closedir
        seekdir
        dirfd
                
        int scandir(const char*dirname,//目录名
            struct dirent***namelist,//返回目录列表
            int (*)(struct dirent*),//回调函数,过滤目录
                                                        //NULL:不过滤
            int (*)(struct dirent*,struct dirent*)//排序返回目录
                                            //NULL:不排序
            );
        
        返回:    
                >=0 目录个数
                =-1 目录查找失败

二.进程

        1.什么是进程

            执行的程序:代码->资源->CPU
            进程有很多数据维护:进程状态/进程的属性
            所有进程属性采用的结构体维护->树性数据结构
            ps 察看进程常见属性
            top 察看系统进程执行状况
            pstree(ptree)
            kill 向进程发送信号
            kill  -s 信号 进程id
            kill -l 显示进程能接受的所有信号
            知道进程有很多属性:ps可以察看的属性
                

        2.创建进程

            1.代码?加载到内存?分配CPU时间片?
                代码由独立的程序存在.
            2.进程有关的创建函数
                int system(const char*filename);
                建立独立进程,拥有独立的代码空间,内存空间
                等待新的进程执行完毕,system才返回.(阻塞)
案例:
        使用system调用一个程序。
        观察进程ID。
        观察阻塞。
            新的返回值与system返回值有关系。
            任何进程的返回值:不要超过255。一个字节。

            system的返回值中8-15位存放返回码

test.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>  int main(){printf("%d\n",getpid());sleep(5);return 99;}</span>
gcc test.c -otest


system.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>main(){int r;printf("%d\n",getpid());r=system("./test");printf("%d\n",r);//返回25344 为什么不是99?        //printf("%d\n",r>>8&255);//99        //printf("%d\n",WEXITSTATUS(r));//99  该宏的作用就是上一行的结果}</span>



练习:
    使用system调用"ls -l"。"ls -l home" 把上面system函数里面改成"ls -l",可见system函数可以调用shell指令

           

popen:创建子进程

               在父子进程之间建立一个管道 可以看到重定向如何实现


案例:

            使用popen调用ls -l,并且建立一个管道读取输出

popen.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>main(){char buf[1024];FILE *f=popen("ls -l","r");int fd=fileno(f);int r;printf("=============\n");while((r=read(fd,buf,1024))>0){buf[r]=0;printf("::%s\n",buf);}printf("=============\n");close(fd);pclose(f);}</span>

                               

exec系列函数:execl   execlp

                    作用是替换当前进程的代码空间中的代码数据
                    函数本身不创建新的进程。
                    int  execl(const char*path,const char *arg,....);
                    第一个参数:替换的程序,
                    第二个参数....:命令行            
                                命令行格式:命令名  选项参数
                                命令行结尾必须空字符串结尾
案例:
            使用exec执行一个程序。
            体会:1 是否创建新的进程?没有
                       2 体会execl的参数的命令行的格式
                       3 体会execl与execlp的区别(execl只当前路径)
                                    execlp 使用系统的搜索路径

                       4 体会execl替换当前进程的代码

    

test.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>  int main(){printf("%d\n",getpid());sleep(5);return 99;}</span>
gcc test.c -otest


exec.c

<span style="font-size:18px;">include <stdio.h>#include <unistd.h>  int main(){printf("main:%d\n",getpid());//没有创建新的进程int r=execl("test","ls","-l",NULL);//printf("结束%d\n",r);//execl函数用test替换当前代码 所以该行没有被执行return 0;}</span>


 fork pid_t  fork();                   

                  //1.创建进程                  

                  //2.新进程的代码是什么:克隆父进程的代码而且克隆了执行的位置所以子进程也执行到了fork位置之后.             

                  //3.在子进程不调用fork所以返回值=0;

                 //4.父子进程同时执行.

fork.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>  int main(){int pid;printf("创建进程前!\n");pid=fork();if(pid==0){while(1){printf("子进程\n");sleep(1);}}else{while(1){printf("父进程\n");sleep(1);}}return 0;}</span>


        3.应用进程

            使用fork创建新的进程有什么应用价值呢?
            使用fork实现多任务.(Unix系统本身是不支持线程)
                            1.进程
                            2.线程
                            3.信号
                            4.异步
                            5.进程池与线程池
案例:
            使用进程创建实现多任务            
            1.UI
            2.建立多任务框架

            3.分别处理不同的任务

demo.c

<span style="font-size:18px;">#include <curses.h>#include <unistd.h>#include <time.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <math.h>WINDOW *wtime,*wnumb;main(){initscr();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()){//show timetime_t tt;struct tm *t;while(1){time(&tt);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{//show numberint num=0;int i;while(1){num=0;for(i=0;i<7;i++){num=num*10+rand()%10;}mvwprintw(wnumb,1,2,"%07d",num);refresh();//wrefresh(wtime);wrefresh(wnumb);usleep(100000);}}endwin();}</span>
gcc demo1.c -lcurses -omain

              

                            

        4.理解进程

            1.父子进程的关系
                    独立的两个进程

                    互为父子关系  pstree命令

hah.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>main(){if(fork()){while(1){printf("parent:%d\n",getpid());//3650sleep(1);}}else{while(1){printf("child:%d\n",getpid());//3651sleep(1);}}}</span>


            2.问题:
                     2.1.父进程先结束子进程怎么办?
                             子进程就依托根进程init:孤儿进程
                             孤儿进程没有任何危害.                             
                     2.2.子进程先结束?
                             子进程会成为僵死进程.
                             僵死进程不占用内存,CPU.但在进程任务管理树占用一个节点.
                             僵死进程造成进程名额资源浪费.

                             所以处理僵死进程.

parentchild.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>main(){if(fork()){//parentprintf("parent!\n");sleep(20);printf("退出!\n");}else{//childsleep(20000);printf("child!\n");}}</span>


            3.僵死进程使用wait回收

             在父进程中使用 执行到wait时会阻塞,直到子进程结束,然后会回收子进程,父进程才继续执行

            
            4.父进程怎么知道子进程退出?
                子进程结束通常会向父进程发送一个信号

                        SIGCHLD  17

parentchild1.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>void deal(int s){int status;wait(&status);printf("回收中.....\n");sleep(5);printf("回收完毕:%d!\n",WEXITSTATUS(status));}main(){if(fork()==0){printf("child!\n");sleep(6);printf("退出!\n");exit(88);}else{signal(17,deal);while(1){printf("parent!\n");sleep(1);}}}</span>

  
            5.父进程处理子进程退出信号
                    signal(int sig,void(*fun)(int));
                    向系统注册:只要sig信号发生,系统停止进程,并调用函数fun
                    当函数执行完毕,继续原来进程  称为中断
                    5.1.实现处理函数
                    5.2.使用signal邦定信号与函数        
僵死进程回收案例:
            6.父子进程的资源访问
                    6.1.内存资源
                    6.2.文件资源
                            
案例:
        说明:子进程克隆整个内存区域,(虚拟地址一样但是指向不同的物理空间)
                    但内存区域指向不同的物理空间

                    尽管克隆,但内存独立. 不能相互访问.

promem.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>#include <sys/mman.h>main(){//int *a=mmap(0,4,PROT_READ|PROT_WRITE,//MAP_ANONYMOUS|MAP_SHARED,0,0);//这种情况是映射到同一块物理空间         //                                             输出90        //int *a=sbrk(4);//输出40        int a=40;if(fork()){printf("parent:%d\n",*a);//40*a=90;}else{printf("child:%d\n",*a);//40sleep(3);printf("child:%d\n",*a);//40}}</span>


                    
                    多进程实现多任务,进程之间的数据交换是大问题.(IPC)
                    Inter-Process Commucation
                    
                    映射内存:
                                MAP_SHARED:映射到同一物理内存
                                MAP_PRIVATE:映射到不同的物理内存.
                    
案例:

        两个进程之间,文件描述符号指向的是同一个文件内核对象.

procfile1.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>#include <sys/mman.h>#include <fcntl.h>main(){//两个文件描述符指向同一个文件 一个写入时影响另一个指针的位置int fd=open("test.txt",O_RDWR);if(fork()){printf("%d\n",fd);//跟下面的输出一样sleep(5);write(fd,"Killer",6);close(fd);}else{printf("%d\n",fd);write(fd,"Clinton",7);sleep(8);close(fd);}}</span>

procfile2.c

<span style="font-size:18px;">#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>#include <signal.h>#include <sys/mman.h>#include <fcntl.h>main(){if(fork()){int fd=open("test.txt",O_RDWR);//读写指针不影响下面指针printf("%d\n",fd);//3sleep(5);write(fd,"Killer",6);close(fd);}else{int fd=open("test.txt",O_RDWR);printf("%d\n",fd);//3write(fd,"Clinton",7);sleep(8);close(fd);}}</span>



                
                结论:
                            进程的数据交换,基于两种方式:
                                        内存:有序/无序:mmap
                                        文件:有序/无序:普通文件                    
                            基于内核对象:文件.内存.队列            
                    

回顾:
        1.目录遍历            
        2.进程创建system popen exec fork
        3.僵死进程出现的条件以及回收
        4.利用多进程实现简单的多任务
        5.理解进程的克隆.

作业:
        1.使用两个进程,查找素数:(多任务)
                A进程查找1-5000
                B进程查找5001-10000
                把素数写入文件.
        2.写一个多任务:(两个进程数据共享)
                A.进程查找素数,放入mmap分配的空间
                B.进程把mmap的数据取出来,判定两个数据是否相邻.
                        相邻就打印这两个素数.
思考:                        
        3.使用opendir/readir遍历指定目录下的所有*.c文件.
                    scandir    

接下来会讲:
        一.进程的基本控制
        二.进程的高级控制-信号
       
0 0