IO进程线程day5(2023.8.2)

一、Xmind整理:

父进程会拷贝文件描述符表给子进程:

 

二、课上练习:

练习1:①从终端获取一个文件的路径以及名字。②若该文件是目录文件,则将该文件下的所有文件的属性显示到终端,类似ls -l该文件夹③若该文件不是目录文件,则显示该文件的属性到终端上,类似ls -l这单个文件

include <stdio.h>
include <string.h>
include <stdlib.h>
include <head.h>
/获取文件类型
har get_fileType(mode_t m)switch(m&S_IFMT){case S_IFSOCK:return('s');break;case S_IFLNK:return('l');break;case S_IFREG:return('-');break;case S_IFDIR:return('d');break;case S_IFCHR:return('c');break;case S_IFBLK:return('b');break;case S_IFIFO:return('p');break;}/获取文件权限
oid get_filePermission(mode_t m)for(int i=0;i<9;i++){if((m&(0400>>i))==0){putchar('-');continue;}//能运行到当前位置,则代表对应位置有权限//需要判断是r  w  x当中的哪一个switch(i%3){                                                                  case 0:putchar('r');break;case 1:putchar('w');break;case 2:putchar('x');break;}}return;nt getstat(struct stat buf,char *str)//文件的类型和权限char type=get_fileType(buf.st_mode);printf("%c",type);get_fileType(buf.st_mode);//文件的硬链接数printf("%ld ", buf.st_nlink);//文件的所属用户//printf("uid: %d\n", buf.st_uid);//将uid转换成名字struct passwd* pwd = getpwuid(buf.st_uid);if(NULL == pwd){ERR_MSG("getpwuid");return -1;}printf("%s ", pwd->pw_name);//文件所属组用户//printf("gid: %d\n", buf.st_gid);//将gid转换成名字struct group* grp = getgrgid(buf.st_gid);if(NULL == grp){ERR_MSG("getgrgid");return -1;}printf("%s ", grp->gr_name);//文件大小printf("%ld ", buf.st_size);//文件的修改时间struct tm* info=NULL;info=localtime(&buf.st_mtime);//printf("%ld ",buf.st_ctime);printf("%02d %02d %02d:%02d ",info->tm_mon+1,info->tm_mday,info->tm_houprintf("%s\n",str);nt main(int argc, const char *argv[])char str[20]="";char path[300]="";char type=0;struct stat buf;DIR* dp=NULL;struct dirent* rp=NULL;//从终端获取一个文件的路径以及名字printf("please enter a filename:");scanf("%s",str);getchar();//判断文件是否是目录文件if(stat(str,&buf) < 0){ERR_MSG("stat");return -1;}type=get_fileType(buf.st_mode);if('d'==type){//文件是一个目录文件,则需要打开目录dp = opendir(str);if(NULL == dp){ERR_MSG("opendir");return -1;}printf("open success \n");while(1){//循环读取目录rp = readdir(dp);if(NULL == rp){if(0 == errno){printf("读取完毕\n");break;}else{ERR_MSG("readdir");return -1;}}//将读取到的文件名传入stat获取属性sprintf(path,"%s%s",str,rp->d_name);//printf("path:%s\n",path);if(stat(path,&buf)<0){ERR_MSG("stat");return -1;}getstat(buf,rp->d_name);//  printf("[%d]%s\n",++i,rp->d_name);}closedir(dp);}elsegetstat(buf,str);return 0;

练习2:文件IO函数实现,拷贝文件。子进程先拷贝后半部分,父进程再拷贝前半部分。允许使用sleep函数

#include <stdio.h>
#include <head.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd_r = open("./1.png", O_RDONLY);if(fd_r < 0){ERR_MSG("open");return -1;}int fd_w = open("copy.png", O_WRONLY|O_CREAT|O_TRUNC, 0644);if(fd_w < 0){ERR_MSG("open");return -1;}//计算文件大小off_t size = lseek(fd_r, 0, SEEK_END);pid_t cpid = fork();if(cpid > 0){sleep(4);//父进程拷贝前半部分//将偏移量修改到0lseek(fd_r, 0, SEEK_SET);lseek(fd_w, 0, SEEK_SET);char c = 0;for(int i=0; i<size/2; i++){read(fd_r, &c, 1);write(fd_w, &c, 1);}printf("前半部分拷贝完毕\n");}else if(0 == cpid){                                                           //子进程拷贝后半部分//将偏移量修改到size/2lseek(fd_r, size/2, SEEK_SET);lseek(fd_w, size/2, SEEK_SET);char c = 0;for(int i=size/2; i<size; i++){read(fd_r, &c, 1);write(fd_w, &c, 1);}                                                               printf("后半部分拷贝完毕\n");}else{ERR_MSG("fork");return -1;}close(fd_r);close(fd_w);return 0;
}

