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




    进程与线程

    实验一:创建进程

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

    注意:

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

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

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

    基础版:创建父子线程 fork

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    编译源文件:

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

    image-20221129143809506

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

    image-20221129144124561

    可以看到:

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

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

    image-20221129144641728

    返回顶部


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

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

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    编译运行:

    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
    
    • 1
    • 2

    image-20221129150054672

    返回顶部


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

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

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    可以看到编译正常运行;

    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 
    #include 
    #include 
    #include 
    #include 
    #include
    #include
    
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    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
    
    • 1
    • 2
    • 3

    运行结果:

    image-20221129171216972

    返回顶部


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

    实验说明:

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

    解决方案:

    • 在进程中定义共享数据,在线程中直接引用并输出该数据。
    image-20221129171410972
    #include 
    #include 
    #include 
    #include 
    #include 
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    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
    
    • 1
    • 2
    • 3

    运行结果:

    在这里插入图片描述

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

    返回顶部


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

    实验说明:

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

    解决方案:

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

    image-20221129172729652

    #include 
    #include 
    #include 
    #include 
    #include 
     
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    创建两个包含英文单词的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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果:

    image-20221129173907407

    返回顶部


  • 相关阅读:
    Python网络爬虫第二课----requests模块应用
    linux安装redis哨兵
    设计模式——工厂模式
    c语言编程题经典100例——(41~45例)
    平时积累的FPGA知识点(7)
    03-JS循环语句
    //路径累加和
    【环境搭建-04】CentOS上搭建Vulhub靶场
    Test Module的创建及使用
    linux xhost命令
  • 原文地址:https://blog.csdn.net/qq_45797116/article/details/128102512