基础操作系统-进程-并发程序设计

这学期感觉最有难度的就是操作系统这门课了,以前Linux接触的不多,双学位跳级学习课程暂时还没有学完其中的基础课。所以可能错误多一些。

1、  相关头文件

<unistd.h> <sys/types.h> <sys/wait.h> <stdio.h> <stdlib.h>

2、  函数说明

l  fork(建立一个新的进程)

定义函数  pid_t fork(void);

函数说明  fork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。

返回值    如果fork()成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回0。如果fork 失败则直接返回-1,失败原因存于errno中。

l  waitpid(等待子进程中断或结束)

定义函数  pid_t waitpid(pid_t pid,int * status,int options);

函数调用  waitpid(pid, NULL, 0);

函数说明  waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。参数pid为欲等待的子进程识别码,其他数值意义如下:

pid<-1 等待进程组识别码为pid绝对值的任何子进程。

pid=-1 等待任何子进程,相当于wait()。

pid=0 等待进程组识别码与目前进程相同的任何子进程。

pid>0 等待任何子进程识别码为pid的子进程。

返回值  如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno

l  getpid(取得进程识别码) 

定义函数  pid_t getpid(void);

函数说明  getpid()用来取得目前进程的进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。

返回值    目前进程的进程识别码

l  exit(正常结束进程) 

定义函数  void exit(int status);

函数说明  exit()用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。

l  execl(执行文件)

定义函数  int execl(const char * path,const char * arg,....);

函数说明  execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。

返回值    如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。

调用ls命令范例: execl("/bin/ls", "/bin/ls",  "-l" , "/etc", NULL);

 安装VMware Workstation Pro,下载Fedora Linux镜像文件,安装成功后如下图。打开终端,输入命令su进入管理员账户,输入yum install gcc 安装gcc编译器,测试编译环境正常

单个子进程的创建分析:通过gcc first.c进行编译,在.a/.out执行输出,例程刚开始输出父进程pid 2218

#include<unistd.h>

#include<stdarg.h>

#include<time.h>

#include<sys/types.h>

#include<sys/wait.h>

#include<stdio.h>

#include<stdlib.h>

int tprintf (const char*fmt, ...);

int main(void)

{

       int i=0 ,j=0;

       pid_t pid;

       printf("Hello from Parent Process, PID is %d. ", getpid());

  1. fork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。因为父与子进程的pid值不一样,可以分别对父进程和子进程做操作,当pid为0时可以对子进程做操作,当pid>0时对父进程做操作,当pid为-1时创建进程失败输出提示信息;

       pid = fork();

       if(pid == 0) //child process

       {

              sleep(1);

              for(i=0;i<3;i++)

              {

                     printf("Hello from Child Process %d. %d times ", getpid(), i+1);

                     sleep(1);

              }

       }

       else if(pid != -1) //parent process

       {

              tprintf("Parent forked one child process--%d. ",pid);

              tprintf("Parent is waiting for child to exit. ");

              waitpid(pid,NULL,0);

              tprintf("Child Process has exited. ");

              tprintf("Parent had exited. ");

       }

       else

       {

              tprintf("Everything was done without error. ");

       }

       return 0;

}

waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。

int tprintf(const char*fmt, ...)

{

       va_list args;

       struct tm *tstruct;

       time_t tsec;

       tsec = time(NULL);

       tstruct = localtime(&tsec);

       printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());

       va_start(args,fmt);

       return vprintf(fmt,args);

}

当pid=-1时创建进程失败输出错误时间、PID等错误信息。最后运行结果如下:

 

在子进程中调用外部命令

#include<unistd.h>

#include<stdarg.h>

#include<time.h>

#include<sys/types.h>

#include<sys/wait.h>

#include<stdio.h>

#include<stdlib.h>

int tprintf (const char*fmt, ...);

int main(void)

{

       pid_t pid;

       pid = fork();

       if(pid == 0) //child process

       {

              sleep(5);

              tprintf("Hello from Child Process! ");

              tprintf("I am calling exec. ");

              execl("/bin/ps","-a",NULL);

              tprintf("You should never see this because the child is already gone. ");

       }

int execl(const char * path,const char * arg,....)用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。返回值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中;

       else if(pid != -1) //parent process

       {

              tprintf("Hello from Parent,pid %d. ", getpid());

              sleep(1);

              tprintf("Parent forked process %d. ",pid);

              sleep(1);

              tprintf("Parent is waiting for child to exit. ");

              waitpid(pid,NULL,0);

              tprintf("Parent had exited. ");

       }

sleep是让线程指定休眠时间,然后继续工作  wait则是等待,直到有线程通知notify()唤醒他才会重新工作;

       else

       {

              tprintf("Everything was done without error. ");

       }

       return 0;

}

int tprintf(const char*fmt, ...)

{

       va_list args;

       struct tm *tstruct;

       time_t tsec;

       tsec = time(NULL);

       tstruct = localtime(&tsec);

       printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());

       va_start(args,fmt);

       return vprintf(fmt,args);

}

 

编写一段程序实现以下功能:

使用系统调用fork()创建两个子进程