练习3:getpid / getppid

功能:获取进程号 、 获取父进程号

原型:

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

返回值:

获取进程号 、 获取父进程号

练习4:_exit

功能:结束进程,销毁其在内存中的资源,且直接摧毁缓冲区,不会刷新缓冲区!!!

原型:

#include <unistd.h>
void _exit(int status);

参数:

int status:可以传递进程退出状态值给其父进程,父进程可以通过wait/ waitpid函数接收。可以传递任意整型;

练习5:exit

功能:结束进程,销毁其在内存中的资源,会刷新缓冲区!!!

原型:

#include <stdlib.h>
void exit(int status);

参数:

int status:可以传递进程退出状态值给其父进程,父进程可以通过wait/ waitpid函数接收。可以传递任意整型;

小练1:

小练2:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{pid_t cpid = fork();if(cpid > 0) //父进程{printf("parent\n");//阻塞函数,阻塞等待任意子进程退出pid_t wpid = wait(NULL);printf("wpid =%d\n",wpid);while(1){                                                                         printf("this is parent: %d %d\n",getpid(),cpid);sleep(1);}}else if(0 == cpid){int i = 0;while(i < 3){printf("this id child: %d %d\n",getppid(),getpid());sleep(1);i++;}printf("子进程准备退出\n");//_exit(0);    //退出进程,不会刷新缓冲区exit(0);    //退出进程,会刷新缓冲区printf("子进程已经退出\n");}else{perror("fork");return -1;}return 0;
}

练习6:wait

功能:1.阻塞函数,阻塞等待任意子进程退出

           2.回收退出的子进程的资源,(回收僵尸进程)

           3.接收子进程退出状态

原型:

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);

参数:

 int *wstatus:接收子进程传递回来的退出状态值,若不想接收,则填NULL;

返回值:

>0, 成功返回退出的子进程的PID号;
=-1,函数运行失败,更新errno; 
若没有子进程,则wait函数运行失败;
父进程只能回收子进程,无法回收孙子进程。

 注:①若子进程退出,父进程没有回收子进程的资源,此时子进程会变成僵尸进程。

        ②没有子进程,则wait函数运行失败

练习7:子进程退出状态

        wait(int* wstatus)中,int* wstatus指向的int类型参数,只有[8bit, 15bit]用于存储子进程传递的退出状态值。范围为[0, 255]。

所以子进程只能传递256种状态

1)从wstatus中提取子进程退出状态值 

int wstatus = -1;
pid_t wpid = wait(&wstatus);    
printf("wpid = %d  wstatus=%d\n", wpid, wstatus>>8); 
WEXITSTATUS(wstatus): 提取wstauts中子进程传递的退出状态;--> ((status) & 0xff00) >> 8)

2)判断子进程是否正常退出 

WIFEXITED(wstatus)     若正常退出,则返回真。
正常退出: exit  _exit  主函数调用return退出。

小练: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{pid_t cpid = fork();if(cpid > 0) //父进程{printf("parent\n");//阻塞函数,阻塞等待任意子进程退出int wstatus = -1;pid_t wpid = wait(&wstatus);printf("wpid = %d wstatus= %d\n",wpid,WEXITSTATUS(wstatus));if(WIFEXITED(wstatus))printf("子进程正常退出\n");elseprintf("子进程异常退出\n");while(1){printf("this is parent: %d %d\n",getpid(),cpid);sleep(1);}}else if(0 == cpid){int i = 0;while(i < 3){printf("this is child: %d %d\n",getppid(),getpid());sleep(1);//  i++;}printf("子进程准备退出\n");//_exit(0);    //退出进程,不会刷新缓冲区exit(0);    //退出进程,会刷新缓冲区printf("子进程已经退出\n");}                                                                    else{perror("fork");return -1;}return 0;
}

 

练习8:waitpid

功能:阻塞等待指定子进程退出

原型:

 #include <sys/types.h>#include <sys/wait.h>pid_t waitpid(pid_t pid, int *wstatus, int options);

