当前位置:首页 » 《随便一记》 » 正文

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

19 人参与  2022年11月30日 15:45  分类 : 《随便一记》  评论

点击全文阅读



文章目录

进程与线程实验一:创建进程基础版:创建父子线程 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.croot@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.croot@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.croot@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread4 /home/course/linux/createThread4.c -l pthreadroot@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.croot@ubuntu:/home/course/linux# gcc -o /home/course/linux/out/createThread5 /home/course/linux/createThread5.c -l pthreadroot@ubuntu:/home/course/linux# ./out/createThread5 ./a.txt ./b.txt total_words=5total_words=3

运行结果:

image-20221129173907407

返回顶部



点击全文阅读


本文链接:http://zhangshiyu.com/post/49295.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1