• 操作系统实验——进程与线程


    目录

    1.使用GCC

    (1)参数

    (2)自定义头文件

    (3)makefile脚本

    (4)gdb调试

    2.进程

    (1)新建进程:fork()

    (2)执行:exec

    (3)消亡:exit()

    (4)阻塞:wait()

    3.线程

    (1)共同操作共享变量

    (2)执行顺序控制


    1.使用GCC

    (1)参数

    gcc test.c -o tested 

    ./tested

    (2)自定义头文件

    gcc testf.c -o test -I fdhead

    -I:指定头文件所在目录,不指定参数将编译出错

    1. #include
    2. #include "math.h"
    3. void main()
    4. {
    5. int x=2,y=4;
    6. printf("please input two numbers:\n");
    7. //scanf("%d,%d",&x,&y);
    8. printf("x*y=%d\n",Fmulitply(x,y));
    9. printf("x! is %f\n",Ffactorial(x));
    10. printf("y! is %f\n",Ffactorial(y));
    11. }
    1. int Fmulitply(int x, int y)
    2. {
    3. int z;
    4. z=x*y;
    5. return z;
    6. }//Fmulitply
    7. float Ffactorial(int t)
    8. {
    9. if (t==1)
    10. return 1;
    11. else
    12. return (float)t*Ffactorial(t-1);
    13. }//Ffactorial

    (3)makefile脚本

    vi makefile

    test:test.c test.h (目标文件:依赖文件)

            gcc test.c –o test (达成目标使用的命令,句子前有一个tab

    make

    或 make test (目标名test与makefile中的要一致)

     myfile:

    1. testf2 : testf.c math.h
    2. gcc testf.c -o testf2
    3. cls :
    4. rm testf2

    除非特殊指定cls,否则只执行第一个,也就是testf2

    (4)gdb调试

    2.进程

    (1)新建进程:fork()

    fork():一次克隆,两个返回值

    父进程返回子进程号,子进程返回0

    例:如下图所示,应有几个进程?

    4个:父,大儿子,二儿子,孙子

    根据pid1和pid2的值可以判断出他们的身份

    1. #include <sys/types.h>
    2. #include <unistd.h>
    3. #include <stdio.h>
    4. #include <stdlib.h>
    5. int main(void){
    6. pid_t pid,pid2;
    7. char *message;
    8. int x;
    9. pid = fork();
    10. pid2 = fork();
    11. if (pid < 0)
    12. { perror("fork failed");
    13. exit(1); }
    14. if (pid == 0 && pid2 > 0)
    15. { message = "This is the child1\n";
    16. x = 10; }
    17. else if (pid > 0 && pid2 == 0)
    18. { message = "This is the child2\n";//父亲第二次克隆孩子
    19. x = 20; }
    20. else if (pid == 0 && pid2 == 0)
    21. { message = "This is the child1-child\n";
    22. x = 30; }
    23. else
    24. { message = "This is the parent\n";
    25. x = 0; }
    26. printf("%s I'm %d, x=%d,my father is:%d\n",message,x,getpid(),getppid());
    27. return 0;
    28. }//main

    (2)执行:exec

    exec是一个函数族,有很多函数,但最后都是下面这个函数执行。

    例:

    1. #include <unistd.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <sys/types.h>
    5. #include <sys/wait.h>
    6. int main(void){
    7. pid_t a;
    8. a = fork();
    9. if (a < 0) {
    10. perror("fork failed");
    11. exit(1); }
    12. if (a == 0) {
    13. printf("child come \n");
    14. execlp ("ps" ,"ps",NULL);
    15. execlp ("ls" ,"ls","-al","/etc/passwd ",NULL);
    16. printf("child exit\n");
    17. return 1; } //if
    18. else {
    19. wait(NULL);
    20. printf("father exit\n");}//else
    21. return 0;
    22. }//main

     

    execlp ("ps" ,"ps",NULL);    
    execlp ("ls" ,"ls","-al","/etc/passwd ",NULL);    

    执行第一行时代码就被新的覆盖了,所以不会再执行第二行。

    只有exec调用失败返回-1,其后的代码才有可能得到执行。

    (3)消亡:exit()

    程序执行结束或调用exit后并不是马上消失,而是变为僵死状态

    想要观察到孩子的僵死态,需要让父亲睡着,否则先祖会来给孩子收尸

    1. #include <sys/types.h>
    2. #include <unistd.h>
    3. #include <stdio.h>
    4. #include <stdlib.h>
    5. int main(void){
    6. pid_t pid;
    7. char *message;
    8. int x;
    9. pid = fork();
    10. if (pid < 0)
    11. { perror("fork failed");
    12. exit(1); }
    13. if (pid == 0)
    14. { message = "This is the child\n";
    15. x = 0; }
    16. else
    17. { message = "This is the parent\n";
    18. x = 10;
    19. sleep(10); }
    20. printf("%s I'm %d, x=%d,my father is:%d\n",message,x,getpid(),getppid());
    21. return 0;
    22. }//main

     &使进程后台执行

    22342就是僵尸进程

    (4)阻塞:wait()

    使调用它的进程等待,进入阻塞状态

    子进程死亡会唤醒阻塞的父进程

    1. include <sys/types.h>
    2. #include <unistd.h>
    3. #include <stdio.h>
    4. #include <stdlib.h>
    5. int main(){
    6. pid_t pc1,pc2,pw1,pw2;
    7. pc1=fork();
    8. pc2=fork();
    9. if (pc1>0 && pc2>0) { //*父进程
    10. pw1=wait(NULL);
    11. pw2=wait(NULL);
    12. printf("***Catch a dead child process with pid: %d\n",pw1);
    13. printf("***Catch a dead child process with pid: %d\n",pw2);
    14. printf("***I'M %d,THE MAIN PROCESS LEAVE!\n",getpid()); }//if
    15. if (pc1==0&&pc2>0) { //*大儿子
    16. printf("===I'M the first child PID:%d,my father is:%d\n",getpid(),getppid());
    17. sleep(10); } //if
    18. if (pc1>0&&pc2==0) //*二儿子
    19. printf("===I'M the 2nd child PID:%d,my father is:%d,i don't sleep.\n",getpid(),getppid());
    20. if (pc1==0&&pc2==0) //*孙进程
    21. printf("I'M grandson PID:%d,my father is:%d,no one is waiting for me.\n",getpid(),getppid());
    22. exit(0);
    23. } //main

    创建出4个进程后,父亲阻塞

    大儿子输出以后,沉睡10s

    二儿子输出然后死亡,唤醒父亲一次

    孙子输出然后死亡,但是唤醒不了大儿子,因为大儿子是sleep而不是wait

    直到10s后大儿子死亡,父亲才被真正唤醒,输出三句话。

    3.线程

    由于pthread库不是Linux系统默认的库,需要使用库libpthread.a,所以,编译连接时,命令后需加“-l pthread” 参数帮助编译器找到相应的库。

    需要注意:线程可以创建新线程,两个线程是等同层次,不存在亲缘关系。

    #include

    创建线程函数:pthread_create

    int pthread_create(pthread_t* tid,const pthread_attr_t* attr,void* (*start_rtn),void *restrict arg);

    成功返回0,失败返回错误号

    (1)tid:事先创建好的pthread_t类型的线程标识符,整型。成功时tid指向的内存单元被设置为新创建线程的线程ID。

    (2)attr:设置线程属性,通常设为NULL。

    (3)start_rtn:新创建的线程从此函数开始运行,无参数时设为NULL。

    (4)arg:start_rtn函数的参数,无参数时设为NULL,有参数时输入参数的地址。当多于一个参数时使用结构体传入。
     

    终止线程: pthread_cancel、pthread_exit

    void pthread_exit(void *retval):retval表示线程退出状态,通常传NULL,终止自己。

    int pthread_cancel(pthread_t tid):终止同进程中的另一个线程,tid是线程标识符。成功返回0,失败返回错误号

    线程等待:pthread_join

    pthread_join(tid,void * *retval):主线程等待子线程tid的终止,reval根据终止状态返回不同值。

    return返回:reval为返回值

    pthread_cancel终止:reval为PTHREAD_CANCEL常量

    pthread_exit终止:pthread_exit的参数

    对终止状态不感兴趣传NULL

    (1)共同操作共享变量

    例1:

    ①线程共享资源: 共同影响进程的message变量

    ②并发执行: 交替输出message当前的值

    1. #include <unistd.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. //#include <pthread>
    5. //5-10
    6. char message[50] = "Hello World";
    7. //线程共享,初值为helloworld
    8. void *thread_function(void *arg){
    9. int k=0;
    10. printf("===Thread_function1 is running. Argument was %s\n", (char *)arg);
    11. //注意此处循环设置过少会使线程执行时间短,测试体现不出并发效果
    12. for(k=0;k<10;k++) {
    13. sleep(rand()%3);
    14. strcpy(message, "THREAD!"); }//for
    15. pthread_exit("===Thank you for your CPU time!"); }
    16. void *thread_function2(void *arg){
    17. int k=0;
    18. printf("===Thread_function2 is running. Argument was %s\n", (char *)arg);
    19. for(k=0;k<10;k++) {
    20. sleep(rand()%3);
    21. strcpy(message,"ANOTHER THREAD!"); }//for
    22. pthread_exit("===Thank you for your CPU time!"); }
    23. int main(){
    24. int res,k;
    25. pthread_t thread1;
    26. pthread_t thread2;
    27. void *thread_result;
    28. res = pthread_create(&thread1, NULL, thread_function, (void *)message);
    29. res = pthread_create(&thread2, NULL, thread_function2, (void *)message);
    30. for(k=0;k<20;k++) {
    31. sleep(1);
    32. printf("Message is now %s\n", message);
    33. } //for
    34. exit(EXIT_FAILURE);
    35. }

    例2:两个线程对共享的进程变量做加1操作,结果加和是错的。

    1. #include <unistd.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>
    5. #include <pthread.h>
    6. long x=0,y=0,z=0;
    7. void *thread_function1()
    8. {
    9. printf("thread_function1 is running \n");
    10. while(1)
    11. {
    12. x++;
    13. z++;
    14. }
    15. pthread_exit("thank you for your cpu time");
    16. }
    17. void *thread_function2()
    18. {
    19. printf("thread_function2 is running \n");
    20. while(1)
    21. {
    22. y++;
    23. z++;
    24. }
    25. pthread_exit("thank you for your cpu time");
    26. }
    27. int main(){
    28. int res;
    29. pthread_t thread1;
    30. pthread_t thread2;
    31. res=pthread_create(&thread1,NULL,thread_function1,NULL);//创建子线程
    32. res=pthread_create(&thread2,NULL,thread_function2,NULL);
    33. sleep(1);
    34. printf(" x= %d\n",x);
    35. printf(" y= %d\n",y);
    36. printf(" x+y = %d\n",x+y);
    37. printf(" z= %d\n",z);
    38. exit(EXIT_FAILURE);
    39. }

    按理说x+y=z,但是结果不对。

    线程对共享变量的处理互斥,否则会出现线程不安全问题 

    (2)执行顺序控制

    1. #include <unistd.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>
    5. #include <pthread.h>
    6. //5-11
    7. char message[50] = "Hello World";
    8. void *thread_function(void *arg) {
    9. printf("===CHILD run and want to sleep(5). message NOW is %s\n", (char *)arg);
    10. sleep(5);
    11. strcpy(message, "HELLO FATHER!");
    12. pthread_exit("===Thank you for your CPU time!");
    13. }
    14. int main(){
    15. int res;
    16. pthread_t threadCH;
    17. void *thread_result;
    18. res=pthread_create(&threadCH,NULL,thread_function,(void *)message);
    19. if(res!=0){
    20. perror("thread creation failed");
    21. exit(EXIT_FAILURE);
    22. }
    23. printf("Main thread is waiting for thread to finish by join \n");
    24. res=pthread_join(threadCH,&thread_result);
    25. if(res!=0){
    26. perror("thread join failed\n");
    27. exit(EXIT_FAILURE);
    28. }
    29. printf("child thread returned %s\n",(char *)thread_result);
    30. printf("message now is %s\n",message);
    31. exit(EXIT_FAILURE);
    32. }

  • 相关阅读:
    保姆级服务器安装宝塔面板教程
    洛谷每日三题之第四天
    C# 什么是继承和派生
    23王道考研操作系统目录一览
    MySQL 中的事务理解
    leetcode - 学习计划之剑指offer
    【LeetCode: 137. 只出现一次的数字 II | 位运算 | 哈希表】
    短视频引爆销售:TikTok如何改变跨境电商游戏规则
    Spring事务及分布式事务专题
    keepalived识别MGR主节点
  • 原文地址:https://blog.csdn.net/qq_51165184/article/details/127831669