参数:

 pid_t pid:< -1  阻塞等待指定进程组下的任意一个子进程退出;-1    阻塞等待当前进程下的任意一个子进程退出; 与wait函数的功能基本一致;0     阻塞等待当前进程组下的任意一个子进程退出;> 0   阻塞等待指定的子进程退出;子进程的pid号 == pid参数;int *wstatus:接收子进程传递回来的退出状态值,若不想接收,则填NULL;int options:    0:阻塞方式运行,当指定的子进程没有退出的时候,该函数阻塞,直到指定子进程退出,解除阻塞;WNOHANG:非阻塞方式运行,当指定的子进程没有退出,该函数不阻塞,立即返回; 

返回值:

 成功,>0, 成功回收到的子进程的pid号;=0, 函数运行成功,但是此时子进程没有退出。函数运行失败,返回-1,更新errno;        1. 若指定的子进程不存在(没有子进程)的时候,函数运行失败;   
2. 子进程无法回收父进程的资源,        
3. 同级之间无法相互回收资源。

小练: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{pid_t cpid = fork();if(cpid > 0) //父进程{printf("parent\n");//阻塞函数,阻塞等待任意子进程退出,并回收子进程的资源//pid_t wpid = waitpid(-1,NULL,0);//非阻塞方式运行,若运行到waitpid的时候,子进程没有退出,则返回0//若运行到waitpid的时候,子进程已经退出了,则收回尸体,并返回子进程的pid号sleep(4);pid_t wpid = waitpid(-1,NULL,WNOHANG);printf("wpid = %d\n",wpid);while(1){printf("this is parent: %d %d\n",getpid(),cpid);sleep(1);}}else if(0 == cpid){int i = 0;while(i < 3){printf("this is child: %d %d\n",getppid(),getpid());sleep(1);i++;}                                                                                printf("子进程准备退出\n");//_exit(0);    //退出进程,不会刷新缓冲区exit(0);    //退出进程,会刷新缓冲区printf("子进程已经退出\n");}else{perror("fork");return -1;}return 0;
}

练习9:孤儿进程

父进程退出,子进程不退出,此时子进程被1号(init)进程收养,变成孤儿进程。

孤儿进程会脱离终端控制,且运行在后端,不能用ctrl+c杀死后端进程,但是可以被kill -9杀死。

 #include <stdio.h>#include <string.h>#include <stdlib.h>#include <head.h>int main(int argc, const char *argv[]){//父进程退出,子进程不退出pid_t cpid = fork();if(cpid > 0) //父进程{}else if(0 == cpid){while(1){printf("this is child: %d %d\n",getppid(),getpid());sleep(1);}}else{perror("fork");return -1;}return 0;}

练习10:僵尸进程

子进程退出,父进程不退出去,且父进程没有给子进程收尸,此时子进程就变成僵尸进程。

注意:

  • 僵尸进程只能被回收,不能被杀死。
  • 僵尸进程有危害:占用进程号,占用部分内存空间,占用物理空间,占用进程调度块(PCB)等等...
  • 回收僵尸进程的方式:
    1. 结合信号的方式回收僵尸进程:当子进程退出后,通知父进程收尸。
    2. wait / waitpid函数回收。缺点:阻塞函数,父进程无法做自己的事情。非阻塞形式,有可能收不到。
    3. 退出父进程后,子进程的资源由内核自动回收。
 #include <stdio.h>#include <string.h>#include <stdlib.h>#include <head.h>int main(int argc, const char *argv[]){//子进程退出,父进程不退出pid_t cpid = fork();if(cpid > 0) //父进程{while(1){printf("this is parent: %d %d\n",getpid(),cpid);sleep(1);}}else if(0 == cpid){}else{perror("fork");return -1;}return 0;}

练习11:守护进程(幽灵进程)

1.守护进程脱离于终端,且运行在后端

2.守护进程在执行过程中不会将信息显示在任何终端上,避免影响前端任务执行。且不会被任何终端产生的终端信息所打断。

3.守护进程目的:需要周期性执行某个任务或者周期性等待处理某些事情的时候,为了避免影响前端执行或者被前端信息打断的时候,可以使用守护进程。

守护进程的创建:

1.创建孤儿进程:所有工作都在子进程中执行,从形式上脱离终端控制。

   fork(), 退出父进程

2.创建新的会话组:使子进程完全独立出来,防止兄弟进程对其有影响

setsid() 函数
功能:创建一个新的进程组和会话组,成为该进程组和会话组组长
原型:#include <sys/types.h>#include <unistd.h>pid_t setsid(void);

