进程控制及进程间通信

本文介绍了在Linux环境下使用多进程编程,涉及fork()、execlp()、定时信号处理、管道通信以及共享内存,展示了如何创建子进程、处理子进程异常和使用信号、定时器以及进程间通信实现同步。
摘要由CSDN通过智能技术生成

一、编写多进程程序

        该实验有3个进程,其中一个为父进程,其余两个是该父进程创建的子进程,其中一个子进程运行“ls -l”指令,另一个子进程在暂停5s之后异常退出,父进程先用阻塞方式等待第一个子进程的结束,然后用非阻塞方式等待另一个子进程的退出,待收集到第二个子进程结束的信息,父进程就返回。

        实验流程图:

        实验代码:

/* multi_proc_wrong.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t child1, child2, child;
    /*创建两个子进程*/
    child1 = fork();
    child2 = fork();
    /*子进程1的出错处理*/
    if (child1 == -1)
    {
        printf("Child1 fork error\n");
        exit(1);
    }
    else if (child1 == 0) /*在子进程1中调用execlp()函数*/
    {
        printf("In child1: execute 'ls -l'\n");
        if (execlp("ls", "ls","-l", NULL)<0)
        {
            printf("Child1 execlp error\n");
        }
    }
  
    if (child2 == -1) /*子进程2的出错处理*/
    {
         printf("Child2 fork error\n");
         exit(1);
    }
    else if( child2 == 0 ) /*在子进程2中使其暂停5s*/
    {
         printf("In child2: sleep for 5 seconds and then exit\n");
         sleep(5);
         exit(0);
    }
    else /*在父进程中等待两个子进程的退出*/
    {
         printf("In father process:\n");
         child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
         if (child == child1)
         {
             printf("Get child1 exit code\n");
         }
         else
         {
             printf("Error occured!\n");
         }
      
         do
         {
             child =waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */
             if (child == 0)
             {
                 printf("The child2 process has not exited!\n");
                 sleep(1);
             }
        } while (child == 0);
      
         if (child == child2)
         {
             printf("Get child2 exit code\n");
         }
         else
         {
             printf("Error occured!\n");
         }
    }  
    exit(0);
}

        运行结果:

In father process:
In child1: execute 'ls -l'
In child2: sleep for 5 seconds and then exit
In child1: execute 'ls -l'
totaltotal  00

Get child1 exit code
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
Get child2 exit code

        改进代码:

/*multi_proc.c */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t child1, child2, child;
    
    /*创建两个子进程*/
    child1 = fork();
        
    /*子进程1的出错处理*/
    if (child1 == -1)
    {
        printf("Child1 fork error\n");
        exit(1);
    }
    else if (child1 == 0) /*在子进程1中调用execlp()函数*/
    {
        printf("In child1: execute 'ls -l'\n");
        if (execlp("ls", "ls", "-l", NULL) < 0)
        {
            printf("Child1 execlp error\n");
        }
    }
    else /*在父进程中再创建进程2,然后等待两个子进程的退出*/
    {
         child2 = fork();
         if (child2 == -1) /*子进程2的出错处理*/
         {
             printf("Child2 fork error\n");
             exit(1);
        }
        else if(child2 == 0) /*在子进程2中使其暂停5s*/
         {
             printf("In child2: sleep for 5 seconds and then exit\n");
             sleep(5);
             exit(0);
         }
  
         printf("In father process:\n");
         child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
         if (child == child1)
         {
             printf("Get child1 exit code\n");
         }
         else
         {
              printf("Error occured!\n");
         }
      
         do
         {
             child = waitpid(child2, NULL, WNOHANG ); /* 非阻塞式等待 */
             if (child == 0)
             {
                  printf("The child2 process has not exited!\n");
                  sleep(1);
             }
         } while (child == 0);
  
         if (child == child2)
         {
             printf("Get child2 exit code\n");
         }
         else
         {
             printf("Error occured!\n");
         }
    }  
    exit(0);
}

        运行结果:

