Раздел «Язык Си».OOP-Instrumental_3sem1:

Объекты могут состоять ихз других объектов, как уже реализованных ранее (например string, sstream, ifstream, ofstream и др.), так и из тех, которые были описаны нами.

Простые и сложные объекты могут порождаться используя стековую память функции, в которой они локальные или используя динамическую память ("кучу"). В первом случае при завершении функции объект уничтожается (с использованием дестуктора). Во втором необходимо освобождать память "вручную" с использование оператора delete.

Дочерние процессы.

Каждый процесс может породить свой дочерний процесс. При этом дочерний процесс наследует от родительского все значения переменных, открытые дескрипторы, терминал. Но этот дочерний процесс — совершенно самостоятельный: у него свой pid, своя память и т.д.

Пример использования порожденных процессов.

#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <ctime> // для clock()
#include <fstream>

using namespace std;
int main(){
// строковый поток (можно пользоваться операторами ввода/вывода
  stringstream sp;
// строка
  string st;
  int pid;   // pid текущего процесса
  int  ppid; // pid родительского процесса
// получить время начала работы родительского процесса
  unsigned int start_time =  clock();
//  в цикле получаем запрос для исполнения: w - запись в файл
// r - читать из файла, d -  удалить 
  while(1){
// получаем строку
   cin>>st;
//  если ввели *, то выходим из программы
   if(st == "*") exit(1);

// порождаем детский процесс
// родительский процесс при этом получает pid детского процесса, 
// а в детском pid будет равен 0
// На каждой итерации цикла порождается 
// НОВЫЙ детский процесс
   pid = fork();
// если это детский процесс, выходим из цикла,
// а родитель остается в цикле получать команды
// каждую команду будет обрабатывать свой детский процесс
   if(pid == 0) break;   
  }

//  получаем порождения детского процесса
  unsigned int run_time =  clock();
// генерим имя файла для записи действий
  sp<<"log_"<<getpid();
// открываем файл на ввод
  ofstream fo;
// sp.str() возвращает строку, а c_str() от строки возвращает C-строку (массив символов)
  fo.open((sp.str()).c_str()); 
// записываем информацию в файл
  fo<<"fork pid: "<<getpid()<<" at time: "<< run_time -start_time<<endl;

  if(st == "w"){
    fo<<" пишем\n";
  }
  if(st == "s"){
    fo<<" ищем\n";
  }
  if(st == "d"){
    fo<<" удаляем\n";
  }
  fo.close();
}

Дан пример реализации объектов типа Actor. Объекты Actor нужны нам для моделирования взаимодействия независимых процессов в борьбе за разделяемый ресурс. Actor в данной реализации только сообщает о своем наличии путем увеличения или уменьшения общего количества объектов в системе используя очередь сообщений.

Самый первый объект создает очередь сообщений и сразу записывает в нее число 1. Все последующие объекты уже могут читать сообщения из очереди. Таким образом каждый объект при создании проверяет есть ли очередь, читает сообщение, увеличивает число на 1 и записывает сообщение в очередь снова. При удалении объекта, он также читает сообщение. Если количество объектов больше 1, то число уменьшается на 1 и сообщение отправляется в очередь.

Для реализации составим программу, которая порождает дочерние процессы, и меняет содержимое каждого дочернего процесса на код программы, реализующей запуск одного объекта

Заголовочный файл actor.h

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#include <iostream>
#include <cstdlib>
#include <fstream>

#define PERM 0666
using namespace std;

// Структура для записи и чтения в/из очереди сообщений
struct Mess{
  long type; // тип сообщения
  int number; // количество процессов
  int pid;  // pid каждого процесса
};

// Процесс, он же дейcтвующее лицо для других задач
class Actor{
   Mess mSend, mRead; // сообщения для посылки и чтения
   key_t key;         // ключ для создания очереди сообщений
   int mesid;        // идентификатор очерреди сообщений
   int lng;
   int n;
public:
// Конструктор
// Создаем или получаем mesid
    Actor(); 
// Удалаяем очередь
   ~Actor();
// что-то делаем
    void act(int);
};

Реализация класса Actor:

#include "actor.h"



