【Ubuntu】进程与线程编程实验

news2024/10/6 6:41:44

文章目录

  • 进程与线程
    • 实验一:创建进程
      • 基础版:创建父子线程 fork
      • 基础版:父子线程交替运行
      • 基础版:创建进程 文件写入
      • 练习版:创建线程 子读父阻塞
    • 实验二:线程共享进程中的数据
    • 实验三:多线程实现单词统计工具


进程与线程

实验一:创建进程

1、学会通过基本的linux进程控制函数,由父进程创建子进程,并实现协同工作
2、创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行

注意:

  • fork创建的新进程被称为子进程,该函数被调用一次,但返回两次。两次返回的区别是:在子进程中的返回值是0,而在父进程中的返回值则是新进程的进程ID。

  • 创建子进程,父进程哪个先运行根据系统调度且赋值父进程的内存空间。

  • vfork创建子进程,但子进程先运行且不复制父进程的内存空间

基础版:创建父子线程 fork

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){

        printf("pid:%d\n",getpid());

        pid_t pid;
        pid = fork(); // 创建子进程

        // 在fork之后会运行两个进程(父进程、子进程)
        if(pid <0){
                perror("fork error");
        } else if(pid>0){
                // 父进程(在父进程中fork返回的是子进程的pid)
                printf("I am parent process pid is %d,ppid is %d,fork return is %d\n",
                       getpid(),getppid(),pid);
        } else { 
                // 子进程(在子进程中fork返回的是0)    
                printf("I am child  process pid is %d,ppid is %d,fork return is %d\n",
                       getpid(),getppid(),pid); 
        }

        printf("pid:%d\n",getpid());

        sleep(1); // 睡眠
        
        return 0;
}

编译源文件:

root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread /home/course/linux/createThread.c
  • -c 表示只编译(compile)源文件但不链接,会把.c或.cc的c源程序编译成目标文件,一般是.o文件。
  • -o 用于指定输出(out)文件名。不用-o的话,一般会在当前文件夹下生成默认的a.out文件作为可执行程序。

image-20221129143809506

root@ubuntu:/home/course/linux/# out/createThread  # 直接运行

image-20221129144124561

可以看到:

  • 父进程(在父进程中fork返回的是子进程的pid)

  • 子进程(在子进程中fork返回的是0)

image-20221129144641728

返回顶部


基础版:父子线程交替运行

使用sleep()函数,实现线程的睡眠,每个进程运行后休眠一段时间,这时按照cpu的资源调度,使得其他进程运行。(若休眠时间短则会出现二次调用的情况)

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){

        printf("pid:%d\n",getpid());

        pid_t pid;
        pid = fork(); // 创建子进程

        // 在fork之后会运行两个进程(父进程、子进程)
        if(pid <0){
                perror("fork error");
        } else if(pid>0){
            for(int i=0;i<10;i++){
                // 父进程(在父进程中fork返回的是子进程的pid)
                printf("I am parent process pid is %d\n",getpid());
                sleep(1);
            }
        } else { 
            for(int i=0;i<10;i++){
                // 子进程(在子进程中fork返回的是0)    
                printf("I am child  process pid is %d\n",getpid()); 
                sleep(1);
            }
        }
        
        return 0;
}

编译运行:

root@ubuntu:/home/course/linux# vi createThread1.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread1 /home/course/linux/createThread1.c

image-20221129150054672

返回顶部


基础版:创建进程 文件写入

父进程使用两种IO的形式进行文件的写入,默认当前路径下创建文件。注意区分缓存的概念以及文件的内容输出。

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){

        printf("pid:%d\n",getpid());

    	/*父进程调用写文件*/
    	FILE *fp =fopen("s.txt","w");
        int fd = open("s_fd.txt",O_WRONLY|O_CREAT|O_TRUNC,S_IRWXU|S_IRWXG);
    	char *s = "hello world!";
    	ssize_t size = strlen(s)*sizeof(char);
    	// 标准IO函数 - 带缓存 - 全缓存
    	fprintf(fp,"s:%s,pid:%d",s,getpid());
    	// 内核提供的IO系统 - 不带缓存
    	write(fd,s,size);
    
        pid_t pid;
        pid = fork(); // 创建子进程

        // 在fork之后会运行两个进程(父进程、子进程)
        if(pid <0){
                perror("fork error");
        } else if(pid>0){
            for(int i=0;i<10;i++){
                // 父进程(在父进程中fork返回的是子进程的pid)
                printf("I am parent process pid is %d\n",getpid());
                sleep(1);
            }
        } else { 
            for(int i=0;i<10;i++){
                // 子进程(在子进程中fork返回的是0)    
                printf("I am child  process pid is %d\n",getpid()); 
                sleep(1);
            }
        }
    	// 父子进程都要执行 - 写入各自缓存
    	fprintf(fp,"pid:%d",getpid());
        
        return 0;
}

