I have a code segment that looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
pid_t first, last;
last = fork();
if (last == 0) {
char *args[] = { "./last", NULL };
char *env[] = { NULL };
execve("./last", args, env);
_exit(1);
} else if (last == -1) {
fprintf(stderr, "Error: failed to fork last.\n");
exit(1);
}
first = fork();
if (first == -1) {
fprintf(stderr, "Error: failed to fork first.\n");
exit(1);
} else if (first > 0) {
int status;
waitpid(first, &status, 0);
} else {
char *args[] = { "./first", NULL };
char *env[] = { NULL };
execve("./first", args, env);
_exit(1);
}
return 0;
}
This works fine in a sense that I can see processes being invoked and running. However, my issue is that process last has an infinity loop, and when process first terminates, it still stays running. Is there a way in C to force process last here to terminate also when process first finishes?
kill() should be of interest. From the man page of kill():
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
Since fork() returns the child PID in the parent, you can use that to call kill(). Here is the modified code and test run.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h> // Modification 1
int main(int argc, char *argv[]) {
pid_t first, last;
last = fork();
if (last == 0) {
char *args[] = { "./last", NULL };
char *env[] = { NULL };
execve("./last", args, env);
_exit(1);
} else if (last == -1) {
fprintf(stderr, "Error: failed to fork last.\n");
exit(1);
}
first = fork();
if (first == -1) {
fprintf(stderr, "Error: failed to fork first.\n");
exit(1);
} else if (first > 0) {
int status;
waitpid(first, &status, 0);
kill(last, SIGINT); // Modification 2
} else {
char *args[] = { "./first", NULL };
char *env[] = { NULL };
execve("./first", args, env);
_exit(1);
}
return 0;
}
Terminal Session (before):
$ ls test first last
first last test
$ ./test
$ ps aux | grep last
root 165130 0.0 0.0 2136 752 pts/3 S 16:58 0:00 ./last
root 165135 0.0 0.0 6136 892 pts/3 S+ 16:58 0:00 grep last
Terminal Session (after):
$ ls test first last
first last test
$ ./test
$ ps aux | grep last
root 165240 0.0 0.0 6136 836 pts/3 S+ 17:01 0:00 grep last
Regarding which signal to be passed: anyone whose default action is termination. You can find more from the signal man page. Since I don't know what is exactly in the last executable, I assume that there is no signal handler registered for SIGINT and hence when last gets SIGINT, the program is terminated by default.
One way to do that is by sending a SIGTERM or SIGKILL signal to the first process, using the function kill()
An example:
kill(first, SIGTERM);
When you send that signal, if you want, you can make some "clean up" on that process. To do that you need to capture and hadle the signal. In that case i would recommend the use of SIGTERM.
For signal handling, take a look at this.
Related
I run my C program on debian-linux ,the program is expected to output
$ ./kill_raise
Child(pid : 4877) is waiting for any signal
Parent kill 4877
but I just got
Parent kill 4877
the string in subprocess (Child(pid : %d) is waiting for any signal) is not print out,why?
and this is my program
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
int res;
pid = fork();
if (pid < 0)
{
perror ("Fork error\n");
exit(0);
}
if (pid == 0)
{
printf ("child(pid:%d) is waiting for signal\n", getpid());
raise(SIGSTOP);
exit(0);
}
else
{
if ((waitpid(pid, NULL, WNOHANG)) == 0)
{
if ((res = kill(pid, SIGKILL)) == 0)
{
printf ("parent kill %d\n", pid);
}
}
waitpid(pid, NULL, 0);
exit(0);
}
}
You're hitting a race condition. The parent is executing the kill before the child can execute its printf. Remove WNOHANG so the parent actually waits for the child to stop. Also, you need to add WUNTRACED so that waitpid reports the stop (by default, it will only report termination). Finally, you shouldn't be testing wait()'s return value against 0 in this case.
The following code simulate the pipe and grep operation by forking process and using execve system call. The output seems fine, however, the grep process seems never exit (still running in the back) until the whole process ends. What's the problem? It is abnormal since using grep in shell always exit.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[], char *env[]) {
char ch[32];
while (1) {
scanf("%s", ch);
int pd[2];
if (pipe(pd) == -1) {
perror("Pipe failed");
exit(1);
}
int childPid, pid;
if ((childPid = fork()) < 0) {
perror("fork failed\n");
exit(1);
} else {
// parent process
if (childPid) {
int status;
wait(&status);
// print exit code of child process
printf("Exit code %d\n", status);
}
// child process, execute command
else {
// fork a child
if ((pid = fork()) < 0) {
perror("fork failed\n");
exit(1);
}
if (pid) { // parent as pipe WRITER
close(pd[0]);
close(1);
// replace input with pipe
dup(pd[1]);
char* cmds[] = { "/bin/cat", "aa", 0 };
execve(cmds[0], cmds, env);
exit(0);
} else { // child as pipe READER
close(pd[1]);
close(0); // close its READ end
dup(pd[0]);
char* cmds[] = { "/bin/grep", "rw", 0 };
execve(cmds[0], cmds, env);
exit(0); // never been here
}
}
}
}
return 0;
}
Here is the output I monitor the process before and after running this program once.
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58073 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58075 1886 0 01:43 pts/11 00:00:00 /bin/grep drw
hengstar 58077 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
The exec family of functions never returns. Their purpose is to load a new program to replace the current program running in the process.
If an exec function returns, it means there was an error.
I laughted so hard I myself when I found this! Did you noticed that while(1) above? Well, everything is okay, except for that! Without it, your program works as you describe it to.
BTW: It happens that if a process's parent dies, the child becomes adopted by init, a.k.a (pid_t)1. That was happening with your code with while(1).
I am to implement a nameless pipe, and I must execute the command in the parent process, not in any of his child. every "-" equals a call for a pipeline ("|"), also part of the assignment
I have this code. can someone explain to me why it doesn't work?
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void my_exec(char* cmd, char** argv)
{
int pipefd[2], f;
if (pipe(pipefd) < 0)
perror("pipe creation failed");
f = fork();
assert(f >= 0);
if (f == 0) {
// inside son process - connecting STDOUT to pipe write
if (dup2(pipefd[1], STDOUT_FILENO) < 0)
perror("dup2 failed");
close(pipefd[0]);
close((int)stdout);
} else {
// inside parent process - connecting STDIN to pipe read and execute command with args
if (dup2(pipefd[0], STDIN_FILENO) < 0)
perror("dup2 failed");
close(pipefd[1]);
close((int)stdin);
if (execvp(cmd, argv) < 0)
perror("execvp failed");
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[1], &argv[1]);
argv = &argv[i];
argc -= i;
i = 0;
}
}
char* args[argc];
args[argc-1] = NULL;
for (i = 1; i < argc; ++i) {
args[i-1] = argv[i];
}
if (execvp(args[0], args) == -1)
perror("execvp failed");
return;
}
the command :
./mypipe.o ls -l - grep "pipe"
returns
total 24
-rw-rw-r-- 1 omer omer 1463 May 23 19:38 mypipe.c
-rwxrwxr-x 1 omer omer 7563 May 23 19:37 mypipe.o
-rw-rw-rw- 1 omer omer 873 May 23 20:01 nice.c
-rwxrwxr-x 1 omer omer 7417 May 23 19:44 nice.o
-rw-rw-r-- 1 omer omer 0 May 23 17:10 try
which obviouslly means the pipe didnt work... any ideas?
I need to make sure that Each call to np_exec starts a single child process that continues parsing the rest of the arguments, while the original parent process executes the given program and arguments (using execvp),
EDIT:
i think i found the mistake:
i switch the "read- write " ends of the pipe.
the correct function:
void np_exec(char* cmd, char** argv)
{
int pipefd[2];
int file;
if (pipe(pipefd) < 0)
perror("failed to create pipe");
file = fork();
assert(file >= 0);
if (file != 0) {
// inside parent process - connecting STDOUT to pipe write and execute command with args
if (dup2(pipefd[WRITE], STDOUT_FILENO) < 0)
perror("the function dup2 failed");
close(pipefd[READ]);
close((int)stdout);
if (execvp(cmd, argv) < 0)
perror("the function execvp failed");
} else {
// inside son process - connecting STDIN to pipe read
if (dup2(pipefd[READ], STDIN_FILENO) < 0)
perror("the function dup2 failed");
close(pipefd[WRITE]);
close((int)stdin);
}
}
UPDATE: So, turns out my original answer was way off base. I didn't fully understand what you wanted to do until now.
Your code is mostly right. It doesn't work for a very simple reason: shells assume that a command is done when the process that started it finishes.
Allow me to explain with an example. Imagine you run ./mypipe a - b - c.
Here's what happens under the hood:
The shell forks a process to execute mypipe. This is when your program starts execution.
Your program forks, and the parent calls exec(a). The child keeps parsing the other arguments and handling pipe creation, as it is supposed to.
So, now, at this point, you have the parent running program a - which, to the eyes of the shell, is the process that corresponds to the command ./mypipe, and you have a child process, which the shell completely neglects, doing the job of mypipe - setting up pipes, reading the rest of the programs to execute, etc.
So, now you have a race condition. Because the process behind mypipe has been replaced by the program a, as soon as a terminates, the shell assumes that the command you typed is done (that is, it assumes mypipe is done), and prints the prompt, expecting you to type the next command.
The problem: a terminates quickly, and your child process is still going over the other programs list and setting up pipes, redirecting input and output, and all that. So, for example, the child process may still be working on creating pipes and parsing the other programs, while by that time a has already finished and written everything to stdout - Oops!
How do you fix this? Simple: invert the order of command execution. Start by executing the last command, then the second to last, etc. Why? Because if you do that, the parent will be the last on the pipeline, so it blocks waiting for input to arrive in the pipe read channel. When the parent terminates, it means the last command in the pipeline has terminated, which is exactly what we want. There are no race conditions, and the shell isn't tricked into thinking our command is done when it actually isn't.
So, it's all a matter of parsing the programs list from right to left instead of left to right. Plus, if you do it, you won't need the auxiliary args array anymore, which is great!
Here's the code - tested and working:
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void my_exec(char* cmd, char** argv)
{
int pipefd[2], f;
if (pipe(pipefd) < 0)
perror("pipe creation failed");
f = fork();
assert(f >= 0);
if (f == 0) {
if (dup2(pipefd[1], STDOUT_FILENO) < 0)
perror("dup2 failed");
close(pipefd[0]);
} else {
if (dup2(pipefd[0], STDIN_FILENO) < 0)
perror("dup2 failed");
close(pipefd[1]);
if (execvp(cmd, argv) < 0)
perror("execvp failed");
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = argc-1; i >= 1; i--) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[i+1], &argv[i+1]);
argc = i;
}
}
if (execvp(argv[0], &argv[1]) == -1)
perror("execvp failed");
return 0;
}
I'm trying to write my own C shell. I'm wondering how to make calling 'kill' in the command line work. For example,
shell> kill 2
shell: process 2 has been killed
Line 1 is user input 'kill 2'
Line 2 is program-printed message of what has been done.
I know I have to take the pid as the argument I believe and send the SIGKILL signal.
Using something like
kill(pid, SIGKILL);
How do I connect this kill function to respond when a user inputs 'kill 2' in a C implementation? I guess I'm having a hard time connecting it to the command line arguments for implementation. I might need strtok/atoi?
Thank you.
Better you go for "getopt" which might look as fallows,
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
int pid;
if((pid = getopt(argc, argv, "0123456789")) != -1)
if(isdigit(pid)){
if(kill(pid, SIGKILL) == -1){
perror("KILL:");
exit(0);
}
}else{
printf("Input format: kill <pid>");
}
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
int main(int argc, char **argv)
{
if (argc < 3)
{
printf("usage: ./kill OPERATION(kill/cont) PID\n");
return -1;
}
if(strcmp(argv[1],"kill") == 0 )
{
printf("Kill:\n");
kill(atoi(argv[2]), SIGKILL);
}
else if(strcmp(argv[1],"cont") == 0)
{
printf("cont:\n");
kill(atoi(argv[2]), SIGCONT);
}
else
{
printf("Kill default:\n");
kill(atoi(argv[2]), SIGKILL);
}
return 0;
}
I have the following code draft.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf( "usage: %i filename", argc );
pid_t pID = fork();
if (pID == 0) // child
{
// Code only executed by child process
printf("Child PID: %i", pID);
int file = open("/tmp/rtail", O_CREAT | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file,1);
char tmp[30];
sprintf(tmp, "cat `tail -f %s`", argv[1]);
}
else if (pID < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
}
else // parent
{
}
// Code executed by both parent and child.
return 0;
}
How do I pass command line arguments to a child process? For example, running ./app alch.txt I want
sprintf(tmp, "cat `tail -f %s`", argv[1]);
to produce
cat `tail -f alch.txt`
in tmp.
How do I pass command line arguments to a child process?
You don't need to do anything special; fork ensures that each process gets all local variables, including argv.
Sorry for the trouble, it indeed works fine. My earlier version didn't work for some reason, but apparently I've changed something to make it right. Will run my code before a question next time.