Actor::Actor(){
// печать для иллюстрации
  cout<<"Конструктор Actor: "; 
// получение ключа
  if ((key = ftok("act2",'A'))<0){
   printf("Can't get key\n");
   exit(1);
  }
// для сохранения кода ошибки
  int err = 0;
// пытаемся создать очередь сообщений
  if((mesid = msgget(key,PERM|IPC_CREAT|IPC_EXCL))<0){
// сохраним код ошибки
    err = errno; 
    if(errno == EEXIST){

     cout <<"Очередь уже создана\n";
// пытаемся получить    mesid   
      if ((mesid = msgget(key,0))< 0){

        printf("Can't create message's queue\n");            
        exit(1);
      }
    }else{
      printf("Can't create message's queue\n");
      exit(1);
   }
  }

  mSend.pid = getpid();
// Читаем-пишем сообщения типа 1
  mSend.type = mRead.type = 1L;
    
  if (err == 0){
// Очередь создал именно этот процесс
   cout<<"Посылаем\n";
   mSend.number = 1;
   if (msgsnd(mesid,(void*)&mSend,sizeof(Mess),0) < 0){
           cout<<"Can't write message\n";
           exit(1);
        }
           cout<<"1\n";
      
    }else{
// Очередь создана другим процессом
      mRead.type = 1L;
      cout<<"Читаем:";
// Сначала читаем сообщение и увеличиваем число
      n=msgrcv(mesid,&mRead, sizeof(Mess), mRead.type,0);
  
      cout<<mRead.number<<endl;
      mSend.number = mRead.number + 1;
      cout<<"New number: "<<mSend.number<<endl;

// отсылаем сообщение обратно с новым числом   
      if (msgsnd(mesid,(void*)&mSend,sizeof(Mess),0) < 0){
        cout<<"Can't write message\n";
        exit(1);
      }
  
      cout<<" Я тоже пришел: "<< mSend.number<<endl;
  }
};

// Деструктор с проверкой возможности удаления очереди
Actor::~Actor(){

    cout<<"Desctuctor: читаем сообщение \n";

// Проверяем сколько прцессов еще не удалено
    n=msgrcv(mesid,&mRead, sizeof(Mess), mRead.type,0);
    cout<<mRead.number<<endl;
// Если число больше 1, то уменьшаем и посылаем новое сообщений   
    if(mRead.number > 1){
    mSend.number = mRead.number - 1;
   cout<<"Посылаем новый номер: "<<mSend.number<<endl;
   if (msgsnd(mesid,(void*)&mSend,sizeof(Mess),0) < 0){
          cout<<"Can't write message\n";
          exit(1);
        }
        cout<<"Ушел: "<< mRead.number<<endl; 
     }else{
//  Остался последний. Удаляем очередь
       if(msgctl(mesid,IPC_RMID,0)<0){
         printf("Can't delete queue\n");
         exit(1);
       }
       cout<<"Последний удалил очередь: "<< mRead.number<<endl; 
     }
};
// Действие
void Actor::act(int tm){
   cout<<"Работаем, работаем: "<<tm<<endl;
    sleep(tm);
};

Для работы одного Actor пишем программу (после компиляции она получит имя act2):

#include "actor.h"

int main(int argc, char **argv){
  int t;
//  ofstream ff("l1.dat");
// параметр - время работы (ожидания) 
  t = atoi(argv[1]);
  cout<< t<< endl;
//   ff<<"Actor "<<getpid()<<endl;
//  ff.close();
// создаем объект
  Actor a;
// действие   
  a.act(t);
// деструктор отработает когда main завершает работу   
  return 0;
}

Программа (t_fork) для запуска нескольких процессов как дочерних. Содержание дочернего процесса заменяется кодом act2

#include "actor.h"

int main(){
   int pid;
// запустить дочерний процесс
   pid = fork();
// если процесс детский:
  if(pid == 0){
    cout<<"Дите: "<<getpid()<<endl;
// время "работы"
    int timeP = rand()%100;
    cout<<"timeR: "<<timeP<<endl;
// формируем строку парметра для запуска act2
    char buf[20];
    sprintf(buf,"%d",timeP);
// Замещение содержимого этого процесса кодом act2
// со строкой параметров buf
     execl("act2",buf,NULL);

  }
  
  return 0;
}

Компилируем и линкуем act20, t_fork.

REFACTOR Задачи

Задача 1.

Запустить в цикле n процессов. n - парметр запуска t_fork.

Задача 2.

Изменить программный код так, чтобы каждый процесс записывал информацию не на экран, а в файл с именем log

Задача 3.

В маленькой мастерской три рабочих осуществляют окончательную сборку некоторого устройства из полуфабриката, установленного в тисках, закрепляя на нем две одинаковые гайки и один винт. Два рабочих умеют обращаться только с гаечным ключом, а один — только с отверткой. Действия рабочих схематически описываются следующим образом: взять элемент крепежа и, при наличии возможности, установить его на устройство; если все три элемента крепежа установлены, то вынуть из тисков готовое устройство и закрепить в них очередной полуфабрикат. Размеры устройства позволяют в данный момент времени работать только одному рабочему. Каждый рабочий, сделав одну свою операцию, отходит от устройства. Рабочиий, который закончил обрабатывать деталь (готова), ставит на ее место новую и выполняет одну свою операцию. Самую пераую деталь ставит рабочий с отверткой.

Написать две программы: для рабочего с отверткой и для рабочего с гайкой, использкя класс Actor для моделирования процесса в течении 1 минуты. Время, затраченное на операцию каждым рабочем соответствующая программа получает как параметр. Всю информацию о своей работе каждый процесс выводит в файл с названием log.

-- TatyanaOvsyannikova2011 - 20 Oct 2016