8.1IO进程线程

笔记

进程

一.多进程引入

1.1引入目的

程序员写程序时,一个程序可能由多个任务组成,如果使用的是单进程,或单任务,那么该任务执行阻塞时,其他任务就无法执行,必须等到该任务解除阻塞后,才能去执行其他任务。

多进程或多线程,可以解决同一个程序中多个任务并发执行的情况

1.2进程的概念

        1>进程是程序的一次执行过程

        2>进程是程序资源分配的基本单位,系统会分给每个进程分配4G的虚拟内存,分为0--3G的用户空间和3--4G的内核空间

                多个进程共享内核空间,用户空间相互独立

        3>进程是一个动态的过程,有生命周期的概念,分为创建态、就绪态、运行态、阻塞态、死亡态

        4>进程在内核空间中存储在一个名叫task_struct中的结构体中(PCB)

task_struct:

进程描述符:task struct包含了描述一个进程所需的所有信息。
进程状态:包括运行、就绪、阻塞等状态。
进程标识符:如进程ID(PID)。
进程调度信息:如优先级、调度策略等。
内存管理信息:如虚拟地址空间、页表等。
文件系统信息:如打开的文件、文件系统根目录等
信号处理:包括待处理信号和信号处理函数。
进程间通信:如消息队列、共享内存等IPC机制相关信息。
时间和定时器:如进程创建时间、CPU使用时间等。
线程信息:在Linux中,线程被视为轻量级进程,也用taskstruct表示。

        5>单核cpu处理多任务时,一般使用的是时间片轮询机制

        6>进程与程序的区别:程序是静态的,是存储在磁盘上的二进制代码

        7>进程是动态的,是有生命周期的7>进程的组成:进程控制块(PCB)、数据段、程序段

1.3 进程的种类

进程一共分为三大类:交互进程、批处理进程、守护进程

        1>交互进程:他是由shel控制,用于直接跟用户进行交互的进程。例如:vim编辑器、文本编辑器

        2>批处理进程:本质上维护了一个队列,被放入队列中的进程会统一被调度。例如gcc编译器的一步到位的编译

        3>守护进程:脱了了终端而存在的进程,随着系统的启动而开始,随着系统的结束而终止。例如:服务进程3

1.4 进程号的概念

每个进程在系统中都有一个唯一的标识位,用一个整数表示,这就是该进程的进程号(PID)

        1>PID(processID):当前进程的进程号

        2>PPID(parent process lD):当前进程的父进程的进程号

每个进程都是由其父进程进行拷贝赋值出来的。

可以进入 /proc目录中的每个数字都是现在正在执行的一个进程

1.5 特殊的进程
        

        1>0号进程:也成为 idel进程,他是操作系统启动后执行的第一个进程,这个进程也叫空闲进

程,当没有其他进程执行时,系统会默认执行该进程。1号进程和2号进程都是由0号进程创建出来

的。

        2>1号进程:也称init进程,该进程由0号进程产生,主要完成系统创建时一些软件硬件的初始化

工作。当其他进程的父进程死亡后,会托管其子进程

        3>2号进程:也称kthreadd,该进程由0号进程产生,也成为调度进程,当某个就绪进程时间片

轮到时,该进程负责进程的调度

工作

        4>孤儿进程:当前进程的父进程死亡后,但是当前进程还没有结束,那么当前进程称为孤儿进

程,孤儿进程会由1号进程收养57僵尸进程:当前进程已经死亡,但是其父进程没有为其收尸,那么

该进程为僵尸进程

1.6 进程的相关指令

        1>查看进程信息的命令:ps,跟不同的选项,执行不同的状态

                ps-ef:显示进程之间的关系

                ps-ajx:显示进程的状态

                ps-aux:可以查看进程资源使用情况

        2>top或htop        可以动态展示进程的占用情况 

        3>pstree:展示进程树,可以显示进程的父子关系

        4>查看给定进程的进程号:pidof 进程名ki:向进程发送信号,发信号的格式

                        ki -信号名(号)进程号

                        能够发送的信号号有:可以通过指令kil-l查看

