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

一.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 time
		time_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 number
		int 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());//3650
			sleep(1);
		}	
	}
	else{
		while(1){
			printf("child:%d\n",getpid());//3651
			sleep(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())
	{
		//parent
		printf("parent!\n");
		sleep(20);
		printf("退出!\n");
	}
	else
	{		
		//child
		sleep(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);//40
		sleep(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);//3
		sleep(5);
		write(fd,"Killer",6);		
		close(fd);
	}
	else
	{
		int fd=open("test.txt",O_RDWR);
		printf("%d\n",fd);//3
		write(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    

接下来会讲:
        一.进程的基本控制
        二.进程的高级控制-信号
       
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值