可以看到编译正常运行;

image-20221129161536294

并且在目录下新生成了 s_fd.txt、s.txt 文件,当我们查看文件内容的时候,会发现两个文件中的内容有偏差:

image-20221129161812264

使用内核提供的IO系统 - 不带缓存,是直接将内容写入,而标准IO函数 - 带缓存,写的内容是:fprintf(fp,"s:%s,pid:%d",s,getpid());,并且在最后的时候父子进程都要执行一次标准的IO,將各自的缓存内容写入到文件中去,所以会重复内容一次。

image-20221129163037732

返回顶部


练习版:创建线程 子读父阻塞

实验说明:

  • 学会通过基本的Linux进程控制函数,由父进程创建子进程,并实现协同工作。创建两个进程,让子进程读取一个文件,父进程等待子进程读完文件后继续执行。

解决方案:

  • 进程协同工作就是要协调好两个或两个以上的进程,使之安排好先后次序并依次执行,可以用wait()或者waitpid()函数来实现这一点。当只需要等待任一子进程运行结束时,可在父进程中调用wait()函数。若需要等待某一特定子进程的运行结果时,需调用waitpid()函数,它是非阻塞型函数。
image-20221129164336225
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

#define COLMAX 1024 //每一个字符串的最大长度(列)
#define ROWMAX 64   //字符串最大个数(行)

/*
本代码实现用子进程打开同目录下的s_fd.txt文件
并且父进程输出内容
*/

int main(void) {
    int p_id = -1;
    //子进程创建失败
    if ((p_id = fork()) == -1) {
        printf("Process_1 Create Error\n");
    } else if (p_id == 0) { //子进程部分
        printf("%d Process Start Work\n", getpid());
        char text[ROWMAX][COLMAX] = {0};
        FILE *fp = fopen("s_fd.txt", "r+");//打开文件
        if (fp == NULL) { //打开文件失败
            printf("Fail to open file!\n");
        } else {
            int i = 0;
            while ((fscanf(fp, "%s", text[i])) != EOF) {
                printf("%s\n", text[i]);
                i++;
                sleep(1);   //等待1s方便查看输出
            }
        }
        fclose(fp);
        exit(0);
    }
    //父进程部分
    waitpid(p_id, NULL, 0);//阻塞等待

    printf("%d process is end\n", p_id);

    return 0;
}
rse/linux# vi createThread3.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread3 /home/course/linux/createThread3.c 
root@ubuntu:/home/course/linux# out/createThread3

运行结果:

image-20221129171216972

返回顶部


实验二:线程共享进程中的数据

实验说明:

  • 了解线程与进程之间的数据共享关系。创建一个线程,在线程中更改进程中的数。

解决方案:

  • 在进程中定义共享数据,在线程中直接引用并输出该数据。
image-20221129171410972
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

static int sharedata=4; // 共享数据

void *create(void *arg){
    printf("new pthread...\n");
    printf("sharedata data = %d \n",sharedata);
    sharedata = 3;
    return (void *)(0);
}

int main(void){

    pthread_t mythread ;
    sharedata=5; // 修改变量值
    int error = 0;
    error = pthread_create(&mythread,NULL,create,NULL);

    if(error){
        printf("pthread_create is not created...\n");
        return -1;
    }
    sleep(1);
    printf("pthread_create is ok...\n");
    printf("And shared data = %d\n \n",sharedata);

    return 0;
}
root@ubuntu:/home/course/linux# vi createThread4.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread4 /home/course/linux/createThread4.c -l pthread
root@ubuntu:/home/course/linux# out/createThread4

运行结果:

在这里插入图片描述

如有报错,参见:https://blog.csdn.net/u014470361/article/details/83214911

返回顶部


实验三:多线程实现单词统计工具

实验说明:

  • 多线程实现单词统计工具。

解决方案:

  • 区分单词原则:
    • 凡是一个非字母或数字的字符跟在字母或数字的后面,那么这个字母或数字就是单词的结尾。
    • 允许线程使用互斥锁来修改临界资源,确保线程间的同步与协作。
    • 如果两个线程需要安全地共享一个公共计数器,需要把公共计数器加锁。线程需要访问称为互斥锁的变量,它可以使线程间很好地合作,避免对于资源的访问冲突。

image-20221129172729652

#include <stdio.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
 
pthread_mutex_t counter_clock=PTHREAD_MUTEX_INITIALIZER;
 