1.7进程的状态

        1>主要状态一共有五个:创建态、就绪态、运行态、阻塞态、死亡态

        2>程序中的进程的状态显示:可以通过指令man ps查看进程的状态

                进程的状态由两部分组成:主状态和附加态

主状态
                D                不可中断的休眠态(usually I0)
                R                运行态(on run queue)
                S                可中断的体眠态(waiting for an event to complete)
                T                暂停态,会给出作业号进行控制
               W                已经弃用
                X                死亡态(should never be seen)
                Z                僵尸态

附加态

                <                高优先级的进程(not nice to other users)

                N                低优先级的进程(nice to other users)

                L                锁到内存中的进程(for real-time and custom Io)

               S                会话组组长,默认为当前终端

                I                包含多线程的进程(using CLONE_THREAD,like NPTL pthreads do)

               +                表示是前台运行的进程

        3>进程状态切换的实例

二.多进程编程

        多线程允许使用同一个线程体函数

2.1 fork进程的创建函数

        1> 在进程的创建过程,其实就是通过拷贝父进程的task struct结构体而来的,子进程中保存

了大部分的父进程的遗传基因,只需要修改少部分的内容,如子进程的进程号,子进程的父进程

号。

        2>子进程和父进程的资源用户空间是完全独立的,创建出子进程后,父子进程的资源相互独立,

互不影响

        3>进程的创建,通过fork函数完成

        4>当子进程创建后,会跟父进程一起执行fork后面的语句

#include<sys/types.h>

#include<unisted.h>

pid_t fork(void)

功能:拷贝父进程得到子进程

参数:无

返回值:成功时,父进程中返回子进程中的pid号,子进程中返回0,失败返回-1并置位错误码

注意:创建出的子进程跟父进程没有先后执行的顺序,遵循时间片轮询机制

2.2进程号的获取

#include<sys/types.h>

#include<unsitd.h>

pid_t getpid(void)

功能:获取当前进程的进程号

参数:无

返回值:当前进程的pid

pid_t getppid(void)

功能:获取当前进程的父进程的pid号

参数:无

返回值:当前进程的父进程的pid

2.3进程的退出(exit、_exit)

#include<stdlib.h>

void exit(int status);

功能:退出当前进程,并刷新当前进程打开的标准IO文件指针的缓冲区

参数:退出时的状态

        EXIT_SUCCESS:成功退出

        EXIT_FAILURE:失败退出

返回值:无

#include<stdlib.h>

void _exit(int status);

功能:退出当前进程,但是不刷新当前进程打开的标准IO文件指针的缓冲区

参数:退出时的状态

        EXIT_SUCCESS:成功退出

        EXIT_FAILURE:失败退出

返回值:无

2.4进程资源的回收

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int *wstatus)

功能:阻塞回收子进程的资源,如果没有进程退出,那么就阻塞等待

参数:子进程退出时的状态,一般不接受,直接填NULL即可

返回值:成功返回回收的子进程的pid,失败返回-1并置位错误码

#include<sys/types.h>

 

#include<sys/wait.h>

pid_t waitpid(pid_t pid,int *wststus,int options)

功能:既可以阻塞也可以非阻塞形式回收僵尸进程

参数1:要回收的进程号

                >0:表示回收具体的某个进程

                =0:能够回收当前进程所在的进程组中的任意一个子进程

                =-1:回收任意一个子进程

                <-1:回收别的进程组(进程组id为给定的pid的绝对值)中的任意一个子进程

参数2:子进程退出时的状态,一般不接收,直接填NULL即可

参数3:表示是否阻塞回收僵尸进程

        0:表示阳塞        

        WNOHANG:表示非阳塞

返回值:

        >0如果成功回收了子进程资源,那么会返回该子进程的pid

        =0:表示以非阳塞的形式回收资源,但是没有回收到子进程

        =-1:失败返回-1并置位错误码

