Write a scheduler Round-Robin with the following characteristics:
The scheduler must accept quantum qt time and the processes to be performed as parameters on the command line with the following format:
./scheduler qt path_process1 startTime1 executionTime1 path_process2 startTime2 executionTime2 ...
Each of the processes that create and will rotate the scheduler should be a separate program that prints a message every 0.5s in the following format:
Program i: Messages printed: 1.
Program i: Messages printed: 2.
Program i: Messages ...
...
When cyclic initiation and termination of each process itself will print messages:
Process pid is executing.
//Execution of process pid
Process pid is suspended.
Stopping and execution processes will be with signals SIGTSTP and SIGCONT .
Consider and comment system behavior for small and large values of the quantum time (e.g. qt = 0.5s, 1s, 2s, 4s) for five processes with the following starting and running times.
startTimes = [0,2,4,6,8]
execTimes = [3,6,4,5,2]
Given Hint:
To carry out a process child as a separate program, use the command execl (...).
For measuring the time elapsed from the start of operation of the scheduler, use the command gettimeofday (...).
Code
Scheduler.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/signal.h>
using namespace std;
float qt;
int k,j,i;
int total_time=0;
int pass_time=0;
void stp_handler(int signal){
}
void con_handler(int signal){
}
int main(int argc, char *argv[])
{
int p=2;
int s=3;
int e=4;
int count=0;
signal(SIGTSTP,stp_handler);
signal(SIGCONT,con_handler);
int start_time[(argc-2)/3];
float execution_time[(argc-2)/3];
char *path_process[(argc-2)/3];
float remaining_time[(argc-2)/3];
timeval time;
int start,end;
if (argc < 2)
{
cout<<"You didn't enter any arguments" << endl;
exit(0);
}
else{
qt = atoi(argv[1]);
while (count < ((argc-2)/3) ){
path_process[count] = argv[p];
start_time[count] = atoi(argv[s]);
execution_time[count] = atoi(argv[e]);
remaining_time[count] = execution_time[count];
total_time = total_time + execution_time[count];
cout << path_process[count] << " " << start_time[count] << " " << execution_time[count] << endl;
p+=3;
s+=3;
e+=3;
count++;
}
}
i=0;
pid_t pid[count];
while(i<count){
pid_t par,child;
child=fork();
if (child==0){
par = getppid();
cout << "Child pid: " << i << " " << getpid() << endl << "Parent pid: " << par << endl;
char num[100]={0x0};
sprintf(num,"%d",i+1);
char pi[100]={0x0};
sprintf(pi,"%d",getpid());
execl(path_process[i],path_process[i],pi,num,(char*)0);
exit(0);
}
else{
sleep(1);
pid[i] = child;
i++;
}
}
i=0;
cout << endl << "Processes time: " << total_time << endl << endl;
gettimeofday(&time,NULL);
start = time.tv_sec;
while(pass_time<total_time){
for(j=0;j<count;j++){
if (pass_time<start_time[j]){
continue;
}
else if (remaining_time[j]<=0){
continue;
}
else if (remaining_time[j]>= qt){
kill(pid[j],SIGCONT);
usleep(qt*1000000);
kill(pid[j],SIGTSTP);
pass_time = pass_time + qt;
remaining_time[j]=remaining_time[j]-qt;
i++;
}
else{
kill(pid[j],SIGCONT);
usleep(remaining_time[j]*1000000);
kill(pid[j],SIGTSTP);
pass_time = pass_time + remaining_time[j];
remaining_time[j]=0;
i++;
}
}
}
sleep(1);
gettimeofday(&time,NULL);
end = time.tv_sec;
cout << "Scheduler time: " << end - start << endl;
for(i=0;i<count;i++)
kill(pid[i],SIGKILL);
return 0;
}
Signals.cpp
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <time.h>
#include <sys/wait.h>
#include <sys/signal.h>
using namespace std;
int j=1;
char* pid;
void sig_handler(int signal){
if (signal == SIGCONT){
cout << endl << "Process " << pid << " is executing." << endl << endl;
j=0;
}
else if (signal == SIGTSTP){
cout << endl << "Process " << pid << " is suspended." << endl << endl;
j=1;
}
}
main(int argc, char *argv[])
{
signal(SIGTSTP,sig_handler);
signal(SIGCONT,sig_handler);
pid = argv[1];
int i=1;
while(1){
while(j==0){
cout << "Program: " << argv[2] << " messages printed: " << i << endl;
usleep(500000);
i++;
}
}
}
Output from Scheduler
(This is correct I guess)
process1 0 3
process2 2 6
process2 4 4
process3 6 5
process4 8 2
Child pid: 0 3406
Parent pid: 3405
Child pid: 1 3407
Parent pid: 3405
Child pid: 2 3408
Parent pid: 3405
Child pid: 3 3409
Parent pid: 3405
Child pid: 4 3410
Parent pid: 3405
Processes time: 20
Output from Signals
I get nothing but as I mentioned above I expect it to be somehow like this.
Program i: Messages printed: 1.
Program i: Messages printed: 2.
Program i: Messages ...
...
When cyclic initiation and termination of each process itself will print messages:
Process pid is executing.
//Execution of process pid
Process pid is suspended.
P.S: In order to get the scheduler to work I compile it and run it with
>./scheduler 0.5 process1 0 3 process2 2 6 process2 4 4 process3 6 5 process4 8 2
Why don't the signals cause any output?
Related
I need your help. Program A executes program B with fork(). Every 5 seconds the process belonging to program B is interrupted. If the user enters any key within a certain time, the process is continued and interrupted again after the same time interval. If no key is entered, both program A and program B are terminated prematurely. I have tried the following code, but it does not work. Any suggestions/tips that will help me?
#include <iostream>
#include <chrono>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
using namespace std;
using namespace chrono;
int pid;
void signal_handler(int signum) {
cout << "Programm B is interrupted. Please enter any key within 5 or the programm will be terminated" << endl;
kill(pid,SIGSTOP);
alarm(5);
pause();
alarm(5);
}
int main(int argc, char* argv[]) {
//Usage
if(string(argv[1]) == "h" || string(argv[1]) == "help"){
cout << "usage" << endl;
return 0;
}
signal(SIGALRM, signal_handler);
pid = fork();
if (pid == 0) {
cout << "Name of programm B: " << argv[1] << endl;
cout << "PID of programm B: " << getpid() << endl;
execvp(argv[1], &argv[1]);
} else if (pid > 0) {
cout << "PID of programm A: " << getpid() << endl;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
waitpid(pid, nullptr, 0);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(t2 - t1).count();
cout << "Computing time: " << duration << "ms" << endl;
} else {
cerr << "error << endl;
return 1;
}
return 0;
}
Any help or sulution. I am a beginner in c++ btw.
Signals can get tricky and there are lots of issues with your approach.
You should:
kick off the timer (alarm(5)) in main
do the sighandler registration and timer kick-off after you've spawned the child (or you somewhat risk running the signal handler in the child in between fork and execvp)
use sigaction rather than signal to register the signal, as the former has clear portable semantics unlike the latter
loop on EINTR around waitpid (as signal interruptions will cause waitpid to fail with EINTR)
As for the handler, it'll need to
use only async-signal-safe functions
register another alarm() around read
unblock SIGALRM for the alarm around read but not before you somehow mark yourself as being in your SIGALRM signal handler already so the potential recursive entry of the handler can do a different thing (kill the child and exit)
(For the last point, you could do without signal-unblocking if you register the handler with .sa_flags = SA_NODEFER, but that has the downside of opening up your application to stack-overflow caused by many externally sent (via kill) SIGALRMs. If you wanted to handle externally sent SIGALRMs precisely, you could register the handler with .sa_flags=SA_SIGINFO and use info->si_code to differentiate between user-sends and alarm-sends of SIGALRM, presumably aborting on externally-sent ones)
It could look something like this (based on your code):
#include <iostream>
#include <chrono>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
//AS-safe raw io helper functions
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const char *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
ssize_t writes(int fd, char const *str0) { return writen(fd,str0,strlen(str0)); }
ssize_t writes2(char const *str0) { return writes(2,str0); }
//AS-safe sigprockmask helpers (they're in libc too, but not specified as AS-safe)
int sigrelse(int sig){
sigset_t set; sigemptyset(&set); sigaddset(&set,sig);
return sigprocmask(SIG_UNBLOCK,&set,0);
}
int sighold(int sig){
sigset_t set; sigemptyset(&set); sigaddset(&set,sig);
return sigprocmask(SIG_BLOCK,&set,0);
}
#define INTERRUPT_TIME 5
using namespace std;
using namespace chrono;
int pid;
volatile sig_atomic_t recursing_handler_eh; //to differentiate recursive executions of signal_handler
void signal_handler(int signum) {
char ch;
if(!recursing_handler_eh){
kill(pid,SIGSTOP);
writes2("Programm B is interrupted. Please type enter within 5 seconds or the programm will be terminated\n");
alarm(5);
recursing_handler_eh = 1;
sigrelse(SIGALRM);
if (1!=read(0,&ch,1)) signal_handler(signum);
alarm(0);
sighold(SIGALRM);
writes2("Continuing");
kill(pid,SIGCONT);
recursing_handler_eh=0;
alarm(INTERRUPT_TIME);
return;
}
kill(pid,SIGTERM);
_exit(1);
}
int main(int argc, char* argv[]) {
//Usage
if(string(argv[1]) == "h" || string(argv[1]) == "help"){
cout << "usage" << endl;
return 0;
}
pid = fork();
if (pid == 0) {
cout << "Name of programm B: " << argv[1] << endl;
cout << "PID of programm B: " << getpid() << endl;
execvp(argv[1], &argv[1]);
} else if (pid < 0) { cerr << "error" <<endl; return 1; }
struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags=0; sigaction(SIGALRM, &sa,0);
//signal(SIGALRM, signal_handler);
alarm(INTERRUPT_TIME);
cout << "PID of programm A: " << getpid() << endl;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
int r;
do r = waitpid(pid, nullptr, 0); while(r==-1 && errno==EINTR);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(t2 - t1).count();
cout << "Computing time: " << duration << "ms" << endl;
return 0;
}
Not that the above will wait only for an enter key. To wait for any key, you'll need to put your terminal in raw/cbreak mode and restore the previous settings on exit (ideally on signal deaths too).
I'm trying to get the return code of the child process created by fork(). I'm using wait() function to get the return code. Everything is working fine but the return values given by wait() is 256 times the actual return value. Can anybody explain why is that.
Code:
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
constexpr int PROCESS_COUNT = 7;
int main() {
pid_t pid;
for (int i = 0; i < PROCESS_COUNT; i++) {
pid = fork();
if (pid > 0) {
int returnCode;
int pid;
pid = wait(&returnCode);
std::cout << "\n Process: " << pid << "; i: " << i
<< "; Return Code: " << returnCode << std::endl;
}
else {
return i;
}
}
return EXIT_SUCCESS;
}
Output:
Process: 7910; i: 0; Return Code: 0
Process: 7911; i: 1; Return Code: 256
Process: 7912; i: 2; Return Code: 512
Process: 7913; i: 3; Return Code: 768
Process: 7914; i: 4; Return Code: 1024
Process: 7915; i: 5; Return Code: 1280
Process: 7916; i: 6; Return Code: 1536
Please read the wait manual page. The value given by wait doesn't only contain the child-process exit code, but also other flags and values.
To get the exit status you first need to make sure that the child-process really exited the normal way. This is done with the WIFEXITED macro.
Then to get the actual status use the WEXITSTATUS macro.
Something like this:
pid_t pid = wait(&returnCode);
if (pid >= 0 && WIFEXITED(returnCode))
{
std::cout << "Child process " << pid << " exited normally with return code " << WEXITSTATUS(returnCode) << '\n';
}
Note that I added the "correct" type actually returned by wait, and that I also check it to make sure that wait didn't fail.
C++98 and Boost 1.54
I'm having trouble figuring out why using boost::this_thread::sleep_for is sleeping my entire program. The only time and place the Wait() function is called is inside this thread, and this thread's sole purpose is to read file names in a directory and trigger an upload.
But for some reason, when it reaches the boost::this_thread::sleep_for line in the Wait() function, it hangs there and sleeps all the other threads as well. I'm unsure what I am missing, so any help would be appreciated.
Code:
void Upload::ReadFileNames()
{
cout << "[DEBUG] ReadFileNames -> A " << endl;
Wait();
cout << "[DEBUG] ReadFileNames -> B " << endl;
// read filename stuff
}
void Upload::Wait()
{
typedef boost::chrono::duration<long, boost::ratio<60> > seconds;
int randomWaitTime = 0;
try{
randomWaitTime = lexical_cast<unsigned int>(getId());
randomWaitTime = randomWaitTime * 10;
}
catch ( const boost::bad_lexical_cast & e){
// cout << "[LOG] FileUpLoad : Wait : bad_lexical_cast : " << e.what() << endl ;
randomWaitTime = 0;
}
seconds testTimeToWait(randomWaitTime);
cout << "[DEBUG] Wait() -> A" << endl;
boost::this_thread::sleep_for(testTimeToWait);
cout << "[DEBUG] Wait() -> B" << endl;
cout << "RANDOM WAIT TIME = " << randomWaitTime << endl;
}
main.cpp
int main()
{
pthread_t threadA;
pthread_create(&threadA,NULL,threadAfn,NULL);
pthread_t threadB;
pthread_create(&threadB,NULL,threadBfn,NULL);
pthread_t Upload; // <--- Thread in question
pthread_create(&Upload,NULL,Uploadfn,NULL);
pthread_join(threadA,NULL);
pthread_join(threadB,NULL);
pthread_join(Upload,NULL); // <--- Thread in question
return 0;
}
Output
[DEBUG] ReadFileNames -> A
[DEBUG] Wait() -> A
// hangs here and rest of the threads are locked/slept as well?
it hangs there and sleeps all the other threads as well
No it doesn't. If it seems that way, that is because the other threads were already stuck or finished.
Look for things that block (mutex.lock, condition wait, IO operations, etc.) or check that the threads didn't exit.
Notes
Your seconds calculations is off. On my system, the following:
Live On Coliru
#include <boost/chrono.hpp>
#include <iostream>
int main() {
std::cout << boost::chrono::duration<long, boost::ratio<60> >(1)/boost::chrono::seconds(1) << std::endl;
}
Prints
60
So, what you named seconds is actually minutes. Just do this instead:
using boost::chrono::seconds;
int delay = std::strtoul(getId().c_str(), NULL, 10)*10;
sleep_for(seconds(delay));
Your random delay is only random if the getId return is. Using boost/random.hpp you can make it truly random, with good range control. E.g. to sleep between 1'000 and 3'000 ms:
int random_gen(int low, int high) { // not threadsafe
static boost::random_device rdev;
static boost::mt19937 prng(rdev);
return boost::uniform_int<>(low, high)(prng);
}
void Upload::Wait() {
int const ms_delay = random_gen(1000, 3000);
cout << "RANDOM WAIT TIME = " << ms_delay << endl;
sleep_for(milliseconds(ms_delay));
}
Note to seed using random_device as shown (so true random seed) you need to link the random library. Otherwise, you can "stoop" to a time-based seed:
static boost::mt19937 prng(std::time(NULL));
Here's a self-contained version of your code with the various suggestions applied, demonstrating that there is no deadlock/softlock:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/chrono.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <boost/random.hpp>
using boost::this_thread::sleep_for;
using boost::chrono::seconds;
using boost::chrono::milliseconds;
using boost::lexical_cast;
using std::cout;
using std::endl;
struct Upload {
std::string getId() const { return "42"; }
void Wait();
void ReadFileNames();
};
void Upload::ReadFileNames() {
cout << "[DEBUG] ReadFileNames -> A " << endl;
Wait();
cout << "[DEBUG] ReadFileNames -> B " << endl;
// read filename stuff
}
int random_gen(int low, int high) { // not threadsafe
static boost::mt19937 prng(std::time(NULL));
return boost::uniform_int<>(low, high)(prng);
}
void Upload::Wait() {
int const ms_delay = random_gen(1000, 3000);
cout << "RANDOM WAIT TIME = " << ms_delay << endl;
sleep_for(milliseconds(ms_delay));
}
void background(char const* name) {
// desync different background threads
sleep_for(milliseconds(boost::hash_value(name) % 1000));
for (int i=0; i<5; ++i) {
sleep_for(seconds(1));
std::clog << name << " " << i << std::endl;
}
}
void threadAfn() { background("thread A"); }
void threadBfn() { background("thread B"); }
void Uploadfn() {
Upload u;
u.ReadFileNames();
}
int main() {
boost::thread threadA(threadAfn);
boost::thread threadB(threadBfn);
boost::thread Upload(Uploadfn);
threadA.join();
threadB.join();
Upload.join();
}
Prints, e.g.:
[DEBUG] ReadFileNames -> A
RANDOM WAIT TIME = 1150
[DEBUG] ReadFileNames -> B
thread A 0
thread B 0
thread A 1
thread B 1
thread A 2
thread B 2
thread A 3
thread B 3
thread A 4
thread B 4
image for what output is supposed to look like:My problem is that I need to write a program that will accept the names of 3 processes as command-line arguments. Each of these processes will run for as many seconds as:(PID%10)*3+5 and terminate. After those 3 children terminated, the parent process
will reschedule each child. When all children have been rescheduled 3 times, the parent will terminate. I have used fork to create the three children but am struggling with getting them to exit with that specific criteria?
using namespace std;
int main(){
int i;
int pid;
for(i=0;i<3;i++) // loop will run n times (n=3)
{
if(fork() == 0)
{
pid = getpid();
cout << "Process p" << i+1 << " pid:" << pid << " Started..." << endl;
exit(0);
}
}
for(int i=0;i<5;i++) // loop will run n times (n=3)
wait(NULL);
}
You can use sigtimedwait to wait for SIGCHLD or timeout.
Working example:
#include <cstdio>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
template<class... Args>
void start_child(unsigned max_runtime_sec, Args... args) {
// Block SIGCHLD.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, nullptr);
// Enable SIGCHLD.
signal(SIGCHLD, [](int){});
pid_t child_pid = fork();
switch(child_pid) {
case -1:
std::abort();
case 0: {
// Child process.
execl(args..., nullptr);
abort(); // never get here.
}
default: {
// paren process.
timespec timeout = {};
timeout.tv_sec = max_runtime_sec;
siginfo_t info = {};
int rc = sigtimedwait(&set, nullptr, &timeout);
if(SIGCHLD == rc) {
std::printf("child %u terminated in time with return code %d.\n", static_cast<unsigned>(child_pid), info.si_status);
}
else {
kill(child_pid, SIGTERM);
sigwaitinfo(&set, &info);
std::printf("child %u terminated on timeout with return code %d.\n", static_cast<unsigned>(child_pid), info.si_status);
}
}
}
}
int main() {
start_child(2, "/bin/sleep", "/bin/sleep", "10");
start_child(2, "/bin/sleep", "/bin/sleep", "1");
}
Output:
child 31548 terminated on timeout with return code 15.
child 31549 terminated in time with return code 0.
With these changes your program produces the desired output:
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
using namespace std;
int main()
{
for (int round = 0; ++round <= 4; )
{
int i;
cout << "*** ROUND: " << round << " ***\n";
for (i=0; i<3; i++) // loop will run n times (n=3)
{
if (fork() == 0)
{
int pid = getpid();
cout << "Process p" << i+1 << " pid:" << pid << " started...\n";
unsigned int seconds = pid%10*3+5;
cout << "Process " << pid << " exiting after "
<< seconds-sleep(seconds) << " seconds\n";
exit(0);
}
}
while (i--) // loop will run n times (n=3)
{
int status;
cout << "Process " << wait(&status);
cout << " exited with status: " << status << endl;
}
}
}
As Serge suggested, we're using sleep() for every child before exiting it. it will pause the process for a number of seconds.
To get the actual status information, we call wait(&status) instead of wait(NULL).
We're doing this all for the first scheduling round plus the desired 3 times of rescheduling.
When this program runs it goes through the loop in the parent then switches to the child when it writes to the pipe. In the child the pipe that reads just causes the program to stop.
Current example output:
Parent 4741 14087 (only this line when 5 more lines are expected)
Expected output(with randomly generated numbers):
Parent 4741 14087
Child 4740 47082
Parent 4741 11345
Child 4740 99017
Parent 4741 96744
Child 4740 98653
(when given the variable 3 and the last number is a randomly generated number)
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <ctime>
using namespace std;
int main (int argc, char *argv[]) {
int pid = fork(), temp, randNum, count, pipeName[2], pipeName2[2];
string conver;
pipe(pipeName);
conver = argv[1];
temp = atoi(conver.c_str());
char letter;
if (pid == 0) { //child
srand((unsigned)time(NULL) * getpid() );
//closing unused pipes
close(pipeName2[1]);
close(pipeName[0]);
//loop to switch between processes
for(int i=0; i<temp; i++) {
count = read(pipeName2[0], &letter, 20);
randNum = rand();
cout << "Child " << getpid() << " " << randNum << endl;
write(pipeName[1], "x", 1);
}
close(pipeName2[0]);
close(pipeName[1]);
}
else { //parent
srand((unsigned)time(NULL) * getpid() );
pipe(pipeName2);
//closing unused pipes
close(pipeName2[0]);
close(pipeName[1]);
//loop to switch between processes
for(int i=0; i<temp; i++) {
if(i != 0)
count = read(pipeName[0], &letter, 20);
randNum = rand();
cout << "Parent " << getpid() << " " << randNum << endl;
write(pipeName2[1], "x", 1);
}
close(pipeName[0]);
close(pipeName2[1]);
}
}
The program ends when it hits the read from pipe line in the child.
Your principal mistake is fork()ing before you initialize the pipes. Both parent and child thus have their own private (not shared via fd inheritance) pipe pair named pipeName, and only the parent initializes pipeName2 with pipe fds.
For the parent, there's simply no data to read behind pipeName[0]. For the child ... who knows what fd it is writing to in pipeName2[1]? If you're lucky that fails with EBADF.
So, first pipe() twice, and then fork(), and see if that improves things.