int main(int ac,char *av[]){
  void *count_words(void *);
  if(ac!=3){
    printf("Usage:%s file1 file2\n",av[0]);
    exit(1);
  }
  /*分別以av[1]、av[2]作为参数,创建两个线程t1、t2,线程t1、t2进入等待状态,输出统计的单词总数*/
  pthread_t tidp1,tidp2;
  int error1,error2;
  error1=pthread_create(&tidp1,NULL,count_words,av[1]);
  error2=pthread_create(&tidp2,NULL,count_words,av[2]);
  pthread_join(tidp1,NULL);
  pthread_join(tidp2,NULL);
  return 0;
}
 
void *count_words(void *f){
  char *filename=(char *)f;
  FILE *fp;
  int c,prevc='\0';
  int total_words=0;
 
  if((fp=fopen(filename,"r"))!=NULL){
    while((c=getc(fp))!=EOF){
      if(!isalnum(c) && isalnum(prevc)){
         pthread_mutex_lock(&counter_clock);
         total_words++;
         pthread_mutex_unlock(&counter_clock);
       } 
       prevc=c;
    }
    fclose(fp);
    printf("total_words=%d\n",total_words);
  }else{
        perror(filename);
  }
  return NULL;
}

创建两个包含英文单词的txt文件:

image-20221129173438945

root@ubuntu:/home/course/linux# vi createThread5.c
root@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread5 /home/course/linux/createThread5.c -l pthread
root@ubuntu:/home/course/linux# ./out/createThread5 ./a.txt ./b.txt 
total_words=5
total_words=3

运行结果:

image-20221129173907407

返回顶部


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

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

相关文章

总结709(bug集合)

今天不知道怎么了&#xff0c;那个曾弄了我20多天的冒险模块&#xff0c;在今天&#xff0c;差不多要完工了。弄出的那一刻&#xff0c;有的是喜悦&#xff0c;但之后&#xff0c;更多的是叹气。 也就是从此刻我才意识到&#xff0c;我曾自认为很崇高的职位&#xff0c;可能事…

传奇出现黑屏卡屏不动是怎么回事

在写这篇文章之前&#xff0c;先给给大家说一下&#xff0c;这篇文章写的是出现黑屏、卡屏不动是我们玩传奇的时候出现的&#xff0c;而不是在架设传奇时候出现的&#xff0c;所以要特别是注意一下&#xff0c;架设和玩出现黑屏是完全不一样的&#xff0c;所以解决方案也不一样…

驱动开发 platfrom总线驱动的三种方式

驱动的分隔与分离&#xff1a; 对于 Linux 这样一个成熟、庞大、复杂的操作系统&#xff0c;代码的重用性非常重要&#xff0c;在驱动程序&#xff0c;因为驱动程序占用了 Linux 内核代码量的大头&#xff0c;如果不对驱动程序加以管理&#xff0c;任由重复的代码肆意增加&…

基于深度学习的合成孔径雷达自聚焦

文章目录引言什么是合成孔径雷达什么是自聚焦经典自聚焦方法基于机器学习的方法基于极速学习机的方法基于深度学习的SAR自聚焦代码附录引言 本文全面介绍合成孔径雷达自聚焦概念和方法。想获取更为详尽的描述&#xff0c;请参考以下几篇论文, 如果数据或代码对你的研究有用&am…

【ASM】字节码操作 工具类与常用类 ClassRemapper 介绍 类映射 代码混淆

文章目录 1.概述2. ClassRemapper#2.1 class info2.2 fields2.3 构造方法2.4 method3. 案例3.1 案例1-修改类名3.2 案例2-修改字段名和方法3.3 案例3-修改2个类4.总结1.概述 在上一篇文章:【ASM】字节码操作 工具类与常用类 InstructionAdapter 介绍 我们知道了,对于Instruc…

[思维模式-5]:《如何系统思考》-1- 认识篇 - 总体结构与知识框架

目录 前言&#xff1a; 第一篇&#xff1a;认知篇 第1章 无所不在的系统 // 2 第2章 思维的转变 // 30 第二篇&#xff1a;方法与工具 第3章 深入思考 // 50 第4章 动态思考 // 78 第5章 全面思考 // 109 第6章 系统思考的“新语言”&#xff1a;因果回路图 // …

Zookeeper(一)- Zookeeper介绍与集群部署

文章目录一、Zookeeper 介绍1. Zookeeper概述2. Zookeeper工作机制3. Zookeeper特点4. Zookeeper数据结构5. Zookeeper应用场景&#xff08;1&#xff09;统一命名服务&#xff08;2&#xff09;统一配置管理&#xff08;3&#xff09;统一集群管理&#xff08;4&#xff09;服…

翻译QT使用手册:将库添加到项目

将库添加到项目 除了 Qt 库之外&#xff0c;您还可以将其他库添加到您的项目中。该过程取决于图书馆的类型和位置。您可以添加系统库、您自己的库或第三方库。该库可以位于当前项目的构建树中&#xff0c;也可以位于另一个构建树中。 将库添加到项目 除了 Qt 库之外&#xff0c…

