I'm running custom Fedora 17 Kernel 3.3.0-0.rc5.git3.1.yfkm2.fc17.x86_64, and the warning was shown on dmesg:
[ 858.634304]
[ 858.634324] ===============================
[ 858.634350] [ INFO: suspicious RCU usage. ]
[ 858.634375] 3.3.0-0.rc5.git3.1.yfkm2.fc17.x86_64 #1 Not tainted
[ 858.634409] -------------------------------
[ 858.634435] kernel/pid.c:425 find_task_by_pid_ns() needs rcu_read_lock() protection!
[ 858.634478]
[ 858.634479] other info that might help us debug this:
[ 858.634480]
[ 858.634528]
[ 858.634529] rcu_scheduler_active = 1, debug_locks = 0
[ 858.634567] no locks held by monitor/10550.
[ 858.634591]
[ 858.634592] stack backtrace:
[ 858.634620] Pid: 10550, comm: monitor Not tainted 3.3.0-0.rc5.git3.1.yfkm2.fc17.x86_64 #1
[ 858.634666] Call Trace:
[ 858.634688] [<ffffffff810c8c55>] lockdep_rcu_suspicious+0xe5/0x100
[ 858.634727] [<ffffffff81086921>] find_task_by_pid_ns+0x81/0xa0
[ 858.634762] [<ffffffff81086962>] find_task_by_vpid+0x22/0x30
[ 858.634798] [<ffffffff8131ccd5>] yfkm2_is_pid_running+0x15/0x40
[ 858.634835] [<ffffffff8131ce54>] sys_yfkm2_monitor+0x14/0x80
[ 858.634870] [<ffffffff816a6ba9>] system_call_fastpath+0x16/0x1b
monitor is user application that call sys_yfkm2_monitor syscall passing a pid to it. The custom code worked as expected but I'm curious with the warning message shown on dmesg. What am I doing wrong?
The user application monitor.c:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#define SYS_yfkm2_monitor __NR_yfkm2_monitor
#define SYS_yfkm2_notifyme __NR_yfkm2_notifyme
int main (int argc, char *argv[])
{
if (argc < 2) {
printf("Error. Use %s <PID>\n", argv[0]);
return 1;
}
pid_t pid = atoi(argv[1]);
long ret;
ret = syscall(SYS_yfkm2_monitor, pid);
if (ret == 0){
printf("Sucess on adding %d!\n", pid);
return 0;
} else {
printf("Failure! Is %s a valid PID?\n", argv[1]);
return 1;
}
}
The Kernel code:
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#define YFKM2_KT_TIMEOUT (1*HZ) /* 1 second */
struct yfkm2 {
pid_t monitor; /* PID to monitor */
pid_t notifyme; /* PID to notify */
struct list_head list; /* Linked List struct */
};
/* How many Kernel Threads are running? */
atomic_t yfkm2_kthread_run_count = ATOMIC_INIT(0);
/* Define and initialize yfkm2_(linked)list */
LIST_HEAD(yfkm2_list);
/* Define and initialize yfkm2_(read&write)lock */
DEFINE_RWLOCK(yfkm2_lock);
/*
* yfkm2_is_pid_running(pid_t pid)
*
* Check if pid is running
*
* return 0 if pid is running
* return 1 if pid is not running
*/
int yfkm2_is_pid_running(pid_t pid)
{
struct task_struct *q;
q = find_task_by_vpid(pid);
if (q != NULL && q->pid == pid)
return 0;
return 1;
}
/*
* yfkm2_kill(pid_t pid)
*
* Kills pid
*
* return 0 if pid was running and send SIGKILL to pid
* return 1 if pid is not running
*/
int yfkm2_kill(pid_t pid)
{
struct task_struct *q;
q = find_task_by_vpid(pid);
if (q != NULL) {
force_sig(SIGKILL, q);
return 0;
}
return 1;
}
/*
* int yfkm2_kthread(void *data)
*
* The Kernel Thread
*
* Traverse the yfkm2_list looking for yfkm2->notifyme that are not 0.
* If any found, check if correspondent yfkm2->monitor is still running. If not
* kill yfkm2->notifyme. After traversing the list, check if the list is empty.
* If so return 0. If not sleep one second and start again.
*
* return 0 if yfkm2_list is empty
* should never return 1
*/
int yfkm2_kthread(void *data) /* data is NEVER used */
{
struct yfkm2 *yfkm2_tmp, *yfkm2_tmp2;
bool empty;
while (true) {
/* Needs write protection due possible item removal from list */
write_lock(&yfkm2_lock); /* Write lock */
list_for_each_entry_safe(yfkm2_tmp, yfkm2_tmp2,
&yfkm2_list, list) {
if (yfkm2_tmp->notifyme != 0) {
if (yfkm2_is_pid_running(yfkm2_tmp->monitor) != 0) {
yfkm2_kill(yfkm2_tmp->notifyme);
list_del(&yfkm2_tmp->list);
kfree(yfkm2_tmp);
}
}
}
write_unlock(&yfkm2_lock); /* Write unlock */
read_lock(&yfkm2_lock); /* Read lock */
empty = list_empty(&yfkm2_list);
read_unlock(&yfkm2_lock); /* Read unlock */
if (empty) {
/* The counter is increased at sys_yfkm2_notifyme()
* Before exit, decrease atomic run counter */
atomic_dec(&yfkm2_kthread_run_count);
return 0;
}
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(YFKM2_KT_TIMEOUT);
}
/* Before exit, decrease atomic run counter */
atomic_dec(&yfkm2_kthread_run_count);
return 1;
}
/*
* asmlinkage long sys_yfkm2_monitor(pid_t monitor)
*
* The system call that check if monitor correspond to a running pid and stores
* monitor at yfkm2_list->monitor
*
* return 0 if pid is running
* return 1 if pid is not running
*/
asmlinkage long sys_yfkm2_monitor(pid_t monitor)
{
struct yfkm2 *yfkm2_tmp;
if (yfkm2_is_pid_running(monitor) == 0) {
yfkm2_tmp = kmalloc(sizeof(*yfkm2_tmp), GFP_KERNEL);
yfkm2_tmp->monitor = monitor;
yfkm2_tmp->notifyme = 0;
write_lock(&yfkm2_lock);
list_add(&yfkm2_tmp->list, &yfkm2_list);
write_unlock(&yfkm2_lock);
return 0;
}
return 1;
}
/*
* asmlinkage long sys_yfkm2_notifyme(pid_t monitor, pid_t notifyme)
*
* The system call that looks for monitor at yfkm2_list->monitor. If found
* store notifyme at yfkm2_list->notifyme. It also starts the kernel thread
* if it is not running.
*
* return 0 if pid is running
* return 1 if pid is not running
*/
asmlinkage long sys_yfkm2_notifyme(pid_t monitor, pid_t notifyme)
{
struct yfkm2 *yfkm2_tmp;
bool found_monitored_pid = false;
write_lock(&yfkm2_lock); /* Write lock */
list_for_each_entry(yfkm2_tmp, &yfkm2_list, list) {
if (yfkm2_tmp->monitor == monitor) {
yfkm2_tmp->notifyme = notifyme;
found_monitored_pid = true;
break;
}
}
write_unlock(&yfkm2_lock); /* Write unlock */
if (found_monitored_pid) {
if (atomic_read(&yfkm2_kthread_run_count) < 1) {
/* The counter is decreased at yfkm2_kthread()
* Before start, increase atomic run counter */
atomic_inc(&yfkm2_kthread_run_count);
kthread_run(&yfkm2_kthread, NULL, "yfkm2_kthread");
}
return 0;
} else {
return 1;
}
}
You are not performing correct locking on the task list. For example, your yfkm2_kill() function should be:
int yfkm2_kill(pid_t pid)
{
struct task_struct *q;
rcu_read_lock();
q = find_task_by_vpid(pid);
if (q)
get_task_struct(q);
rcu_read_unlock();
if (q == NULL)
return 1;
force_sig(SIGKILL, q);
put_task_struct(q);
return 0;
}
...but your whole design appears to be severely racy. For example, one of the ->monitor tasks could exit and be replaced with a new, different task with the same PID before your kernel thread notices.
You seem to be running code without the required locks.
Such things tend to work, except that they crash once in a while (possibly a long while).
I don't know these functions so much, but it seems like find_task_by_vpid should be called under some RCU lock (probably the one that protects the process list), in read mode.
Related
Assume I have a program foo that simply prints forever but is stopped before then,
int main(int argc, char * argv[])
{
kill(getpid(), SIGSTOP);
while(1) {
printf("foo\n");
}
return 0;
}
And then I have a program bar that forks and runs foo:
int main(int argc, char * argv[])
{
pid_t pid = fork();
if (pid == 0)
{
execl("foo", "foo", NULL);
}
return 0;
}
Now, I want bar to be able to send SIGCONT to foo so it can go on its printing duties, and then later send SIGSTOP again, and even later send SIGCONT again, and so on.
I couldn't figure out how to do that. Any help?
EDIT: I figured out my problem. Apparently, the foo program isn't ready to accept signals immediately when it runs. When I did
sleep(5);
int rc = kill(pid, SIGCONT);
it worked.
pid_t pid = fork();
if (pid == 0)
{
execl("foo", "foo", NULL);
}
int rc = kill(pid, 18);
return 0;
Suggest: don't forget to handle errors codes from system calls! Work without handling of error codes is worse style! This two actions you can do in one program. In this case it's will be more effective:
#include <errno.h>
int main(int argc, char * argv[])
{
int rc = 0;
pid_t pid = fork();
if (pid == 0) /* child */
{
kill(getpid(), SIGSTOP);
if (rc == -1) {
perror("Something wrong while kill() in child");
}
while (1) {
printf("foo\n");
}
} else if (pid > 0) { /* father */
rc = kill(pid, SIGCONT);
if (rc == -1) {
perror("Something wrong while kill() in parent");
}
} else if (pid < 0) {
perror("Something wrong while fork()");
return -1;
}
return 0;
}
I need to change the buffer size in the character device by parameter, so that, for example, when I write
insmod chardev.ko len=6
echo "Have a nice day!" > /dev/chardev.ko
cat /dev/chardev.ko
My output would be:
Have a
I've simply tried to replace the msg[BUF_SIZE] by msg[len], but I receive an error 'variably modified at file scope'.
I have no idea how to proceed. Any hints?
Here's the code:
/*
* chardev.c: Creates a read-only char device that says how many times
* you've read from the dev file
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* for put_user */
/*
* Prototypes - this would normally go in a .h file
*/
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static int len=100;
module_param(len, int, S_IRUGO);
MODULE_PARM_DESC(len, "The length of buffer");
#define SUCCESS 0
#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */
#define BUF_LEN 100
/*
* Global variables are declared as static, so are global within the file.
*/
static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open?
* Used to prevent multiple access to device */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
/*
* This function is called when the module is loaded
*/
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk(KERN_ALERT "Registering char device failed with %d\n", Major);
return Major;
}
printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
printk(KERN_INFO "the driver, create a dev file with\n");
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
// minor numbers are used to differentiate multiple instances of a
// device that use the same driver.
printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
printk(KERN_INFO "the device file.\n");
printk(KERN_INFO "Remove the device file and module when done.\n");
return SUCCESS;
}
/*
* This function is called when the module is unloaded
*/
void cleanup_module(void)
{
/*
* Unregister the device
*/
#if 0
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0)
printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);
#endif
unregister_chrdev(Major, DEVICE_NAME);
}
/*
* Methods
*/
/*
* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open)
return -EBUSY;
Device_Open++;
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
msg_Ptr = msg;
try_module_get(THIS_MODULE);
return SUCCESS;
}
/*
* Called when a process closes the device file.
*/
static int device_release(struct inode *inode, struct file *file)
{
Device_Open--; /* We're now ready for our next caller */
/*
* Decrement the usage count, or else once you opened the file, you'll
* never get get rid of the module.
*/
module_put(THIS_MODULE);
return 0;
}
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
char *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
/*
* Number of bytes actually written to the buffer
*/
int bytes_read = 0;
/*
* If we're at the end of the message,
* return 0 signifying end of file
*/
if (*msg_Ptr == 0)
return 0;
/*
* Actually put the data into the buffer
*/
while (length && *msg_Ptr) {
/*
* The buffer is in the user data segment, not the kernel
* segment so "*" assignment won't work. We have to use
* put_user which copies data from the kernel data segment to
* the user data segment.
*/
put_user(*(msg_Ptr++), buffer++);
length--;
bytes_read++;
}
/*
* Most read functions return the number of bytes put into the buffer
*/
return bytes_read;
}
/*
* Called when a process writes to dev file: echo "hi" > /dev/hello
*/
static ssize_t
device_write(struct file *filp, const char *buffer, size_t length, loff_t * off)
{
int i;
#ifdef DEBUG
printk(KERN_INFO "device_write(%p,%s,%d", file, buffer, length)
#endif // DEBUG
for(i=0; (i<length && i<BUF_LEN) || i<len; i++)
{
get_user(msg[i], buffer+i);
}
msg_Ptr=msg;
return i;
}
https://pastebin.com/wQP9p42G
Thanks in advance!
I am using stm32f0 MCU.
I have a simple UART echo code in which every byte received will be transmitted out. I tested that it works. Here it is;
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) //current UART
{
HAL_UART_Transmit(&huart1, &Rx_data[0], 1, tx_timeout);
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
I do not feel comfortable with the code even though it works. Firstly, tx_timeout is 0 and most code examples are non-zero. I do not know the side effect. Secondly, HAL_UART_Transmit() is a blocking call and it is not advisable to use blocking calls inside an interrupt. So, I decided to use an interrupt for uart transmission HAL_UART_Transmit_IT()instead of a blocking call. Here is the modified code;
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) //current UART
{
HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1);
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
However, it does not work as expected. My PC transmits ASCII 12345678 to stm32. If things work as expected, the PC should be receiving 12345678 back. However, the PC receives 1357 instead. What is wrong with this code that uses HAL_UART_Transmit_IT()?
First:
As has been described in answers to your previous question null timeout just exclude wait for flag state. If you open HAL_UART_Transmit code - you will see that when you send 1 byte without timeout no any blocking state will!
Second:
It's not true method to send/receive one byte from a huge HAL's functions and their callbacks. I guess: next your question will "how i must implement parse there?". And I hope you will not insert you parse function in IRQ callback!
So generally you need buffers. And it is good idea to use cyclic buffer.
mxconstants.h:
/* USER CODE BEGIN Private defines */
/* Buffer's length must be select according to real messages frequency */
#define RXBUF_LEN 128 // must be power of 2
#define TXBUF_LEN 128 // must be power of 2
#define RXBUF_MSK (RXBUF_LEN-1)
#define TXBUF_MSK (TXBUF_LEN-1)
/* USER CODE END Private defines */
main.c:
uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
/* xx_i - counter of input bytes (tx - pushed for transmit, rx - received)
xx_o - counter of output bytes (tx - transmitted, rx - parsed)
xx_e - counter of echoed bytes */
volatile uint16_t rx_i = 0, tx_o = 0;
uint16_t rx_o = 0, rx_e = 0, tx_i = 0;
volatile uint8_t tx_busy = 0;
void transmit(uint8_t byte)
{
tx_buf[TXBUF_MSK & tx_i] = byte;
tx_i++;
tx_busy = 1;
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);
}
void main(void)
{
/* Initialization code */
/* ... */
/* Enable usart 1 receive IRQ */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
for (;;) {
/* Main cycle */
while (rx_i != rx_e) {
/* echo here */
transmit(rx_buf[RXBUF_MSK & rx_e]);
rx_e++;
}
while (rx_i != rx_o) {
/* parse here */
/* ... */
rx_o++;
}
/* Power save
while (tx_busy);
HAL_UART_DeInit(&huart1);
*/
}
}
stm32f0xx_it.c:
extern uint8_t rx_buf[RXBUF_LEN], tx_buf[TXBUF_LEN];
extern volatile uint16_t rx_i, tx_o;
extern uint16_t rx_o, rx_e, tx_i;
extern volatile uint8_t tx_busy;
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if((__HAL_UART_GET_IT(&huart1, UART_IT_RXNE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE) != RESET))
{
rx_buf[rx_i & RXBUF_MSK] = (uint8_t)(huart1.Instance->RDR & 0x00FF);
rx_i++;
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(&huart1, UART_RXDATA_FLUSH_REQUEST);
}
if((__HAL_UART_GET_IT(&huart1, UART_IT_TXE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE) != RESET))
{
if (tx_i == tx_o) {
__HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);
} else {
huart1.Instance->TDR = (uint8_t)(tx_buf[TXBUF_MSK & tx_o] & (uint8_t)0xFF);
tx_o++;
}
}
if((__HAL_UART_GET_IT(&huart1, UART_IT_TC) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TC) != RESET))
{
tx_busy = 0;
__HAL_UART_DISABLE_IT(&huart1, UART_IT_TC);
}
/* And never call default handler */
return;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
And third!!!
And about this:
Why HAL_UART_Transmit_IT not help/work?
Because it's too slow! And if you try to count HAL_BUSY results:
uint8_t Rx_data[5];
uint32_t tx_timeout = 0;
//Interrupt callback routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint32_t hal_busy_counter = 0;
if (huart->Instance == USART1) //current UART
{
if (HAL_UART_Transmit_IT(&huart1, &Rx_data[0], 1) == HAL_BUSY) {
hal_busy_counter++;
}
HAL_UART_Receive_IT(&huart1, Rx_data, 1); //activate UART receive interrupt every time on receiving 1 byte
}
}
When you pause MCU in debugger after data exchange - you will be suprised: it will be equal to count of missed chars.
Is there a vxWorks command that lists all the wv events that are compiled in the code. I am trying to segregate all the events on one of our products which uses vxWorks 6.5.
No there isn't a command giving you a list of WindView events of your code.
Why don't you search for all lines containing wvEvent of all your source files and remove the duplicates? This should produce a list of all events...
--- Update ---
I've written the following small library to hook symbols within the VxWorks symbol table. This can be used to hook system calls within applications loaded after a symbol was hooked.
The following mechanism works since the ld command resolves all unresolved references of the application using the system symbol table (e.g. calls to printf are replaced with the value of the symbol printf which is the address of the printf implementation).
Here is the code of the library:
#include <vxWorks.h>
#include <symLib.h>
#define MAX_HOOKS 256
typedef struct vxhook_struct
{
SYMTAB_ID symTblId; /** id of symbol table containing the symbol */
SYMBOL *symbol; /** pointer to symbol entry within symbol table */
void *originalValue; /** original value of symbol */
void *hookedValue; /** hooked value of symbol */
} VXHOOK;
/** counter of installed hooks */
static int hooks = 0;
/** list of installed hooks */
static VXHOOK hook[MAX_HOOKS];
/*
** symbolIterator
** VxWorks callback for symbol table iteration. See symEach(..) for more information.
*/
static BOOL symbolIterator(char *name,
int val,
SYM_TYPE type,
int arg,
UINT16 group)
{
BOOL result = TRUE; /* continue iteration */
char *pName;
pName = (char *)arg;
if ((pName != NULL) && (name != NULL))
{
if (!strcmp(name, pName))
{
result = FALSE; /* symbol found => stop iteration! */
}
}
return (result);
}
/*
** vxHookGet
** Searches the hook list for a specific entry and returns a pointer to it if found.
*/
static VXHOOK *vxHookGet(SYMTAB_ID symTblId, /* symbol table to seachr for name */
char *name, /* name of symbol */
int *index) /* optional return value of index within hook list */
{
VXHOOK *pHook = NULL;
int i;
if (index != NULL)
{
*index = -1;
}
for (i = 0; i < hooks; i++)
{
if ((hook[i].symTblId == symTblId) && (!strcmp(hook[i].symbol->name, name)))
{
pHook = &(hook[i]);
if (index != NULL)
{
*index = i;
}
break;
}
}
return (pHook);
}
/*
** vxHook
** Hooks a symbol within a symbol table (if not already hooked).
*/
STATUS vxHook(SYMTAB_ID symTblId, /* symbol table to seachr for name */
char *name, /* name of symbol */
void *value, /* new value to replace symbol value with */
void **pValue) /* optional pointer receiving original value from symbol table */
{
STATUS result = ERROR;
SYMBOL *symbol;
VXHOOK *pHook;
if ((name != NULL) && (value != NULL))
{
pHook = vxHookGet(symTblId, name, NULL);
if (pHook == NULL) /* symbol not hooked, yet? */
{
if (hooks < MAX_HOOKS) /* free entries available? */
{
pHook = &(hook[hooks]);
symbol = symEach(symTblId, symbolIterator, (int)name);
if (symbol != NULL) /* symbol found? */
{
pHook->symTblId = symTblId;
pHook->symbol = symbol;
pHook->originalValue = symbol->value;
pHook->hookedValue = value;
if (pValue != NULL)
{
*pValue = symbol->value;
}
/* install hook */
symbol->value = value;
printf("Value of symbol '%s' modified from 0x%x to 0x%x.\n", name, pHook->originalValue, pHook->hookedValue);
hooks++;
result = OK;
}
else
{
printf("Symbol '%s' not found in symTblId=0x%08x.\n", name, symTblId);
}
}
else
{
printf("Maximum number of hooks (%d) reached.\n", MAX_HOOKS);
}
}
else
{
printf("Symbol '%s' in symTblId=0x%08x already hooked.\n", name, symTblId);
}
}
return (result);
}
/*
** vxHookList
** Prints a list of all installed hooks to stdout.
*/
STATUS vxHookList(void)
{
int i;
printf(" name symTblId value original\n");
printf("------------------- ---------- ---------- ----------\n");
for (i = 0; i < hooks; i++)
{
printf("%3d: 0x%08x %s\n", i, hook[i].symTblId, hook[i].symbol->name);
}
return (OK);
}
/*
** vxUnhook
** Unhooks a hooked symbol (restoring the original value).
*/
STATUS vxUnhook(SYMTAB_ID symTblId, /* symbol table to search for name */
char *name) /* name of symbol */
{
STATUS result = ERROR;
VXHOOK *pHook;
int i;
pHook = vxHookGet(symTblId, name, &i);
if (pHook != NULL)
{
pHook->symbol->value = pHook->originalValue;
printf("Original value 0x%x of symbol '%s' restored.\n", pHook->originalValue, name);
/* remove hook */
hooks--;
/* shift remaining hooks... */
for (; i < hooks; i++)
{
memcpy(&(hook[i]), &(hook[i + 1]), sizeof(VXHOOK));
}
result = OK;
}
else
{
printf("Hook for '%s' (symTblId=0x%08x) not found.\n", name, symTblId);
}
return (result);
}
To hook wvEvent(..) calls you have to do the following:
#include <wvLib.h>
#include <symLib.h>
STATUS (*WVEVENT_FPTR)(event_t, char *, size_t);
WVEVENT_FPTR orig_wvEvent = wvEvent;
STATUS my_wvEvent(event_t usrEventId, /* event */
char * buffer, /* buffer */
size_t bufSize) /* buffer size */
{
/* create a list of usrEventId... */
return (orig_wvEvent(usrEventId, buffer, bufSize));
}
STATUS hookWvEvents(void)
{
STATUS result = ERROR;
result = vxHook(sysSymTbl, "wvEvent", my_wvEvent, &orig_wvEvent);
return (result);
}
After calling hookWvEvents you can load your application and all calls to wvEvent from within your application will be redirected to my_wvEvent (and then passed on to the original wvEvent function). Remember that hooked symbols stay hooked for your application even if you unhook the symbol using vxUnhook.
Note: This mechanism is also very helpful for application testing and debugging since you can stress your application by forcing system calls to fail...
I have read around 50 posts and tutorials on this topic, I have copied, written and tested around 20 alternatives and done every possible research I can think of. Still, I have not seen a working solution for the following problem:
Parent process A wants to pass data to an external process B, let process B modify the data and pass it back to parent process A, then continue with parent process A. Process B is part of an external program suite that I have no influence over, and that is normally run like this on the UNIX command line:
< input_data program_B1 | program_B2 | program_B3 > output_data
...where
input_data, output_data: Some data that is processed in programs B1-B3
program_B1,B2,B3: Programs that read data from stdin (fread) and output to stdout (fwrite) and apply some processing to the data.
So, in sequence:
(1) Parent process A passes data to child process B
(2) Child process B reads data and modifies it
(3) Child process B passes data back to parent process A
(4) Parent process A reads data and continues (for example passing it further on to a process B2..).
(5) Parent process A passes another data set to child process B etc.
The problem is, whatever I do, the program almost always ends up hanging on a read/fread (or write/fwrite?) to or from a pipe.
One important thing to note is that the parent process cannot simply close the pipes after passing data on to the child process, because it works in a loop and wants to pass another set of data to the child process once it has finished processing the first set.
Here is a working set of parent/child programs (compile with g++ pipe_parent.cc -o pipe_parent, g++ pipe_child.cc -o pipe_child) illustrating the problem with unnamed pipes. I have also tried named pipes, but not as extensively. Each execution can have a slightly different outcome. If the sleep statement is omitted in the parent, or the fflush() statement is omitted in the child, the pipes will almost surely block. If the amount of data to be passed on is increased, it will always block independent of the sleep or fflush.
Parent program A:
#include <cstring>
#include <cstdio>
#include <cstdlib>
extern "C" {
#include <unistd.h>
#include <fcntl.h>
}
using namespace std;
/*
* Parent-child inter-communication
* Child is external process
*/
int main() {
int fd[2];
if( pipe(fd) == -1 ) {
fprintf(stderr,"Unable to create pipe\n");
}
int fd_parentWrite = fd[1];
int fd_childRead = fd[0];
if( pipe(fd) == -1 ) {
fprintf(stderr,"Unable to create pipe\n");
exit(-1);
}
int fd_childWrite = fd[1];
int fd_parentRead = fd[0];
pid_t pid = fork();
if( pid == -1 ) {
fprintf(stderr,"Unable to fork new process\n");
exit(-1);
}
if( pid == 0 ) { // Child process
dup2( fd_childRead, fileno(stdin) ); // Redirect standard input(0) to child 'read pipe'
dup2( fd_childWrite, fileno(stdout) ); // Redirect standard output(1) to child 'write pipe'
close(fd_parentRead);
close(fd_parentWrite);
close(fd_childRead);
close(fd_childWrite);
// execl replaces child process with an external one
int ret = execl("/disk/sources/pipe_test/pipe_child","pipe_child",NULL);
fprintf(stderr,"External process failed, return code: %d...\n", ret);
exit(-1);
// Child process is done. Will not continue from here on
}
else { // Parent process
// Nothing to set up
}
// ...more code...
if( pid > 0 ) { // Parent process (redundant if statement)
int numElements = 10000;
int totalSize = numElements * sizeof(float);
float* buffer = new float[numElements];
for( int i = 0; i < numElements; i++ ) {
buffer[i] = (float)i;
}
for( int iter = 0; iter < 5; iter++ ) {
fprintf(stderr,"--------- Iteration #%d -----------\n", iter);
int sizeWrite = (int)write( fd_parentWrite, buffer, totalSize );
if( sizeWrite == -1 ) {
fprintf(stderr,"Parent process write error\n");
exit(-1);
}
fprintf(stderr,"Parent #%d: Wrote %d elements. Total size: %d\n", iter, sizeWrite, totalSize);
sleep(1); // <--- CHANGE!
int sizeRead = (int)read( fd_parentRead, buffer, totalSize );
if( sizeRead <= 0 ) {
fprintf(stderr,"Parent process read error\n");
}
while( sizeRead < totalSize ) {
fprintf(stderr,"Parent #%d: Read %d elements, continue reading...\n", iter, sizeRead);
int sizeNew = (int)read( fd_parentRead, &buffer[sizeRead], totalSize-sizeRead );
fprintf(stderr," ...newly read %d elements\n", sizeNew);
if( sizeNew < 0 ) {
exit(-1);
}
sizeRead += sizeNew;
}
fprintf(stderr,"Parent #%d: Read %d elements. Total size: %d\n", iter, sizeRead, totalSize);
fprintf(stderr,"Examples : %f %f %f\n", buffer[0], buffer[10], buffer[100]);
}
delete [] buffer;
}
close(fd_parentRead);
close(fd_parentWrite);
close(fd_childRead);
close(fd_childWrite);
return 0;
}
Child program B:
#include <cstdio>
using namespace std;
int main() {
int numElements = 10000;
int totalSize = numElements * sizeof(float);
float* buffer = new float[numElements];
int counter = 0;
int sizeRead = 0;
do {
sizeRead = fread( buffer, 1, totalSize, stdin);
fprintf(stderr,"Child #%d: Read %d elements, buffer100: %f\n", counter, sizeRead, buffer[100]);
if( sizeRead > 0 ) {
for( int i = 0; i < numElements; i++ ) {
buffer[i] += numElements;
}
int sizeWrite = fwrite( buffer, 1, totalSize, stdout);
fflush(stdout); // <--- CHANGE!
fprintf(stderr,"Child #%d: Wrote %d elements\n", counter, sizeWrite);
counter += 1;
}
} while( sizeRead > 0 );
return 0;
}
Is there any way to check when the pipe has enough data to be read? Or is there an alternative way to resolve the above problem, with or without pipes?
Please help!
Possibly the best solution when reading is to check with select whether you can read from the pipe. You can even pass a timeout. The alternative might be setting the O_NONBLOCK flag on file descriptor 0 (stdin) with fcntl, though I think the select way is better.
As with ensuring non-blocking write: that's a bit harder as you don't know how much you can write before the pipe blocks. One way (that I feel is very ugly) would be to only write 1 byte chunks and again check with select whether you can write. But that would be a performance killer, so use only if performance in communication is not an issue.
The first answer (using select to find out whether a pipe is ready to be read from) was good but didn't really solve my issue, see also my previous comments. Sooner or later I always ended up with a "race condition" where the program kept hanging either on a read or write.
The solution (maybe not be the only one?) is to run the child-to-parent data transfer in a different thread. I also went back and implemented the pipes as named pipes. It would probably also work with unnamed pipes but I didn't check that.
The final code is below. Note that no explicit flushing is required; the parent-to-child and child-to-parent data transfers are now decoupled. Any comments how this can be improved welcome! One residual problem I can see is that the pipes may fill up depending on how long time the child needs to process the data. I'm not sure how likely this is to happen. And by the way this worked fine with my external programs, not only with the provided child program.
Parent program A:
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <iostream>
extern "C" {
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
}
using namespace std;
static int const READING = -1;
static int const BUFFER_READY = 1;
static int const FINISHED = 0;
/*
* Parent-child inter-communication
* Child is external process
*/
struct threadStruct {
FILE* file_c2p;
int sizeBuffer;
float* buffer;
int io_flag;
};
// Custom sleep function
void mini_sleep( int millisec ) {
struct timespec req={0},rem={0};
time_t sec = (int)(millisec/1000);
millisec = (int)(millisec-(sec*1000));
req.tv_sec = sec;
req.tv_nsec = millisec*1000000L;
nanosleep(&req,&rem);
}
// Function to be executed within separate thread: Reads in data from file pointer
// Hand-shaking with main thread is done via the flag 'io_flag'
void *threadFunction( void *arg ) {
threadStruct* ptr = (threadStruct*)arg;
ptr->io_flag = READING;
while( ptr->io_flag != FINISHED ) {
if( ptr->io_flag == READING ) {
int sizeRead = fread( ptr->buffer, 1, ptr->sizeBuffer, ptr->file_c2p );
if( sizeRead <= 0 ) {
ptr->io_flag = FINISHED;
return NULL;
}
ptr->io_flag = BUFFER_READY;
}
else {
mini_sleep(10);
}
}
return NULL;
}
//--------------------------------------------------
int main() {
std::string filename_p2c("/tmp/fifo11_p2c");
std::string filename_c2p("/tmp/fifo11_c2p");
fprintf(stderr,"..started\n");
int status = mknod(filename_p2c.c_str(), S_IRUSR | S_IWUSR | S_IFIFO, 0);
if( (status == -1) && (errno != EEXIST) ) {
fprintf(stderr,"Error creating named pipe: %s\n", strerror(errno));
exit(-1);
}
status = mknod(filename_c2p.c_str(), S_IRUSR | S_IWUSR | S_IFIFO, 0);
if( (status == -1) && (errno != EEXIST) ) {
fprintf(stderr,"Error creating named pipe: %s\n", strerror(errno));
exit(-1);
}
FILE* file_dump = fopen("parent_dump","w");
int fd_p2c;
int fd_c2p;
FILE* file_c2p = NULL;
//--------------------------------------------------
// Set up parent/child processes
//
pid_t pid = fork();
if( pid == -1 ) {
fprintf(stderr,"Unable to fork new process\n");
}
if( pid == 0 ) { // Child process
fd_p2c = open( filename_p2c.c_str(), O_RDONLY );
if( fd_p2c < 0 ) {
fprintf(stderr,"Child: Error opening the named pipe: %d %d '%s'\n", fd_p2c, errno, strerror(errno));
exit(-1);
}
fd_c2p = open( filename_c2p.c_str(), O_WRONLY );
if( fd_c2p < 0 ) {
fprintf(stderr,"Child: Error opening the named pipe: %d %d '%s'\n", fd_c2p, errno, strerror(errno));
exit(-1);
}
dup2(fd_p2c,fileno(stdin)); // Redirect standard input(0) to child 'read pipe'
dup2(fd_c2p,fileno(stdout)); // Redirect standard output(1) to child 'write pipe'
close(fd_p2c);
close(fd_c2p);
int ret = execl("/disk/sources/pipe_test/pipe_child","pipe_child",NULL);
fprintf(stderr,"External process failed, return code: %d...\n", ret);
kill( getppid(), 9 ); // Kill parent process
exit(-1);
}
else { // Parent process
fd_p2c = open( filename_p2c.c_str(), O_WRONLY );
if( fd_p2c < 0 ) {
fprintf(stderr,"Parent: Error opening the named pipe: %d %d '%s'\n", fd_p2c, errno, strerror(errno));
exit(-1);
}
file_c2p = fopen( filename_c2p.c_str(), "r");
fd_c2p = fileno( file_c2p );
if( fd_c2p < 0 ) {
fprintf(stderr,"Parent: Error opening the named pipe: %d %d '%s'\n", fd_c2p, errno, strerror(errno));
exit(-1);
}
}
int numElements = 10000;
int sizeBuffer = numElements * sizeof(float);
float* bufferIn = new float[numElements];
float* bufferOut = new float[numElements];
for( int i = 0; i < numElements; i++ ) {
bufferIn[i] = 0.0;
}
int numIterations = 5;
int numBytesAll = numElements * sizeof(float) * numIterations;
pthread_t thread;
threadStruct* threadParam = new threadStruct();
threadParam->file_c2p = file_c2p;
threadParam->sizeBuffer = sizeBuffer;
threadParam->buffer = bufferIn;
threadParam->io_flag = READING;
int thread_stat = pthread_create( &thread, NULL, threadFunction, threadParam );
if( thread_stat < 0 ) {
fprintf(stderr,"Error when creating thread\n");
exit(-1);
}
int readCounter = 0;
int numBytesWrite = 0;
int numBytesRead = 0;
for( int iter = 0; iter < numIterations; iter++ ) {
for( int i = 0; i < numElements; i++ ) {
bufferOut[i] = (float)i + iter*numElements*10;
}
int sizeWrite = (int)write( fd_p2c, bufferOut, sizeBuffer );
if( sizeWrite == -1 ) {
fprintf(stderr,"Parent process write error\n");
exit(-1);
}
numBytesWrite += sizeWrite;
fprintf(file_dump,"Parent #%d: Wrote %d/%d bytes.\n", iter, numBytesWrite, numBytesAll);
if( iter == numIterations-1 ) close(fd_p2c); // Closing output pipe makes sure child receives EOF
if( threadParam->io_flag != READING ) {
numBytesRead += sizeBuffer;
fprintf(file_dump,"Parent #%d: Read %d/%d bytes. Examples: %f %f\n",
readCounter, numBytesRead, numBytesAll, bufferIn[1], bufferIn[numElements-1] );
readCounter += 1;
if( threadParam->io_flag != FINISHED ) threadParam->io_flag = READING;
}
}
//********************************************************************************
//
fprintf(file_dump,"------------------------------\n");
while( threadParam->io_flag != FINISHED ) {
if( threadParam->io_flag == BUFFER_READY ) {
numBytesRead += sizeBuffer;
fprintf(file_dump,"Parent #%d: Read %d/%d bytes. Examples: %f %f\n",
readCounter, numBytesRead, numBytesAll, bufferIn[1], bufferIn[numElements-1] );
readCounter += 1;
if( threadParam->io_flag != FINISHED ) threadParam->io_flag = READING;
}
else {
mini_sleep(10);
}
}
// wait for thread to finish before continuing
pthread_join( thread, NULL );
fclose(file_dump);
fclose(file_c2p);
waitpid(pid, &status, 0); // clean up any children
fprintf(stderr,"..finished\n");
delete [] bufferIn;
delete [] bufferOut;
return 0;
}
Child program B:
#include <cstdio>
using namespace std;
int main() {
int numElements = 10000;
int totalSize = numElements * sizeof(float);
float* buffer = new float[numElements];
FILE* file_dump = fopen("child_dump","w");
int counter = 0;
int sizeRead = 0;
do {
sizeRead = fread( buffer, 1, totalSize, stdin);
if( sizeRead > 0 ) {
fprintf(file_dump,"Child #%d: Read %d bytes, examples: %f %f\n", counter, sizeRead, buffer[1], buffer[numElements-1]);
for( int i = 0; i < numElements; i++ ) {
buffer[i] += numElements;
}
int sizeWrite = fwrite( buffer, 1, totalSize, stdout);
fprintf(file_dump,"Child #%d: Wrote %d bytes, examples: %f %f\n", counter, sizeRead, buffer[1], buffer[numElements-1]);
counter += 1;
}
} while( sizeRead > 0 );
fprintf(file_dump,"Child is finished\n");
fclose(file_dump);
fclose(stdout);
return 0;
}