各个子进程显示和输出一些提示信息和自己的进程标识符。

父进程显示自己的进程ID和一些提示信息,然后调用waitpid()等待多个子进程结束,并在子进程结束后显示输出提示信息表示程序结束。

#include<unistd.h>

#include <stdarg.h>

#include <time.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <stdio.h>

#include <stdlib.h>

int tprintf(const char*fmt,...);

int main()

{

       int i=0,j=0;

       pid_t pid1;

       pid_t pid2;

       printf("Hello from Parent Process,PID is %d. ",getpid());

       pid1 = fork();

       if(pid1 == 0)

       {

              sleep(1);

              for(i=0;i<3;i++)

       {

          printf("Hello from Child Process one %d. %d times ",getpid(),i+1);

          sleep(1);

       }

       }

       else if(pid1 !=-1)

       {

              tprintf("Part forked one child process--%d. ",pid1);

              tprintf("Parent is waiting for child to exit. ");

              waitpid(pid1,NULL,0);

              pid2 = fork();

              if(pid2 == 0)

              {

                     sleep(1);

                     for(i=0;i<3;i++)

                     {

                            printf("Hello from Child Process two %d. %d times ",getpid(),i+1);

                            sleep(1);

                     }

              }

              else if(pid2 !=-1)

              {

                     tprintf("Part forked one child process--%d. ",pid2);

                     tprintf("Parent is waiting for child to exit. ");

                     waitpid(pid2,NULL,0);

                     tprintf("Child Process has exited. ");

                     tprintf("Parent had exited. ");

              }      

       }

       else  tprintf("Everything was done without error. ");  

       return 0;

}

int tprintf(const char*fmt,...)

{

       va_list args;

       struct tm *tstruct;

       time_t tsec;

       tsec = time(NULL);

       tstruct = localtime (&tsec);

       printf("%02d:%02d:%02d:%5d",tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec,

       getpid());

       va_start(args,fmt);  

       return vprintf(fmt,args);

}

通过父进程创建一个子进程,运行一个子进程,再创建一个子进程,再运行一个子进程的方式,输出各个进程的标识码,可以得到正确的结果。而当两个子进程同时fork()时,会产生3个子进程,这是应为2个fork()放在一起时,会多fork()一个子进程。

 创建多个(3个以上)进程并发运行,控制好各个子进程输出自己的进程标识符和一些提示信息

#include<unistd.h>

#include<stdarg.h>

#include<time.h>

#include<sys/types.h>

#include<sys/wait.h>

#include<stdio.h>

#include<stdlib.h>

int tprintf (const char*fmt, ...);

int main(void)

{

       int i=0 ,j=0;

       pid_t pid,pid1,pid2;

       printf("Hello from Parent Process, PID is %d. ", getpid());

       pid = fork();//fork1

       if(pid == 0) //child process 1

       {

              sleep(1);

              for(i=0;i<3;i++)

              {

                     printf("Hello from Child Process 11111 %d. %d times ", getpid(), i+1);

                     sleep(5);

              }

       }

       else if(pid != -1) //parent process

       {

              tprintf("Parent forked 111 child process--%d. ",pid);

                pid1 = fork();//fork2

                if(pid1 == 0) //child process 2

                 {

                     sleep(1);

                        for(i=0;i<3;i++)

                        {

                              printf("Hello from Child Process 22222 %d. %d times ", getpid(), i+1);

                              sleep(5);

                        }

                 }

                 else if(pid1 != -1) //parent process

                 {

                     tprintf("Parent forked 222 child process--%d. ",pid);

                                   

                     pid2 = fork();//fork3

                     if(pid2 == 0) //child process 3

                     {

                           sleep(1);

                           for(i=0;i<3;i++)

                           {

                                  printf("Hello from Child Process 33333 %d. %d times ", getpid(), i+1);

                                  sleep(5);

                           }

                      }

                      else if(pid2 != -1) //parent process

                      {

                            tprintf("Parent forked 333 child process--%d. ",pid);

                         //wait for children

                         pid_t temp_p;

                         while((temp_p = waitpid(-1, NULL, 0)) > 0)

                         {

                            tprintf("child had exited %d ",temp_p);

                         }

                            tprintf("Parent had exited. ");

                      }

                      else

                      {

                           tprintf("3333 Everything was done without error. ");

                      }

                 }

                 else

                 {

                      tprintf("2222 Everything was done without error. ");

                 }

       }

       else

       {

              tprintf("1111 Everything was done without error. ");

       }

           

       return 0;

}

int tprintf(const char*fmt, ...)

{

       va_list args;

       struct tm *tstruct;

       time_t tsec;

       tsec = time(NULL);

       tstruct = localtime(&tsec);

       printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());

       va_start(args,fmt);

       return vprintf(fmt,args);

}

输出结果与设想中的顺序不同。因为占用内存的进程不会因为wait pid中的PID参数而固定不变,而是三个子进程会按照第一次运行的顺序继续运行,也就是第一次运行的顺序固定之后,后面的运行顺序不再改变。因此输出顺序结果如下图:

 作者:Nathaneko

原文地址:https://www.cnblogs.com/nathaneko/p/6638672.html