远距离双目视觉测量系统纵深方向测量精度较低原因分析

两台相机基线距离约1200mm&#xff0c;对20m外的一个LED发光点进行持续观测&#xff0c;效果如下视频所示&#xff1a; 可见ZZZ方向的重复性精度比较差&#xff0c;波动量甚至多于2mm了&#xff0c;而以10mm导轨基准距离为基准&#xff0c;精度测试结果也比较差&#xff0c;如…

[附源码]计算机毕业设计springboot个性化产品服务管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

xss-labs/level11

首先输入 <script>alert(xss)</script> 丝毫不差 没有出现回弹现象 根本就不出人意料好吧 接着来看一下源代码好吧 能够看得出来第一个输出点由于htmlspecialchars转义操作 所以上述代码根本行不通 这一关比上一关又多了一个隐藏表单 一共是有四个隐藏表单 从…

C++入门笔记

C 入门笔记Functions in CC header Files下面主要是我学习C的一个笔记&#xff0c;记录学习中遇到的一些重点事项。下面是视频的连接https://www.bilibili.com/video/BV1Ay4y1i7Z6/?p10&spm_id_from333.1007.top_right_bar_window_history.content.click&vd_sourcee6e…

单连通图的判断

单连通图的判断算法&#xff1a; &#xff08;1&#xff09;对每个点进行dfs得到一棵dfs树&#xff1b; &#xff08;2&#xff09;判断是否存在前向边和横向边&#xff0c;若有则必定存在两个点之间有至少2条简单路径&#xff0c;因此该图不属于单连通图&#xff1b; &#xf…

一个开源且完全自主开发的国产网络协议栈

已剪辑自: https://mp.weixin.qq.com/s/_1LE7mGc9mRuajRgNsyirQ onps是一个开源且完全自主开发的国产网络协议栈&#xff0c;适用于资源受限的单片机系统&#xff0c;提供完整地ethernet/ppp/tcp/ip协议族实现&#xff0c;同时提供sntp、dns、ping等网络工具&#xff0c;支持以…

mac pro M1(ARM)安装:.Net、C#开发环境

0.引言 因为.Net是开发、运行环境&#xff0c;而C#是一个编程语言&#xff0c;专用于.Net平台&#xff0c;C#是运行在.Net之上的&#xff0c;所以两者的安装是密不可分的 1. 安装 1、因为arm架构安装Visual Studio 2022 for mac可以自动附带安装.Net&#xff0c;所以我们只需…

刷题之完全二叉树的权值和小字辈及根据后序和中序遍历输出先序遍历

1、完全二叉树的权值 1&#xff09;题目 给定一棵包含 N 个节点的完全二叉树&#xff0c;树上每个节点都有一个权值&#xff0c;按从 上到下、从左到右的顺序依次是 A1, A2, AN&#xff0c;如下图所示&#xff1a; 现在小明要把相同深度的节点的权值加在一起&#xff0c;他想…

开关电源32个检测项目、检测方法与检测设备(下)

开关电源是电器必备&#xff0c;本文详细介绍了开关电源的32个常见检测项目、检测方法与仪器&#xff0c;内容较多&#xff0c;前些日子介绍了上部分16个项目&#xff08;点击阅读&#xff09;&#xff0c;今天介绍剩下的16个项目。抱歉久等了&#xff01; 过压保护测试 一、目…

MySQL数据库事务控制

目录 一、事务控制概念 二、事务控制特性 三、事务控制相关命令 1、测试begin和commit&#xff08;开始事务和提交事务&#xff09; 2、测试事务回滚rollback 四、总结 1、事务的ACID特性 2、事务之间的相互影响 3、事务的隔离级别 一、事务控制概念 在MySQL中用于处理…

上海大治河二线船闸总体设计与结构计算

目录 第一章 设计基本资料 1 1.1工程概况 1 1.2 货运量预测 1 1.3 建筑物设计等级 1 1.4 通航情况及计算参数的选取 1 1.5 设计船型 2 1.6 特征水位表 2 1.7 水文气象资料 3 1.8 工程地质 3 第二章 总体设计 4 2.1 船闸地理位置确定 4 2.2 船闸线、级数确定 5 2.3 船闸的基本尺…

【能效管理】变电所运维云平台在上海某医院的设计分析

摘要&#xff1a;本文概述了变电所电力运维技术&#xff0c;分析了医院变电所中存在的技术设备老化和技术荷载不足的技术性问题&#xff0c;并从主变低压进出线路监测故障、环境监测故障、设备档案记录、运维排班记录、分析报告五个方面探讨了变电所电力运维技术的具体应用。变…