In father process:
In child2: sleep for 5 seconds and then exit
In child1: execute 'ls -l'
total 0
Get child1 exit code
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
The child2 process has not exited!
Get child2 exit code

        比较之前两个代码,可以发现第二个代码在父进程中再创建进程2,然后等待两个子进程的退出,减少了一次child1的执行,提升了代码效率。

        得出结论:几个子进程的执行有竞争关系,因此,结果中的顺序是随机的。

        一个进程,包括分配给该进程的代码、数据和资源。fork()函数使用一个系统调用来创建一个与原始进程几乎相同的进程,这意味着两个进程可以做完全相同的事情,但如果传入的初始参数或变量不同,也可以做不同的事情。在进程调用fork()函数后,系统首先为新进程分配资源,例如存储数据和代码的空间。然后将原始进程的所有值复制到新进程中,除了一些与原始进程不同的值,即相当于对自己的克隆。

二、编写linux定时器程序

        下面的程序实现了如下功能:main函数运行中被定时信号打断,从而转入用户指定的信号处理函数执行。该程序说明在linux也可以通过信号模拟硬件定时器的中断触发机制。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
 
void sig_handler(int num)
{
    printf("receive the signal %d.\n", num);
    alarm(2);
}
 
int main()
{
    signal(SIGALRM, sig_handler);
    alarm(2);
    while(1){
        pause();
        printf("pause is over.\n");
    }
    exit(0);
}

        运行结果:

Time limit exceeded (wall clock)

三、管道通信实验

        mknod命令的功能是用于创建字符及块设备文件,Linux系统中的一切都是文件,在系统与设备通信之前,都会自动在/dev目录中生成对应的设备文件,如若没有自动完成,则需要管理员手动使用mknod命令建立它们。

        实验流程图:

        运行代码:

/* pipe_select.c*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#define FIFO1               "in1"
#define FIFO2               "in2"
#define MAX_BUFFER_SIZE         1024          /* 缓冲区大小*/
#define IN_FILES            3                   /* 多路复用输入文件数目*/
#define TIME_DELAY          60              /* 超时值秒数 */
#define MAX(a, b)           ((a > b)?(a):(b))

int main(void)
{
     int fds[IN_FILES];
     char buf[MAX_BUFFER_SIZE];
     int i, res, real_read, maxfd;
     struct timeval tv;
     fd_set inset,tmp_inset;
    
     fds[0] = 0;        
    
     /* 创建两个有名管道 */
     if (access(FIFO1, F_OK) == -1) 
     {
        if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))
        {
            printf("Cannot create fifo file\n");
            exit(1);
        }
     }
     if (access(FIFO2, F_OK) == -1) 
     {
        if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST))
        {
            printf("Cannot create fifo file\n");
            exit(1);
        }
     }
        
     /* 以只读非阻塞方式打开两个管道文件 */
     if((fds[1] = open (FIFO1, O_RDONLY|O_NONBLOCK)) < 0)
     {
        printf("Open in1 error\n");
        return 1;
     }
    if((fds[2] = open (FIFO2, O_RDONLY|O_NONBLOCK)) < 0)
    {
        printf("Open in2 error\n");
        return 1;
     }

     /*取出两个文件描述符中的较大者*/
    maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);
    /*初始化读集合inset,并在读文件描述符集合中加入相应的描述集*/
    FD_ZERO(&inset); 
    for (i = 0; i < IN_FILES; i++)
    {
        FD_SET(fds[i], &inset);
    }
    FD_SET(0, &inset);

    tv.tv_sec = TIME_DELAY;
    tv.tv_usec = 0;
    /*循环测试该文件描述符是否准备就绪,并调用select()函数对相关文件描述符做相应操作*/
    while(FD_ISSET(fds[0],&inset) 
             || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))
    { 
     /* 文件描述符集合的备份, 免得每次进行初始化 */
        tmp_inset = inset; 
        res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
        switch(res)
        {
            case -1:
            {
                  printf("Select error\n");
                  return 1;
              }
              break;                  
              case 0: /* Timeout */
              {
                  printf("Time out\n");
                  return 1;
           }
            break;
            default:
            {
                  for (i = 0; i < IN_FILES; i++)
                  {
                      if (FD_ISSET(fds[i], &tmp_inset))
                      {
                          memset(buf, 0, MAX_BUFFER_SIZE);
                          real_read = read(fds[i], buf, MAX_BUFFER_SIZE);
                          if (real_read < 0)
                          {
                              if (errno != EAGAIN)
                              {
                                  return 1;
                              }
                      	     }
                           else if (!real_read)
                           {
                               close(fds[i]);
                               FD_CLR(fds[i], &inset);
                          }
                          else
                          {
                               if (i == 0)
                               {/* 主程序终端控制 */
                                  if ((buf[0] == 'q') || (buf[0] == 'Q'))
    	                              {
          	        	                  return 1;
                               }
                               }
                               else
                               {/* 显示管道输入字符串 */
                                   buf[real_read] = '\0';
                                   printf("%s", buf);
                              }
              	        }
                      } /* end of if */
                  } /* end of for */
             }
             break;
          } /* end of switch */
    } /*end of while */
    return 0;
}

        运行结果:

