我正在尝试与c和tcl共享变量,问题是当我尝试从tcl读取c线程中的变量时,它会导致分段错误,我不确定这是正确的方法它,但它似乎适用于整数。导致分段错误的部分是这一行是当我尝试打印“Var”但我想读取变量以在变量更改时执行相应的操作。
这是我正在使用的C代码
void mode_service(ClientData clientData) {
while(1) {
char* Var = (char *) clientData;
printf("%s\n", Var);
usleep(100000); //100ms
}
}
static int mode_thread(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
Tcl_ThreadId id;
ClientData limitData;
limitData = cdata;
id = 0;
Tcl_CreateThread(&id, mode_service, limitData, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS);
printf("Tcl_CreateThread id = %d\n", (int) id);
// Wait thread process, before returning to TCL prog
int i, aa;
for (i=0 ; i<100000; i++) {aa = i;}
// Return thread ID to tcl prog to allow mutex use
Tcl_SetObjResult(interp, Tcl_NewIntObj((int)id));
printf("returning\n");
return TCL_OK;
}
int DLLEXPORT Modemanager_Init(Tcl_Interp *interp){
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
if (Tcl_PkgProvide(interp, "PCIe", "1.0") == TCL_ERROR) {
return TCL_ERROR;
}
// Create global Var
int *sharedPtr=NULL;
//sharedPtr = sharedPtr = (char *) Tcl_Alloc(sizeof(char));
Tcl_LinkVar(interp, "mode", (char *) &sharedPtr, TCL_LINK_STRING);
Tcl_CreateObjCommand(interp, "mode_thread", mode_thread, sharedPtr, NULL);
return TCL_OK;
}
在tcl代码中,只要用户按下按钮,我就会改变变量模式,例如:
set mode "Idle"
button .startSamp -text "Sample Start" -width 9 -height 3 -background $btnColor -relief flat -state normal -command {set mode "Sampling"}
set threadId [mode_thread]
puts "Created thread $threadId, waiting"
答案 0 :(得分:2)
你的代码完全混乱!你需要决定你在做什么,然后做到这一点。特别是,您正在使用Tcl_LinkVar
,因此您需要确定要链接的变量类型。如果你在存储,C访问模式和声明的语义类型之间出现不匹配,你就会崩溃。
因为你的代码太乱了,我无法弄清楚你想要做什么,所以我将用不那么密切相关的例子进行说明。您需要从中了解如何更改代码中的内容以获得所需的结果。
让我们做一个简单的案例:一个全局 int
变量(声明在之外的任何函数)。
int sharedVal;
您希望C代码读取该变量并获取值。简单!只是阅读它在范围内。您还希望Tcl代码能够写入该变量。简单!在包初始化函数中,输入:
Tcl_LinkVar(interp /* == the Tcl interpreter context */,
"sharedVal" /* == the Tcl name */,
(char *) &sharedVal /* == pointer to C variable */,
TCL_LINK_INT /* == what is it! An integer */);
请注意,在Tcl代码从Tcl变量读取之后(直到Tcl_UnlinkVar
),当前值将从C变量中获取并转换。
如果您希望该变量在堆上,那么您可以:
int *sharedValPtr = malloc(sizeof(int));
使用*sharedValPtr
进行C代码访问,然后使用:
Tcl_LinkVar(interp /* == the Tcl interpreter context */,
"sharedVal" /* == the Tcl name */,
(char *) sharedValPtr /* == pointer to C variable */,
TCL_LINK_INT /* == what is it! An integer */);
还有许多其他语义类型以及TCL_LINK_INT
(请参阅文档列表),但它们都遵循{em>除之外的TCL_LINK_STRING
模式。有了这个,你可以:
char *sharedStr = NULL;
Tcl_LinkVar(interp, "sharedStr", (char *) &sharedStr, TCL_LINK_STRING);
您还需要注意,字符串将始终分配Tcl_Alloc
(这比典型的Tcl内存使用模式的大多数系统内存分配器快得多),而不是任何其他内存分配器,因此也始终与Tcl_Free
取消分配。实际上,这意味着如果您从C端设置字符串,则必须使用Tcl_Alloc
来分配内存。
要注意的最后一件事是,当您从C侧设置变量但希望Tcl注意到更改已设置时(例如,因为已设置trace
或因为您已将值显示在一个Tk GUI),你应该Tcl_UpdateLinkedVar
让Tcl知道它应该注意的变化。如果您从未使用跟踪(或Tk GUI或vwait
命令)来监视变量的更新,则可以忽略此API调用。
答案 1 :(得分:1)
Donal的回答是正确的,但我试图向您展示您对ClientData的所作所为。
澄清:所有(或几乎所有,Idk)带有函数指针的Tcl函数也采用类型为ClientData
的参数,当Tcl调用它时,该参数将传递给您的函数。
让我们来看看这一行:
Tcl_CreateObjCommand(interp, "mode_thread", mode_thread, NULL, NULL);
// ------------------------------------------------------^^^^
您始终将NULL
作为ClientData
传递给mode_thread
函数
在mode_thread
函数中,您使用传递的ClientData
(NULL
)将其作为ClientData传递给新的线程:
limitData = cdata;
// ...
Tcl_CreateThread(&id, mode_service, limitData, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS);
在mode_service
函数中,您使用ClientData
(仍为NULL
)作为指向char
数组的指针:
char* Var = (char *) clientData;
这是指向地址0x00
的指针
然后你告诉printf
取消引用这个NULL指针:
printf("%s\n", Var);
这显然会导致你的程序崩溃。