2.5创建三个进程

可以让父进程创建一个子进程,再由父进程或者子进程创建一个子进程

2.6写时拷贝技术

2.7特殊进程的验证

        1>僵尸进程

#include<myhead.h>int main(int argc, const char *argv[])
{pid_t pid = fork();      //创建出子进程if(pid < 0){perror("fork error");return -1;}else if(pid == 0){printf("我是子进程\n");exit(EXIT_SUCCESS);         //退出子进程}//父进程内容printf("我是父进程\n");sleep(5);return 0;
}

        2>孤儿进程

#include<myhead.h>int main(int argc, const char *argv[])
{pid_t pid = fork();      //创建出子进程if(pid < 0){perror("fork error");return -1;}else if(pid > 0){printf("我是父进程\n");exit(EXIT_SUCCESS);         //退出子进程}//父进程内容printf("我是子进程\n");sleep(5);return 0;
}

        3>创建守护进程

#include <myhead.h>int main(int argc, const char *argv[])
{pid_t pid=fork();if(pid<0){perror("fork error");return -1;}else if(pid>0){//退出父进程exit(EXIT_SUCCESS);}//将自己设置成会话组组长setsid();//更改操作目录为根目录chdir("/");//获得创建文件的最大权限umask(0);//将标准输入输出出错重定向到long.txt文件中int fd =open("./long.txt",O_WRONLY|O_CREAT|O_APPEND,0664);if(-1==fd){perror("open error");return -1;}//重定向dup2(fd,STDIN_FILENO);dup2(fd,STDOUT_FILENO);dup2(fd,STDERR_FILENO);//执行操作while(1){printf("hello!\n");fflush(stdout);sleep(1);}close(fd);return 0;
}

线程

一.多线程基本概念

        1> 线程:也称为轻量版的进程(LWP),是更小的任务执行单元,是进程的一个执行路径

        2> 线程是任务器调度的最小单位

        3> 一个进程中可以包含多个线程,多个线程共享进程的资源。

        4> 线程几乎不占用资源,只是占用了很少的用于程序状态的资源(大概有8k左右)

        5> 由于多个线程共同使用进程的资源,导致,线程在操作上容易出现不安全的状态

        6> 线程操作开销较小、任务切换效率较高

        7> 一个进程中,至少要包含一个线程(主线程)

        8> 在有任务执行漫长的IO等待过程中,可以同时执行其他任务

        9> linux中不直接支持线程相关的支持库,需要引入第三方库,线程支持库

                sudo apt-get install manpages-posix manpages-posix-dev

                如果程序中使用的线程支持库中的函数,编译程序时,需要加上 -lpthread 选项

二.线程支持函数(多线程编程)

多线程允许使用同一个线程体函数

2.1pthread_create:创建线程
 #include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);功能:在当前进程中,创建一个分支线程参数1:用于接收创建好的线程ID参数2:线程的属性,一般填NULL表示使用系统默认的属性创建线程参数3:线程体函数,需要传递一个函数,参数为void*,返回值为void*参数4:参数3的参数返回值:成功创建返回0,失败返回一个错误码,注意,不是内核提供的错误码   
#include<myhead.h>//定义一个用于传递数据的结构体类型
struct Buf
{int num;double key;
};//定义线程体函数
void *task(void *arg)
{int num = (*((struct Buf*)arg)).num;double key = ((struct Buf*)arg)->key;while(1){printf("我是分支线程, num = %d, key=%.2lf\n", num, key);sleep(1);}
}/************************主程序*****************************/
int main(int argc, const char *argv[])
{int num = 520;       //定义整形变量double key = 1314;   //定义浮点型数据struct Buf buf = {num, key};    //封装要传递的数据//定义变量存储线程号pthread_t tid = -1;//创建分支线程if(pthread_create(&tid, NULL, task, &buf) != 0){printf("pthread_create error\n");return -1;}printf("我是主线程,tid = %#lx\n", tid);//while(1);getchar();return 0;
}
2.2pthread_self:线程号的获取
       #include <pthread.h>pthread_t pthread_self(void);功能:获取当前线程的线程号参数:无返回值:当前线程的线程号
2.3pthread_exit:线程退出函数
       #include <pthread.h>void pthread_exit(void *retval);功能:退出当前的线程参数:线程退出时的状态,一般填NULL返回值:无
2.4pthread_jion:线程资源回收函数
  #include <pthread.h>int pthread_join(pthread_t thread, void **retval);功能:阻塞等待给定线程的退出,并回收该线程的资源参数1:要回收的线程号参数2:接收线程退出时的状态返回值:成功返回0,失败返回错误码
2.5pthread_detach:线程分离太态
       #include <pthread.h>int pthread_detach(pthread_t thread);功能:将线程设置成分离态,设置了分离态的线程,退出后,由系统回收其资源参数:要被分离的线程id号返回值:成功返回0,失败返回错误码
2.6 pthread_setcancelstate:设置取消属性
     #include <pthread.h>int pthread_setcancelstate(int state, int *oldstate);功能:设置是否接收取消指令参数1:新的状态PTHREAD_CANCEL_ENABLE:可接受状态PTHREAD_CANCEL_DISABLE:不可接收状态参数2:线程的旧的状态容器,如果不愿意要之前的状态,填NULL即可返回值:成功返回0,失败返回错误码
   返回值:成功返回0,失败返回错误码#include<myhead.h>//定义线程体函数
void *task(void *arg)
{//设置线程不可取消状态pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);printf("我是分支线程,线程号为:%#lx\n", pthread_self());//exit(EXIT_SUCCESS);        //退出进程int count = 0;while(1){printf("我真的还想再活五百年。。。。\n");sleep(1);count++;if(count == 10){break;}}pthread_exit(NULL);           //退出线程
}/************************主程序*****************************/
int main(int argc, const char *argv[])
{//定义变量存储线程号pthread_t tid = -1;//创建分支线程if(pthread_create(&tid, NULL, task, NULL) != 0){printf("pthread_create error\n");return -1;}printf("我是主线程,tid = %#lx, 主线程线程号:%#lx\n", tid, pthread_self());/*回收分支线程的资源if(pthread_join(tid, NULL) == 0){printf("成功回收了%#lx的资源\n", tid);}*///将线程设置成分离态pthread_detach(tid);printf("线程已经分离\n");//休眠5秒sleep(5);//向分之线程中发送一个取消请求pthread_cancel(tid);//while(1);getchar();return 0;
}

作业

使用两个线程完成两个文件的拷贝,分支线程1拷贝前一半,分支线程2拷贝后一半,主线程回收两个分支线程的资源

#include <myhead.h>
//定义数据结构体
typedef struct data
{const char *argv1;const char *argv2;int size;int len;
}Data;
//定义复制函数
void* copy(void *data)
{//将数据结构体中的值取出const char*argv1=((Data*)data)->argv1;const char*argv2=((Data*)data)->argv2;int size=((Data*)data)->size;int len=((Data*)data)->len;int fp=-1;//打开被复制文件if((fp=open(argv1,O_RDONLY))==-1){perror("open argv[1] error");return NULL;}int fd=-1;//打开准复制文件if((fd=open(argv2,O_WRONLY|O_CREAT|O_TRUNC,0664))==-1){return NULL;}//移动光标到指定位置lseek(fp,size,SEEK_SET);lseek(fd,size,SEEK_SET);char temp=0;//按给定长度提取字符并写入到文件中for (int i=0; i<len;i++){read(fp,&temp,sizeof(temp));write(fd,&temp,sizeof(temp));}//关闭文件close(fp);close(fd);printf("拷贝成功!\n");}
int main(int argc, const char *argv[])
{//判断传入的文件是否为三个if(argc!=3){perror("file input error");return-1;}//打开被复制文件int fp =-1;if((fp=open(argv[1],O_RDONLY))==-1){perror("open argv[1] error");return-1;}//计算文件大小int size=lseek(fp,0,SEEK_END);//分支线程复制长度int len=size-size/2;//关闭文件close(fp);//定义传入函数的数据变量,不能只定义一个变量,因为传入函数的数据是地址传递,两个线程要调用两次函数,如果一个线程修改变量,另一个也会修改Data data1;Data data2;data1.argv1=argv[1];data1.argv2=argv[2];//分支线程复制位置的值赋给数据结构体data1.size=size/2;//分支线程复制长度的值赋给数据结构体data1.len=len;//创建分支线程pthread_t tid=-1;if(pthread_create(&tid,NULL,copy,&data1)!=0){perror("pthread error");return -1;}//主线程复制需要的数据的值赋给数据结构体data2.argv1=argv[1];data2.argv2=argv[2];data2.size=0;data2.len=size/2;//调用复制函数copy(&data2);//阻塞回收分支线程资源pthread_join(tid,NULL);return 0;
}

思维导图

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

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

相关文章

2024上半年热门内容透视 | 品牌种草解析

2024年上半年&#xff0c;小红书平台“考公上岸”、“不确定性”、“重养自己一遍”、“人生是旷野”、“原生家庭顶配”等话题热议之下&#xff0c;透露着消费者怎样的需求&#xff1f; 综合热门内容及小红书用户的分享发现&#xff0c;变数和不确定性成为新常态&#xff0c;消…

基于OpenCV C++的网络实时视频流传输——Windows下使用TCP/IP编程原理

1.TCP/IP编程 1.1 概念 IP 是英文 Internet Protocol &#xff08;网络之间互连的协议&#xff09;的缩写&#xff0c;也就是为计算机网络相互连接进行通信而设计的协议。任一系统&#xff0c;只要遵守 IP协议就可以与因特网互连互通。 所谓IP地址就是给每个遵循tcp/ip协议连…

3D打印随形透气钢:模具困气终结者

困气是模具经常遇到的问题&#xff0c;是制约生产效率与产品质量的关键因素之一。传统透气钢材料虽有所助益&#xff0c;但其在加工复杂度、形状适应性及性能均衡性上的局限性明显。在此背景下&#xff0c;3D打印技术的革新性应用——随形透气钢应运而生&#xff0c;为困气、排…

NLP与搜广推常见面试问题

1 auc指标 AUC的两种意义 一个是ROC曲线的面积另外一个是统计意义。从统计学角度理解&#xff0c;AUC等于随机挑选一个正样本和负样本时&#xff0c;模型对正样本的预测分数大于负样本的预测分数的概率。下图为搜广推场景下的一个计算auc的例子 2 GAUC指标 就是在推荐系统…

字符设备驱动基础—sys文件系统,udev介绍,驱动模块在内核空间注册设备

文章目录 sys文件系统介绍设计思想应用和功能 udev介绍主要功能工作原理使用 udevadm 工具 设备文件创建流程驱动程序的注册device_create函数详解示例代码效果图 sys文件系统介绍 sysfs 是 Linux 内核中的一种虚拟文件系统&#xff0c;它为用户空间和内核之间提供了一种统一的…

Kafka基本概念,工作流程介绍

1、消息队列与Kafka 1.1、Kafka简介 Kafka使用scala开发&#xff0c;支持多语言客户端&#xff08;c、java、python、go等&#xff09; Kafka最先由LinkedIn公司开发&#xff0c;之后成为Apache的顶级项目。 Kafka是一个分布式的、分区化、可复制提交的日志服务 LinkedIn使…

SpringBoot中的server.context-path

一、问题引入 书接上回&#xff0c;SpringBoot 在 idea中的 .idea和 .iml文件-CSDN博客&#xff0c;我在boot-test的测试项目中使用的 SpringBoot版本为 1.3.5.RELEASE&#xff0c;新项目 cps-task中使用的版本为 2.4.8&#xff0c;造成了连接异常&#xff0c;问题很好解决&…

(20240801)矿山固废基胶凝材料及混凝土中文期刊整理

一、篇名:固废 级别:EI + 篇名:固废混凝土/水泥/胶砂/胶凝材料 级别:EI

二叉树LeetCode热题

94.二叉树的中序遍历 题目 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 输入&#xff1a;root [1,null,2,3]输出&#xff1a;[1,3,2] 代码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …

基于python的百度迁徙迁入、迁出数据分析(六)

书接上回&#xff0c;苏州市我选取了2024年5月1日——5月5日迁入、迁出城市前20名并求了均值&#xff0c;从数据中可以看出苏州市与上海市的关系还是很铁的&#xff0c;都互为对方的迁入、迁出的首选且迁徙比例也接近4分之一&#xff0c;名副其实的老铁了&#xff1b; 迁出城市…

Springboot学习-day16

Springboot学习-day16 Springboot是spring家族中的一个全新框架&#xff0c;用来简化spring程序的创建和开发过程。在以往我们通过SpringMVCSpringMybatis框架进行开发的时候&#xff0c;我们需要配置web.xml&#xff0c;spring配置&#xff0c;mybatis配置&#xff0c;然后整…

鸿蒙应用框架开发【JS注入与执行】 Web

JS注入与执行 介绍 本示例基于H5游戏&#xff0c;通过arkui的button实现对游戏实现基本控制&#xff0c;展示webview的JS注入与执行能力&#xff0c;及native应用与H5的通信能力。 效果预览 使用说明 1.设备连接热点&#xff0c;可访问互联网。 2.打开应用&#xff0c;通过…

RuoYi-3.0代码审计

1 第三方组件漏洞审计 本项目使用Maven构建的。因此我们直接看pom.xml文件引入了哪些组件。通过IDEA打 开该若依&#xff0c;发现本项目采用了多模块方式。因此每个模块下都会有一个pom.xml&#xff0c;项目 最外层的pom.xml为父POM。我们可以通过 pom.xml 或者 External Libr…

【C语言】简易版扫雷游戏(数组、函数的练习)

目录 一、分析和设计 1.1、扫雷游戏的功能分析 1.2、文件结构设计&#xff08;多文件的练习&#xff09; 1.3、数据结构的设计 二、代码 三、效果展示 三、优化 一、分析和设计 1.1、扫雷游戏的功能分析 以在线版的扫雷游戏为参考&#xff0c;分析它的功能&#xff1a;扫…

⌈ 传知代码 ⌋ 基于矩阵乘积态的生成模型

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

钉耙编程(3)

1001深度自同构 Problem Description 对于无向图中的点&#xff0c;定义一个点的度为与其相连的边的条数。 对于一棵有根树&#xff0c;定义一个点的深度为该点到根的距离。 对于由若干有根树构成的森林&#xff0c;定义该森林是深度自同构的&#xff0c;当且仅当森林中任意…

【论文解读|Data Intelligence】 Dr.ICL: Demonstration-Retrieved In-context Learning

论文链接&#xff1a; 来源&#xff1a; Data Intelligence 论文介绍&#xff1a; 该研究由亚利桑那州立大学和谷歌研究团队的专家撰写&#xff0c;深入探讨了通过利用基于检索的方法来提高大型语言模型&#xff08;LLM&#xff09;性能的策略。 主要亮点&#xff1a; • 创…

解开基于大模型的Text2SQL的神秘面纱

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

程序员修炼之路

成为一名优秀的程序员&#xff0c;需要广泛而深入地学习多个领域的知识。这些课程不仅帮助建立扎实的编程基础&#xff0c;还培养了问题解决、算法设计、系统思维等多方面的能力。以下是一些核心的必修课&#xff1a; 计算机基础 计算机组成原理&#xff1a;理解计算机的硬件组…