Time limit exceeded (wall clock)

        与实验二结果一致。

四、共享内存实验

        该实现要求利用共享内存实现文件的打开和读写操作。

        实验流程图:

        共享内存缓冲区的数据结构的定义代码如下:

/* shm_com.h */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_BUFF_SZ 2048
struct shm_buff
{
    int pid;
    char buffer[SHM_BUFF_SZ];
};

        以下是“生产者”程序部分:

/* sem_com.h 和 sem_com.c 与“信号量”小节示例中的同名程序相同 */
/* producer.c */
#include "shm_com.h"
#include "sem_com.h"
#include <signal.h>
int ignore_signal(void)
{ /* 忽略一些信号,免得非法退出程序 */
    signal(SIGINT, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    return 0;
}

int main()
{
    void *shared_memory = NULL;
    struct shm_buff *shm_buff_inst;
    char buffer[BUFSIZ];
    int shmid, semid;
    /* 定义信号量,用于实现访问共享内存的进程之间的互斥*/
      ignore_signal(); /* 防止程序非正常退出 */
    semid = semget(ftok(".", 'a'),  1, 0666|IPC_CREAT); /* 创建一个信号量*/
    init_sem(semid);/* 初始值为1 */

    /* 创建共享内存 */
    shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
    if (shmid == -1)
    {
        perror("shmget failed");
        del_sem(semid);
        exit(1);
    }
    
    /* 将共享内存地址映射到当前进程地址空间 */
    shared_memory = shmat(shmid, (void*)0, 0);
    if (shared_memory == (void*)-1)
    {
        perror("shmat");
        del_sem(semid);
        exit(1);
    }
    printf("Memory attached at %X\n", (int)shared_memory);
    /* 获得共享内存的映射地址 */
    shm_buff_inst = (struct shared_use_st *)shared_memory;
    do
    {
        sem_p(semid);    
        printf("Enter some text to the shared memory(enter 'quit' to exit):");
        /* 向共享内存写入数据 */
            if (fgets(shm_buff_inst->buffer, SHM_BUFF_SZ, stdin) == NULL)
        {
            perror("fgets");
            sem_v(semid);
            break;
        }
        shm_buff_inst->pid = getpid();        
        sem_v(semid);
    } while(strncmp(shm_buff_inst->buffer, "quit", 4) != 0);

    /* 删除信号量 */
    del_sem(semid);
    /* 删除共享内存到当前进程地址空间中的映射 */
    if (shmdt(shared_memory) == 1)
    {
        perror("shmdt");
        exit(1);
    }
    exit(0);
}

        以下是“消费者”程序部分:

/* customer.c */
#include "shm_com.h"
#include "sem_com.h"

int main()
{
    void *shared_memory = NULL;
    struct shm_buff *shm_buff_inst;
    int shmid, semid;
    /* 获得信号量 */
    semid = semget(ftok(".", 'a'),  1, 0666);
    if (semid == -1)
    {    
        perror("Producer is'nt exist");
        exit(1);
    }
    /* 获得共享内存 */
    shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    /* 将共享内存地址映射到当前进程地址空间 */
    shared_memory = shmat(shmid, (void*)0, 0); 
    if (shared_memory == (void*)-1)
    {
        perror("shmat");
        exit(1);
    }
    printf("Memory attached at %X\n", (int)shared_memory);
    /* 获得共享内存的映射地址 */
    shm_buff_inst = (struct shm_buff *)shared_memory;
    do
    {
        sem_p(semid);
        printf("Shared memory was written by process %d :%s"
,                         shm_buff_inst->pid, shm_buff_inst->buffer);
        if (strncmp(shm_buff_inst->buffer, "quit", 4) == 0) 
        {
            break;
        }
        shm_buff_inst->pid = 0;
        memset(shm_buff_inst->buffer, 0, SHM_BUFF_SZ);
        sem_v(semid);
    } while(1);

    /* 删除共享内存到当前进程地址空间中的映射 */
    if (shmdt(shared_memory) == -1)
    {
        perror("shmdt");
        exit(1);
    }
    /* 删除共享内存 */
    if (shmctl(shmid, IPC_RMID, NULL) == -1)
    {
        perror("shmctl(IPC_RMID)");
        exit(1);
    }
    exit(0);
}

        实验结果:

$./producer
Memory attached at B7F90000
Enter some text to the shared memory(enter 'quit' to exit):First message
Enter some text to the shared memory(enter 'quit' to exit):Second message
Enter some text to the shared memory(enter 'quit' to exit):quit
$./customer
Memory attached at B7FAF000
Shared memory was written by process 3815 :First message
Shared memory was written by process 3815 :Second message
Shared memory was written by process 3815 :quit

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验进程管理   Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。对于C++ 运行库来说,这一调用发生在应用程序的main() 函数返回之后。 1. 创建进程 CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。表 2-1详细地列出了每个参数的类型和名称。   表2-1 CreateProcess() 函数的参数 参数名称 使用目的 LPCTSTR lpApplivationName 全部或部分地指明包括可执行代码的EXE文件的文件名 LPCTSTR lpCommandLine 向可执行文件发送的参数 LPSECURIITY_ATTRIBUTES lpProcessAttributes 返回进程句柄的安全属性。主要指明这一句柄是否应该由其他子进程所继承 LPSECURIITY_ATTRIBUTES lpThreadAttributes 返回进程的主线程的句柄的安全属性 BOOL bInheritHandle 一种标志,告诉系统允许新进程继承创建者进程的句柄 DWORD dwCreationFlage 特殊的创建标志 (如CREATE_SUSPENDED) 的位标记 LPVOID lpEnvironment 向新进程发送的一套环境变量;如为null值则发送调用者环境 LPCTSTR lpCurrentDirectory 新进程的启动目录 STARTUPINFO lpStartupInfo STARTUPINFO结构,包括新进程的输入和输出配置的详情 LPPROCESS_INFORMATION lpProcessInformation 调用的结果块;发送新应用程序的进程和主线程的句柄和ID   可以指定第一个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者利用搜索方法找到的路径;lpCommandLine参数允许调用者向新应用程序发送数据;接下来的三个参数与进程和它的主线程以及返回的指向该对象的句柄的安全性有关。 然后是标志参数,用以在dwCreationFlags参数中指明系统应该给予新进程什么行为。经常使用的标志是CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用ResumeThread() API来启动进程。另一个常用的标志是CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口,而不是利用父窗口。这一参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他的活动进程来说,给此进程多少CPU时间。 接着是CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第一个参数是lpEnvironment参数,指明为新进程提供的环境;第二个参数是lpCurrentDirectory,可用于向主创进程发送与缺省目录不同的新进程使用的特殊的当前目录;第三个参数是STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。 CreateProcess() 的最后一个参数是用于新进程对象及其主线程的句柄和ID的返回值缓冲区。以PROCESS_INFORMATION结构中返回的句柄调用CloseHandle() API函数是重要的,因为如果不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。 2. 正在运行的进程 如果一个进程拥有至少一个执行线程,则为正在系统中运行的进程。通常,这种进程使用主线程来指示它的存在。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少数特性也允许加以修改。 首先可查看的进程特性是系统进程标识符 (PID) ,可利用GetCurrentProcessId() API函数来查看,与GetCurrentProcess() 相似,对该函数的调用不能失败,但返回的PID在整个系统中都可使用。其他的可显示当前进程信息的API函数还有GetStartupInfo()和GetProcessShutdownParameters() ,可给出进程存活期内的配置详情。 通常,一个进程需要它的运行期环境的信息。例如API函数GetModuleFileName() 和GetCommandLine() ,可以给出用在CreateProcess() 中的参数以启动应用程序。在创建应用程序时可使用的另一个
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值