libuv: difference between fork and uv_spawn? - c

Recently I have been playing around with Libuv. I don't get the programming model as far as child processes are concerned.
For example look at the following code:
uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
void on_exit(uv_process_t* proc, long int io, int hj)
{
std::cout<<"on exit call back"<<std::endl;
}
int main()
{
loop = uv_default_loop();
char* args[3];
args[0] = "mkdir";
args[1] = "test-dir";
args[2] = NULL;
options.exit_cb = on_exit;
options.file = "mkdir";
options.args = args;
int r;
r = uv_spawn(loop, &child_req, &options);
std::cout<<r<<std::endl;
if (r) {
std::cout<<"from line 231"<<std::endl;
fprintf(stderr, "%s\n", uv_strerror(r));
return 1;
} else {
fprintf(stderr, "Launched process with ID %d\n", child_req.pid);
}
return uv_run(loop, UV_RUN_DEFAULT);
}
Here the output printed on console is:
0
Launched process with ID 511168
on exit call back
In my understanding uv_spawn acts like fork(). In child process the value of r is 0 and in parent process it is non-zero. So from line 231 should also be printed. But evidently it is not. I read the documentation end to end but I have no clue.
Any help will be appreciated.

n my understanding uv_spawn acts like fork()
And then like execve and child becomes mkdir. So child executes mkdir, and only parent returns to your code.

Related

Getting result of exec*() from child process without waiting in any case (not using pipes and vfork())

I am working on custom wrappers for Unix-specific system calls now. And the last problem I met is about how to create a complete function for creating new processes with another image. And I want this function to return TRUE or FALSE. The last piece of this puzzle is how to get a result of exec*() from a child process without waiting for it's end in case of exec*()'s success. In other words, I need to get FAIL of SUCCESS result of exec*() quickly and continue execution of a parent process.
And I don't want to use vfork() and pipes.
My current results:
Using vfork() and a volatile variable for keeping result made their work.
static int QCreateProcess(char* args, ...)
{
if (processInfoStruct == NULL)
{
return Q_ERROR;
}
volatile int result = TRUE;
pid_t procHandle = vfork();
if (procHandle == 0)
{
char* argsToExec[2] = { args, NULL };
execv(argsToExec[0], argsToExec);
result = FALSE;
_exit(EXIT_FAILURE);
}
else if (procHandle == -1)
{
processInfoStruct->processHandle = NULL;
result = FALSE;
}
else
{
if (result == TRUE)
{
waitpid(procHandle, NULL, WNOHANG);
processInfoStruct->processHandle = procHandle;
}
else
{
processInfoStruct->processHandle = 0;
result = FALSE;
}
}
return result;
}
This code works and returns correct results.
How can this be implemented using fork() and waitpid() without the status variable (it won't work with fork() anyway...) and pipes? I tried to find solutions with different options for the last function (waitpid()), but a desired combination was not found.

Pointers Accessing Incorrect Addresses OS161

I'm using OS161, and I have a piece of code that looks like this in process.c:
void
process_exit(int exit_code)
{
splhigh();
curthread->p_process->exited_flag = 1; // Process exited
curthread->p_process->exit_code = exit_code;
struct process * process;
// Now all the child process will be orphant, we need to adopt them
// Search through the process table, change all children's ppid
for (int i = 0; i < array_getnum(process_table); i++) {
*process = array_getguy(process_table, i);
if (process != NULL && process->ppid == curthread->p_process->pid) { // We found a child here, it should be a orphant now
process->ppid = 1; // Now the init(boot/menu) process should adopt the child process
process->adopted_flag = 1;
}
}
V(curthread->p_process->sem_exit); // Now signal processes which are waiting
// Now exit the thread
thread_exit();
}
The definition of process struct:
struct process{
char* process_name;
struct addrspace *process_vmspace;
struct vnode *process_cwd;
pid_t pid;
pid_t ppid;
int adopted_flag;
int exited_flag;
int exit_code;
struct thread *p_thread;
struct semaphore *sem_exit;
};
I'm getting an END OF FILE error, and GDB told me it was where process_exit was defined. I'm not super familiar with OS programming, does anyone know why this could be happening?
Edit: This was the GDB message:
panic: Fatal exception 3 (TLB miss on store) in kernel mode
panic: EPC 0x8001a008, exception vaddr 0x18
sleep: Dropping thread <boot/menu>
panic: I can't handle this... I think I'll just die now...
I did gdb list *0x8001a008 and it pointed to the curthread->p_process->exited_flag = 1;.
Given #ctx's analysis, try this code to prove whether we're on the right track:
void
process_exit(int exit_code)
{
splhigh();
if (curthread && curthread->p_process)
{
curthread->p_process->exited_flag = 1; // Process exited
curthread->p_process->exit_code = exit_code;
}
// same code as before below here ...

Thread don't terminate their job

I am writing a concurrent C program where I want to wait for all threads to finish in the main().
Based on this solution, I wrote the following code in main():
// Create threads
pthread_t cid[num_mappers];
int t_iter;
for (t_iter = 0; t_iter < num_mappers; t_iter++){
pthread_create(&(cid[t_iter]), NULL, &map_consumer, NULL);
}
// Wait for all threads to finish
for (t_iter = 0; t_iter < num_mappers; t_iter++){
printf("Joining %d\n", t_iter);
int result = pthread_join(cid[t_iter], NULL);
}
printf("Done mapping.\n");
The function passed into threads is defined as:
// Consumer function for mapping phase
void *map_consumer(void *arg){
while (1){
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
return NULL;
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);
// Do the mapping
printf("%s\n", filename);
g_map(filename);
}
}
The threads are all successfully created and executed, but the join loop will never finish if num_mappers >= 2.
You return without unlocking the mutex:
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
return NULL; <-- mutex is still locked here
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);
So only one thread ever returns and ends - the first one, but since it never unlocks the mutex, the other threads remain blocked.
You need something more like
pthread_mutex_lock(&g_lock);
if (g_cur >= g_numfull){
// No works to do, just quit
pthread_mutex_unlock(&g_lock);
return NULL;
}
// Get the file name
char *filename = g_job_queue[g_cur];
g_cur++;
pthread_mutex_unlock(&g_lock);