3.修改当前孤儿进程的运行目录为不可卸载的文件系统:例如根目录,/tmp

   防止运行目录被删除后,导致进程崩溃

chdir函数
功能:修改运行目录;
原型:#include <unistd.h>int chdir(const char *path);
chdir("/");

注意:从当前位置往后,运行在指定的目录下

4.重设文件权限掩码:umask(0), 一般清零;

5.关闭所有文件描述符,从父进程继承过来的文件描述符不会用到,浪费资源。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{//创建孤儿进程pid_t cpid = fork();if(0 == cpid){//创建新的会话pid_t sid = setsid();printf("sid = %d\n", sid);//修改运行目录为不可卸载的文件目录下chdir("/");//清空文件权限掩码umask(0);//关闭所有文件描述符for(int i = 0; i<getdtablesize(); i++)     close(i);while(1){                                          //守护进程运行的周期性代码sleep(1);}}return 0;
}

三、课后作业:

1.打印时钟在终端上,若终端输入quit,结束时钟

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <head.h>
int main(int argc, const char *argv[])
{pid_t cpid = fork();if(cpid > 0){time_t t;struct tm *info=NULL;while(1){if(waitpid(-1,NULL,WNOHANG) > 0)break;t = time(NULL);info = localtime(&t);printf("%d-%02d-%02d %02d:%02d:%02d\r",\info->tm_year+1900,info->tm_mon+1,\info->tm_mday,info->tm_hour,info->tm_min,info->tm_sec);fflush(stdout);sleep(1);}}else if(0 == cpid){char str[10]="";while(1){                                                                   scanf("%s",str);if(0 ==strcmp(str,"quit"))exit(0);sleep(1);}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/19417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux命令200例】touch用来创建新的文件或者修改已有文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

前端视频播放技术概览

转眼间&#xff0c;2023 年已进入下半场&#xff0c;在这样一个时间节点下&#xff0c;长视频平台如爱奇艺、优酷、腾讯视频等&#xff0c;以及短视频平台如抖音、快手等&#xff0c;对大家来说早已是司空见惯的事物。然而&#xff0c;在我们追剧、刷弹幕的时候&#xff0c;很少…

电压放大器工作在什么状态

电压放大器是一种广泛应用于电子电路中的基本电路元件&#xff0c;其主要功能是将输入信号的电压放大到所需的输出电压幅值&#xff0c;并且保持信号的形状不变。在实际电路设计中&#xff0c;电压放大器的工作状态会受到多种因素的影响&#xff0c;比如输入信号的频率、放大倍…

iOS--runtime

什么是Runtime runtime是由C和C、汇编实现的一套API&#xff0c;为OC语言加入了面向对象、运行时的功能运行时&#xff08;runtime&#xff09;将数据类型的确定由编译时推迟到了运行时平时编写的OC代码&#xff0c;在程序运行过程中&#xff0c;最终会转换成runtime的C语言代…

爱尔眼科四川省区“同心博爱 光明工程”“西部健康公益行”炉霍站启动

8月1日&#xff0c;“同心博爱 光明工程”“西部健康公益行”炉霍站出征仪式在四川爱尔眼科医院隆重举行。 此次公益活动由民革成都市委会、中共锦江区委统战部指导&#xff0c;如意树爱心促进会主办&#xff0c;民革锦江区总支部、爱尔眼科四川省区支持&#xff0c;四川爱尔眼…

手把手教你从零开始搭建个人博客

随着技术的进步和用户需求的变化&#xff0c;个人博客的形式和内容一直在不停地演变。为了给读者提供更丰富、有趣的阅读体验&#xff0c;搭建个人博客的网站一直在寻找更好的优化方法。所以现在出现了一批功能更完善的个人博客搭建软件&#xff0c;今天looklook就以HelpLook为…

C++设计模式之适配器设计模式

文章目录 C适配器设计模式什么是适配器设计模式该模式有什么优缺点优点缺点 如何使用 C适配器设计模式 什么是适配器设计模式 适配器设计模式是一种行为型设计模式&#xff0c;它允许你将两个不兼容的接口组合在一起&#xff0c;使它们能够协同工作。 该模式有什么优缺点 优…

Activiity跳转startActivity源码分析Activity启动流程(下)

调用ActivityThread子类ClientTranslationHandler的scheduleTranslation 注意上图有个sendMessage的 接着会执行translacationExecutor的execute方法。 都会走cycleToPath方法 cycleToPath方法对应的performLifecycleSequence 调用Actvitiy各个生命周期。 然后是第二种情况&am…

设计模式行为型——命令模式

目录 什么是命令模式 命令模式的实现 命令模式角色 命令模式类图 命令模式举例 命令模式代码实现 命令模式的特点 优点 缺点 使用场景 注意事项 什么是命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种数据驱动的设计模式&#xff0c;它属…

【C#学习笔记】值类型(1)

虽然拥有编程基础的人可以很快地上手C#&#xff0c;但是依然需要学习C#的特性和基础。本系列是本人学习C#的笔记&#xff0c;完全按照微软官方文档编写&#xff0c;但是不适合没有编程基础的人。 文章目录 .NET 体系结构Hello&#xff0c;World类型和变量&#xff08;重要&…

docker容器创建私有仓库(第三篇)

目录 六、创建私有仓库 七、Docker资源限制 7.1、CPU使用率 7.2、CPU共享比例 7.3、CPU周期限制 7.4、CPU核心限制 7.5、CPU 配额控制参数的混合案例 7.6、内存限制 7.7、Block IO 的限制 7.8、限制bps 和iops 8、Docker数据持久化 8.1、数据持久化介绍 8.2、Volum…

操作系统的运行机制、中断和异常、系统调用

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 操作系统 一、操作系统的运行机制1.1内核程序1.2应用程序1…

maven如何打包你会吗?

1.新建一个maven项目&#xff0c;在main/java中建立Main类 public class Main {public static void main(String[] args) {System.out.println("hello java ...");} } 2.添加依赖&#xff0c;使其成为可执行包 <build><plugins><!--打包成为可执行包-…

ESP32cam系列教程003:ESP32cam实现远程 HTTP_OTA 自动升级

文章目录 1.什么是 OTA2. ESP32cam HTTP_OTA 本地准备2.1 HTTP OTA 升级原理2.2 开发板本地基准程序&#xff08;程序版本&#xff1a;1_0_0&#xff09;2.3 开发板升级程序&#xff08;程序版本&#xff1a;1_0_1&#xff09;2.4 本地 HTTP_OTA 升级测试2.4.1 本地运行一个 HT…

ARP协议请求

文章目录 作用请求与应答流程数据包ARP协议以太网帧协议具体应用 作用 通过 IP地址 查找 MAC地址。 请求与应答流程 A&#xff1a;数据发送主机 B&#xff1a;目标主机 目前只知道目标主机IP地址&#xff0c;想把数据发送过去&#xff0c;需要查询到目标主机的MAC地址&#x…

Git使用详细教程

1. cmd面板的常用命令 clear&#xff1a;清屏cd 文件夹名称----进入文件夹cd … 进入上一级目录(两个点)dir 查看当前目录下的文件和文件夹(全拼:directory)Is 查看当前目录下的文件和文件夹touch 文件名----创建文件echo 内容 > 创建文件名----创建文件并写入内容rm 文件名…

vue3.3-TinyMCE:TinyMCE富文本编辑器基础使用

一、TinyMCE官网 GitHub - tinymce/tinymce TinyMCE中文文档中文手册 二、官网介绍 TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有&#xff1a;UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势&…

LabVIEW开发小型减阻试验平台

LabVIEW开发小型减阻试验平台 湍流摩擦在粘性流体的阻力中起着重要作用&#xff0c;减少湍流摩擦是流体力学领域的热门话题之一。在油气管道的长距离流体输送中&#xff0c;泵站提供的几乎所有动力都用于克服流体的胫骨摩擦。在流体输送领域&#xff0c;船舶的蒙皮摩擦阻力占总…

css实现水平居中

代码示例 <div class"box"><div class"box1"></div> </div>1.弹性布局&#xff1a;&#xff08;推荐&#xff09; display:flex&#xff1b; 这些要添加在父级的&#xff0c;是父级的属性 //父级添加display:flex; //父级添加jus…

SpringCloud Gateway 在微服务架构下的最佳实践

作者&#xff1a;徐靖峰&#xff08;岛风&#xff09; 前言 本文整理自云原生技术实践营广州站 Meetup 的分享&#xff0c;其中的经验来自于我们团队开发的阿里云 CSB 2.0 这款产品&#xff0c;其基于开源 SpringCloud Gateway 开发&#xff0c;在完全兼容开源用法的前提下&a…