Help in Unix in bash here is the apue.h file lock_reg((fd), F_SETLKW, F_RDLCK, (
ID: 3727207 • Letter: H
Question
Help in Unix in bash here is the apue.h file
lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len)
lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
pid_t lock_test(int, int, off_t, int, off_t); /* {Prog locktest} */
#define is_read_lockable(fd, offset, whence, len)
(lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len)
(lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)
void err_msg(const char *, ...); /* {App misc_source} */
void err_dump(const char *, ...) __attribute__((noreturn));
void err_quit(const char *, ...) __attribute__((noreturn));
void err_cont(int, const char *, ...);
void err_exit(int, const char *, ...) __attribute__((noreturn));
void err_ret(const char *, ...);
void err_sys(const char *, ...) __attribute__((noreturn));
void log_msg(const char *, ...); /* {App misc_source} */
void log_open(const char *, int, int);
void log_quit(const char *, ...) __attribute__((noreturn));
void log_ret(const char *, ...);
void log_sys(const char *, ...) __attribute__((noreturn));
void log_exit(int, const char *, ...) __attribute__((noreturn));
void TELL_WAIT(void); /* parent/child from {Sec race_conditions} */
void TELL_PARENT(pid_t);
void TELL_CHILD(pid_t);
void WAIT_PARENT(void);
void WAIT_CHILD(void);
#endif /* _APUE_H */
Explain the logic of the program in Example 2, especially on (a) when SIGTSTP is received, which process (parent or child) is handling the signal, and (b) what happens (or is done) by the signal handler (signhand).
void sighand(int signum){
/* supposedly need to reset this here (per "man signal" and
it's discussion of signals under linux), but doesn't actually
seem to matter. Documentation != observed program behavior. */
kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);
}
(3) Modify the program in Example 2 so that (a) the signal is handled by the child (not the parent), and (b) output message (fprintf statement) in sighand for SIGTSTP will also print the PID of the current process handling the signal.
void sighand(int signum){
/* supposedly need to reset this here (per "man signal" and
it's discussion of signals under linux), but doesn't actually
seem to matter. Documentation != observed program behavior. */
kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);
}
EX 1:
#include "apue.h"
static void sig_usr(int); /* one handler for both signals */
int
main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can’t catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can’t catch SIGUSR2");
for ( ; ; )
pause();
}
static void
sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1 ");
else if (signo == SIGUSR2)
printf("received SIGUSR2 ");
else
err_dump("received signal %d ", signo);
}
Commands for EX 1:
$ ./a.out &
[1] 7216
$ kill -USR1 7216
received SIGUSR1
$ kill -USR2 7216
received SIGUSR2
$ kill 7216
[1]+ Terminated
./a.out
EX 2:
// minishell part 1 - Shell sample code with execvp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define MAX_ARG 10
int pid_child;
/*** Main ***/
void sighand(int signum);
int main(){
// declare variables
char *prompt = "sh1$ ";
char line[256];
char line1[256];
char program[32], command[32], *args[MAX_ARG];
int pid, status, i;
// print the prompt
printf("%s", prompt);
// Try getting input. If error or EOF, exit
while(fgets(line, sizeof line, stdin) != NULL)
{
// fgets leaves ' ' in input buffer. ditch it
line[strlen(line)-1] = '';
strcpy(line1,line);
// If the input is not null, get in
if (line != NULL) {
// parse command and arg
args[0] = strtok(line, " "); // grab command
for(i=1; i<10; i++) // grab arguments
args[i] = strtok(NULL, " ");
strcpy(command, args[0]); // store command variable
// check to see if the command is 'exit'
if(!strcmp("exit", args[0])) {
exit(0);
}
// check to see if the command is 'cd'
else if(!strcmp("cd", args[0])) {
chdir(args[1]);
}
// handles all other commands
else {
pid = fork(); // fork to create new child
if(pid == 0) { // code to be run by the child
signal(SIGTSTP, SIG_IGN);
status = execvp(command, args); // execute
exit(0); // exit child
}
else { // code to be run by the parent
pid_child = pid;
signal(SIGTSTP, sighand);
waitpid(-1); // wait for children to finish
signal(SIGTSTP, SIG_DFL);
}
}
}
// print the prompt
printf("%s", prompt );
}
return 0;
}
void sighand(int signum)
{
/* supposedly need to reset this here (per "man signal" and
it's discussion of signals under linux), but doesn't actually
seem to matter. Documentation != observed program behavior. */
//signal(signum, nyahnyah);
kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);
}
$ ./a.out &
[1] 7216
$ kill -USR1 7216
received SIGUSR1
$ kill -USR2 7216
received SIGUSR2
$ kill 7216
[1]+ Terminated
./a.out
Explanation / Answer
Explain the logic of the program in Example 2, especially on
(a) when SIGTSTP is received, which process (parent or child) is handling the signal ?
Ans. Following are the contents from your code.
if(pid == 0) { // code to be run by the child
signal(SIGTSTP, SIG_IGN);
status = execvp(command, args); // execute
exit(0); // exit child
}
else { // code to be run by the parent
pid_child = pid;
signal(SIGTSTP, sighand);
waitpid(-1); // wait for children to finish
signal(SIGTSTP, SIG_DFL);
}
This signal function takes two arguments. First argument is the signal to be handled and the 2nd argument is the action to be done on the reciept of that signal. There are three actions can be done on the receipt of a signal.
SIG_DFL ----- Default action to be taken. These default actions are enabled for every signal and no need to call signal() function to enable it. The default actions for every signal is pre-defined and cannot be modified. The default action for the signal SIGTSTP is to suspend the process which receives the signal.
SIG_IGN ----- Signal has to be ignored. The action makes the process to ignore the signal upon receipt.
UserDefined ----- User can manually define a function with actions i.e. what to be done when a process receives a signal.
Note : fork() returns 0 to the child process and the child's process id to the parent.
so pid is 0 for child and pid = x for parent, when x is child's process id.
The parent block executes the following code.
pid_child = pid; // child_pid variable is updated with pid. i.e. child_pid now holds the process id of child.
signal(SIGTSTP, sighand); // we are making parent process to call sighand() when it received SIGTSTP. So basically parent is hadling the signal.
waitpid(-1); // wait for children to finish. This is not the correct syntax. You have to use wait(-1) or waitpid(child_pid, NULL, 0);
signal(SIGTSTP, SIG_DFL); // After killing the child, we are changing the action for the signal to default.
Explanation: When user enters CTRL+Z in place of any command, At this point, parent will receive SIGTSTP (because of ctrl+z), and it has to go to suspended state. So parent calls the signal handler to do the cleanup process. Parent does the cleaning by following code.
kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum); // we are printing some message with signal number to std error.
By sending signal SIGKILL to child process. This signal causes the child to terminate. But note that, here child pid doesn't hold any valid process id because the fork() has not been called when user pressed ctrl+z. So the variable child_pid is holding older child's process id (from the last command if executed), which had been killed by exit(0) after executing execvp(). So this is an invalid operation. Parent is trying to kill an invalid child process.
Whereas in child, we are executing
signal(SIGTSTP, SIG_IGN);
Which will cause the signal to be ignored upon receiption. The signal SIGTSTP can be given manually by user using ctrl+z, or when you try to do some invalid memory operations the os will try to suspend the process. Thus child will ignore the signal SIGTSTP.
(b) what happens (or is done) by the signal handler (signhand).
I think the answer to this question is explained in previous question answer. The parent is basically trying to do the cleanup by killing the child (by sending SIGKILL which causes the receiving process to be terminated). This case is only useful, when you have entered a command which takes much more time to execute and you dont want to wait, so you have given ctrl+z, then parent will try to terminate the program.
(3) Modify the program in Example 2 so that (a) the signal is handled by the child (not the parent), and (b) output message (fprintf statement) in sighand for SIGTSTP will also print the PID of the current process handling the signal.
If you want the child to handle this you can do this by using following code. Now we will call sighand in child. So when child receives SIGTSTP it will terminate itself. And if parent receives SIGTSTP, the default action is to terminate. So parent will terminate and the child will receive signal SIGHUP, whose default action is to terminate process. So child will automatically terminate if parent terminate.
// minishell part 1 - Shell sample code with execvp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_ARG 10
int pid_child;
/*** Main ***/
void sighand(int signum);
int main(){
// declare variables
char *prompt = "sh1$ ";
char line[256];
char line1[256];
char program[32], command[32], *args[MAX_ARG];
int pid, status, i;
// print the prompt
printf("%s", prompt);
// Try getting input. If error or EOF, exit
while(fgets(line, sizeof line, stdin) != NULL)
{
// fgets leaves ' ' in input buffer. ditch it
line[strlen(line)-1] = '';
strcpy(line1,line);
// If the input is not null, get in
// if (line != NULL) {
if (strlen(line)) {
// parse command and arg
args[0] = strtok(line, " "); // grab command
for(i=1; i<10; i++) // grab arguments
args[i] = strtok(NULL, " ");
strcpy(command, args[0]); // store command variable
// check to see if the command is 'exit'
if(!strcmp("exit", args[0])) {
exit(0);
}
// check to see if the command is 'cd'
else if(!strcmp("cd", args[0])) {
chdir(args[1]);
}
// handles all other commands
else {
pid = fork(); // fork to create new child
if(pid == 0) { // code to be run by the child
signal(SIGTSTP, sighand);
status = execvp(command, args); // execute
exit(0); // exit child
}
else { // code to be run by the parent
pid_child = pid;
waitpid(pid_child, NULL, 0); // wait for children to finish
}
}
}
// print the prompt
printf("%s", prompt );
}
return 0;
}
void sighand(int signum)
{
/* supposedly need to reset this here (per "man signal" and
it's discussion of signals under linux), but doesn't actually
seem to matter. Documentation != observed program behavior. */
//signal(signum, nyahnyah);
// kill(pid_child,SIGKILL);
fprintf(stderr, "nyah, nyah Mr. #%d. ", signum);
printf("Process %d is handling signal %d ", getpid(), signum);
kill(getpid(),SIGKILL); //As child itself is handling this, call getpid(), which will return its own pid and send SIGKILL to itself
}
Ping me back for doubts. Thanks.
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.