c - Unable to write data to a file from inside a child process

I have two furnctions in a c program, create_open_log_file() and write_to_log_file() and a global file pointer.
When these functions get called, the log file is created as expected (I can see it in the dir). Then write_to_log_file() is called and a child process is created. At this point I would have expected that the string test test test would be written to this file in a loop. The string child process is printed on the terminal o I know the code is being excuted. However, the log file has no content?
I'd appreicate if somebody could tell me if I am doing something obvious wrong.
FILE *log_file;
static void create_open_log_file(void) {
char filename[40];
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char s[64];
strftime(s, sizeof(s), "%a%b%d%T", tm);
sprintf(filename, "dut1_serial_log_%s", s);
log_file = fopen(filename,"w");
if (log_file == NULL) {
perror("Error creating log file");
}
}
static write_to_log_file() {
// Prevent killed child-processes remaining as "defunct"
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction( SIGCHLD, &sigchld_action, NULL ) ;
// Duplicate ("fork") the process. Will return zero in the child
// process, and the child's PID in the parent (or negative on error).
int pid = fork();
global_pid = pid;
if( pid < 0 ) {
printf( "Fork failed\n" ) ;
return 1 ;
}
// ------------ Child process
if( pid == 0 ) {
// ------------ Child process
// Open log file and write to it from /dev/USB1
create_open_log_file();
while( 1 ) {
printf( "child process\n" ) ;
char str[] = "test test test";
fwrite(str , 1 , sizeof(str) , log_file);
sleep(1) ;
}
return 0 ; //never reached
}
}
From a quick code review, it looks like the child process never closes the file, hence the data may or may not reach the file.
Ah. Since it is an infinite loop you really don't intend a close. Yes. Flushing the buffer will generally get the data all the way to the disk, which is what I am guessing is what you are really after.
Flushing a FILE is needed; otherwise your output just sits in memory (the file's buffer block) till the block is filled up, or you fclose the file pointer. That's part of the difference between buffered stdio, and bare file handles.

Fork Infinite Loop

I am trying to make an directory monitoring program in C. So far it detects the subdirectories of a main directory, however the problem happens when I try to monitor the subdirectories of the subdirectories.
char * const son[] = { argv[0], name, NULL };
pid_t pid = fork();
if (pid == 0) {
execvp(son[0], son);
}
This code should have created a child process which would monitor the subdirectories. name is a string with the subdirectory "Desktop/test", for example.
I tried to print "name" before and it is the subdirectory I want so the problem isn't here.
The program works flawlessly until I add this. Once I add it, it enters an infinite loop, despite working previously. I also want to point out I don't use signals so the problem doesn't come from them.
This of course is just an excerpt of he code. If you think I need to post the full code, which is much bigger, I will add it, although honestly I doubt the problem is in it since it worked perfectly before.
EDIT:
Better add all the code, argv[1] is the directory,argv[2] for how many minutes I want the program to run, argv[3], it's the pause between each scan. It works if I remove the excerpt above and I know it's a bit confusing but if you have any questions just say.
int main(int argc, char *argv[]) {
char** direc;
int direct_pos = 0;
direc = (char**) malloc(2 * sizeof(char*));
double actual_time = 0;
double MAXTIME = atof(argv[2]);
MAXTIME = MAXTIME * 60;
double IterationTime = atof(argv[3]);
time_t start = time(0);
char dot2[100];
char dot[100];
sprintf(dot, "%s/.", argv[1]);
sprintf(dot2, "%s/..", argv[1]);
direct_pos++;
direc[direct_pos - 1] = (char*) malloc(strlen(dot) * sizeof(char));
strcpy(direc[direct_pos - 1], dot);
direct_pos++;
direc[direct_pos - 1] = (char*) malloc(strlen(dot2) * sizeof(char));
strcpy(direc[direct_pos - 1], dot2);
if (argc != 4) {
fprintf(stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
while (actual_time < MAXTIME) {
DIR *dirp;
if ((dirp = opendir(argv[1])) == NULL) {
perror(argv[1]);
exit(2);
}
struct stat stat_buf;
struct dirent *direntp;
while ((direntp = readdir(dirp)) != NULL) {
char name[100];
sprintf(name, "%s/%s", argv[1], direntp->d_name);
if (lstat(name, &stat_buf) == -1)
{
perror("lstat ERROR");
exit(3);
}
if (S_ISDIR(stat_buf.st_mode))
{
int x;
for (x = 0; x <= direct_pos; x++) {
if (x == direct_pos) {
char**newarray;
newarray = (char**) malloc((direct_pos + 1)* sizeof(char*));
int l;
for (l = 0; l < direct_pos; l++) {
//printf("\nxxxx%d\n", sizeof(direc[l]));
newarray[l] = (char*) malloc((strlen(direc[l])+1)
* sizeof(char));
strcpy(newarray[l], direc[l]);
}
direc = newarray;
direc[direct_pos] = (char*) malloc(sizeof(name)
* sizeof(char));
direc[direct_pos] = strcpy(direc[direct_pos], name);
direct_pos++;
double seconds_since_start = difftime(time(0), start);
double new_time = (MAXTIME - seconds_since_start) / 60;
char time_array[10];
sprintf(time_array,"%f",new_time);
char * const son[] = { argv[0], name, time_array,
argv[3], NULL };
printf("\n%s\n",son[1]);
x = direct_pos + 2;
pid_t pid = fork();
if (pid == 0) {
execvp(son[0], son)==-1);
break;
}
} else if (strcmp(name, direc[x]) == 0) {
x = direct_pos + 2;
}
}
}
}
sleep(IterationTime);
actual_time += IterationTime;
closedir(dirp);
}
exit(0);
}
You have a program that forks and then runs a new copy of itself. Think of it as endless recursion. There's no need to exec a new instance, simply write your code so that the program continues down one of two paths depending on the returned process ID.
But that's not the correct solution.
The correct solution is not to fork at all. You gain almost no benefit from having a thousand processes that look at one directory, versus a single process that looks at a thousand directories. Actually, you may be far worse, by putting load on the scheduler.
It looks like you always fork no matter what. I would put a check in there to make sure that if you have no subdirectories in the current directory that you don't fork.
You are forking a child-process, but what does the parent process do after the call to fork()? It seems you are wanting to recursively fork processes, but in order for that to work properly, you will have to-do the following:
1) Check to see if there are any sub-directories in the current directory ... if there aren't any, then exit(), otherwise read all the sub-directories in the current directory.
2) For each sub-directory fork a process. If the fork is the child (i.e., pid == 0), then make a call to execvp().
3) If pid != 0, then you're in the parent process. Rather than trying to sleep for some period of time, make a call to wait(), and keep on repeating the call to wait() until there are no child processes left.
4) Once there are no child-processes left (i.e., wait() returns an error status such that errno == ECHILD), then you can make a call to exit() in the parent process.
So if you follow those 4 steps, you should not incurr any infinite loops. Every child process will at some point exit once they reach a directory with no more sub-directories, and every parent will wait for it's children before exiting so you won't end up with any orphaned processes. If you do end up with an infinite loop, then it's because a child process is not exiting, which would then point to the logic used to detect if there are any sub-directories in the current directory, or you're not properly detecting that there are no longer any child left to wait for in a parent process.
Hope this helps,
Jason

Resources