dbus基础
这是以前学习dbus时候写的使用dbus API来接收消息的一个比较全的例子,目的是熟悉dbus的c api的功能。今天又温习了一下,贴出来永久保留:), 有时间把原理写下,kde4都用它了,应该很重要。
/**
* the demo is to demonstrate the whole process of setting up
* a dbus message loop manually. for simplicity, we connect to
* session bus instead of coding a server from the grand up.
* the best of dbus is that we can register our own callbacks in
* all most every step of message process. by using notify functions
* the main loop of application awares of the changes, including coming
* of raw message data from the transport, new outgoing message, incoming
* message queue is empty and so on.
*/
#include dbus/dbus.h>
#include stdbool.h>
#include unistd.h>
#include stdio.h>
#include stdlib.h>
#include stdarg.h>
#include string.h>
#include sys/select.h>
/**
* for every connection, there is a transport mechanism ( here
* maybe is unix domain socket ). data come and go from the connection,
* a watch is used to monitor incoming or outgoing data flow or exception.
* a watch bind with a fd (i know here fd is owned by the socket),
* when fd is ready to read data or write from transport, a notify
* event occurs.
*/
struct watchlist_t {
DBusWatch *watch;
struct watchlist_t *next;
};
static struct watchlist_t *watchlist = NULL;
/**
* when you send a message typed method_call with a reply, you
* may need to set up a timeout handler. when a reply available,
* a timeout event ocurrs.
*/
static struct timeoutlist_t {
DBusTimeout *timeout;
struct timeoutlist_t *next;
} *timeoutlist = NULL;
/**
* you can register any path you like, just follow the dbus specification,
* such as "/org", "/org/redflag", "/com" etc. i think dbus internally
* splits all path into components ("org", "redflag", "com") and arranges
* them into a tree, every node bind a handler. if one node has no
* handler registered, it processed by dbus( use
* DBUS_HANDLER_RESULT_NOT_YET_HANDLED to notify dbus).
*/
char *objectPaths[] = {
"/org/freedesktop",
"/com",
"/com/redflag",
"/org/freedesktop/DBus",
"/org/freedesktop/csy",
"/com/redflag/csy"
};
/** object handlers and object path handlers */
DBusHandlerResult object_dbus_handler(DBusConnection*, DBusMessage*, void*);
DBusHandlerResult object_csy_handler(DBusConnection*, DBusMessage*, void*);
DBusHandlerResult subsection_com_redflag_handler(DBusConnection*,
DBusMessage*, void*);
DBusHandlerResult subsection_com_handler(DBusConnection*,
DBusMessage*, void*);
DBusHandlerResult subsection_org_freedesktop_handler(DBusConnection*,
DBusMessage*, void*);
void object_unregister_handler(DBusConnection*, void*);
DBusObjectPathVTable objectPathVTable[] = {
{
.unregister_function = NULL,
.message_function = subsection_org_freedesktop_handler
},
{
.unregister_function = NULL,
.message_function = subsection_com_handler
},
{
.unregister_function = NULL,
.message_function = subsection_com_redflag_handler
},
{
.unregister_function = NULL,
.message_function = object_dbus_handler
},
{
.unregister_function = object_unregister_handler,
.message_function = object_csy_handler
},
{
.unregister_function = object_unregister_handler,
.message_function = object_csy_handler
}
};
static struct seriallist_t {
dbus_uint32_t serial;
struct seriallist_t *next;
} *seriallist = NULL;
/**------------- debug facilities --------------------------------*/
void err_quit(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fprintf(stderr, fmt, args);
va_end(args);
exit(1);
}
/**
* for debug purpose
*/
char* _verbose_watch(DBusWatch *watch) {
char *s_flags[] = {
"readable",
"writable"
};
char p[1024] = "", ret[1024] = "";
if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE )
strncpy(p, s_flags[0], strlen(s_flags[0]));
else if (dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE ) {
if ( p[0] )
strncat(p, "&", strlen("&"));
strncat(p, s_flags[1], strlen(s_flags[1]));
}
sprintf(ret, ":%s", dbus_watch_get_fd(watch), p);
return ret;
}
char* _verbose_message(DBusMessage *msg) {
char s_msg[1024] = "", ret[1024] = "";
int bc = sprintf(s_msg, "\ttype: %s\n\tpath: %s\n"
"\tmember: %s\n\t",
dbus_message_type_to_string(dbus_message_get_type(msg)),
dbus_message_get_path(msg),
dbus_message_get_member(msg));
strncpy(ret, s_msg, bc+1);
if (dbus_message_get_serial(msg)) {
bc = sprintf(s_msg, "serial: %ud\n\t",
dbus_message_get_serial(msg));
strncat(ret, s_msg, bc+1);
}
if (dbus_message_get_reply_serial(msg)) {
bc = sprintf(s_msg, "reply_serial: %ud\n\t",
dbus_message_get_reply_serial(msg));
strncat(ret, s_msg, bc+1);
}
DBusMessageIter args, subargs;
char *s;
int i;
dbus_message_iter_init(msg, &args);
bc = sprintf(s_msg, "args: ");
strncat(ret, s_msg, bc+1);
/** here is not the best way to parse the params, i just want to test some of
* nested situations and different types of params. */
while (DBUS_TYPE_INVALID != dbus_message_iter_get_arg_type(&args)) {
// demo here: only care about int and string, igore other types
switch (dbus_message_iter_get_arg_type(&args)) {
case DBUS_TYPE_STRING:
dbus_message_iter_get_basic(&args, &s);
bc = sprintf(s_msg, " %s", s);
strncat(ret, s_msg, bc+1);
break;
case DBUS_TYPE_INT32:
dbus_message_iter_get_basic(&args, &i);
bc = sprintf(s_msg, " %d", i);
strncat(ret, s_msg, bc+1);
break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(&args, &subargs);
strcat(ret, " [ ");
while (dbus_message_iter_get_arg_type(&subargs)
!= DBUS_TYPE_INVALID) {
switch (dbus_message_iter_get_arg_type(&subargs)) {
case DBUS_TYPE_STRING:
dbus_message_iter_get_basic(&subargs, &s);
bc = sprintf(s_msg, " %s", s);
strncat(ret, s_msg, bc+1);
break;
case DBUS_TYPE_INT32:
dbus_message_iter_get_basic(&subargs, &i);
bc = sprintf(s_msg, " %d", i);
strncat(ret, s_msg, bc+1);
break;
}
dbus_message_iter_next(&subargs);
}
strcat(ret, " ] ");
}
dbus_message_iter_next(&args);
}
return ret;
}
char* _verbose_timeout(DBusTimeout *timeout) {
char ret[1024] = "";
sprintf(ret, "timeout: \n",
(unsigned int)timeout, dbus_timeout_get_interval(timeout));
return ret;
}
/** -------------------------------------------------------*/
DBusHandlerResult handle_method_return(DBusConnection *conn,
DBusMessage *reply) {
struct seriallist_t *l = seriallist;
while (l != NULL) {
if (l->serial == dbus_message_get_reply_serial(reply)) {
printf("reply_msg:\t%s\n", _verbose_message(reply));
return DBUS_HANDLER_RESULT_HANDLED;
}
l = l->next;
}
return DBUS_HANDLER_RESULT_HANDLED;
}
int reply_to_method_call(DBusMessage *msg, DBusConnection *conn) {
DBusMessage *reply;
DBusMessageIter args;
DBusError err;
// suppose we expect two argument, one is id, and second is
// a string.
if (!dbus_message_iter_init(msg, &args))
err_quit("arg init error.\n");
dbus_int32_t id;
char *content;
dbus_error_init( &err );
dbus_message_get_args( msg, &err,
DBUS_TYPE_INT32, &id,
DBUS_TYPE_STRING, &content,
DBUS_TYPE_INVALID );
if (dbus_error_is_set(&err))
err_quit("get arg error.\n");
reply = dbus_message_new_method_return(msg);
if (NULL == reply)
err_quit("Memory is not enough.\n");
printf("received call args: %d: %s\n", id, content);
const char *comment = "reply to method call from com.redflag.csy.";
dbus_message_iter_init_append( reply, &args );
dbus_message_append_args ( reply,
DBUS_TYPE_STRING, &comment,
DBUS_TYPE_INVALID );
dbus_uint32_t serial;
dbus_connection_send( conn, reply, &serial );
dbus_connection_flush( conn );
printf( "build reply msg and send: \n\t%s\n", _verbose_message(reply) );
dbus_message_unref( reply );
}
dbus_bool_t watchAddNotify(DBusWatch *watch, void *data) {
struct watchlist_t *l;
for (l = watchlist; l != NULL; l = l->next) {
if (l->watch == watch)
return TRUE;
}
printf("watchAdd: %s\n", _verbose_watch(watch));
l = dbus_new(struct watchlist_t, 1);
if ( NULL == l )
return FALSE;
l->watch = watch;
l->next = watchlist;
watchlist = l;
return TRUE;
}
void watchRemoveNotify(DBusWatch *watch, void *data) {
struct watchlist_t *l, *pre;
for ( pre = l = watchlist; l != NULL; pre = l, l = l->next ) {
if (l->watch == watch) {
printf("watchRemove: \n", dbus_watch_get_fd(watch));
if ( l == watchlist ) {
watchlist = l->next;
dbus_free(l);
} else {
pre->next = l->next;
dbus_free(l);
}
break;
}
}
}
void watchToggleNotify(DBusWatch *watch, void *data) {
if (watch == NULL)
err_quit("line %d: watch should not be null.", __LINE__);
printf( "toggleNotify: watch %d toogled %s",
dbus_watch_get_fd(watch),
dbus_watch_get_enabled(watch)?"enable":"disabled" );
}
/**
* process any watches added to connection
*/
dbus_bool_t watchHandler() {
struct watchlist_t *l;
fd_set rfds, wfds, efds;
int maxid = -1, fd;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
// prepare all readble and writable fds
for (l = watchlist; l != NULL; l = l->next) {
if (!dbus_watch_get_enabled(l->watch))
continue;
fd = dbus_watch_get_fd(l->watch);
if ( fd & DBUS_WATCH_READABLE ) {
FD_SET(fd, &rfds);
maxid = (maxid fd? fd: maxid);
}
if ( fd & DBUS_WATCH_WRITABLE ) {
FD_SET(fd, &wfds);
maxid = (maxid fd? fd: maxid);
}
if ( fd & DBUS_WATCH_ERROR ) {
FD_SET(fd, &efds);
maxid = (maxid fd? fd: maxid);
}
}
int ret = select(maxid+1, &rfds, &wfds, &efds, NULL);
if (ret = 0)
return;
// call dbus_watch_handle is a must, it uses internal predefined
// watch handler to do some thing, but i am not quite sure what
// it is right now.
for (l = watchlist; l != NULL; l = l->next) {
fd = dbus_watch_get_fd(l->watch);
if (FD_ISSET(fd, &rfds))
dbus_watch_handle(l->watch, DBUS_WATCH_READABLE);
if (FD_ISSET(fd, &wfds))
dbus_watch_handle(l->watch, DBUS_WATCH_WRITABLE);
if (FD_ISSET(fd, &efds))
dbus_watch_handle(l->watch, DBUS_WATCH_ERROR);
}
}
/**---- timeout process functions ------------------------------*/
dbus_bool_t timeoutAddNotify(DBusTimeout *timeout, void *data) {
struct timeoutlist_t *l;
for (l = timeoutlist; l != NULL; l = l->next) {
if (l->timeout == timeout)
return TRUE;
}
l = dbus_new(struct timeoutlist_t, 1);
if (NULL == l)
return FALSE;
l->timeout = timeout;
fprintf(stdout, "timeoutAdd:%s\n", _verbose_timeout(timeout));
l->next = timeoutlist;
timeoutlist = l;
return TRUE;
}
void timeoutRemoveNotify(DBusTimeout *timeout, void *data) {
struct timeoutlist_t *pre = NULL, *l = timeoutlist;
while( l != NULL ) {
if (l->timeout == timeout) {
if (pre == NULL)
timeoutlist = l->next;
else
pre->next = l->next;
fprintf(stdout, "timeoutRemove:%s\n",
_verbose_timeout(timeout));
break;
}
pre = l;
l = l->next;
}
}
void timeoutToggleNotify(DBusTimeout *timeout, void *data) {
fprintf(stdout, "timeoutToggle: %s\n", _verbose_timeout(timeout));
}
/**
* in this function, we call dbus_timeout_handle to handle all timeout
* events, it will call internal predefined handler to process.
*/
void timeoutHandle() {
struct timeoutlist_t *l = timeoutlist;
for (; l != NULL; l = l->next) {
if (dbus_timeout_get_enabled(l->timeout)) {
printf("timeoutHandle: %s\n", _verbose_timeout(l->timeout));
dbus_timeout_handle(l->timeout);
}
}
}
/**----------- all handlers -------------------------------------*/
/**
* filter messages that already stayed in the incoming queue,
* decide whether a further process is needed.
*/
DBusHandlerResult msgFilter(DBusConnection *conn,
DBusMessage *msg, void *data) {
printf("incoming msg: %s\n", _verbose_message(msg));
switch (dbus_message_get_type(msg)) {
case DBUS_MESSAGE_TYPE_METHOD_CALL:
if (!strcmp(dbus_message_get_member(msg), "ignore")) {
DBusMessage *errMsg;
errMsg = dbus_message_new_error(msg,
"com.redflag.csy.IgnoreService",
"this demonstrate the filter.");
dbus_connection_send(conn, errMsg, NULL);
return DBUS_HANDLER_RESULT_HANDLED;
} else
break;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
// never reach here.
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
break;
case DBUS_MESSAGE_TYPE_ERROR:
break;
}
// set this flag is very important, if not, dbus may not
// process messages for you. it pass the control to dbus
// default filter.
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
DBusHandlerResult subsection_com_handler(DBusConnection* conn,
DBusMessage* msg, void* data) {
if ( strncmp(dbus_message_get_path(msg), objectPaths[1],
strlen(objectPaths[1])) != 0 ) {
printf("subsection_com_handler: something wrong.\n");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if ( strncmp(dbus_message_get_member(msg), "pseudo", 6) == 0 ) {
reply_to_method_call(msg, conn);
printf("subsection_com_handler: handled.\n");
return DBUS_HANDLER_RESULT_HANDLED;
} else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
DBusHandlerResult subsection_org_freedesktop_handler(
DBusConnection* conn, DBusMessage* msg, void* data) {
if ( strncmp(dbus_message_get_path(msg), objectPaths[0],
strlen(objectPaths[0])) != 0 ) {
printf("subsection_org_freedesktop__handler: something wrong.\n");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if ( strncmp(dbus_message_get_member(msg), "error", 5) == 0 ) {
printf("subsection_org_freedesktop_handler(error): handled.\n");
return DBUS_HANDLER_RESULT_HANDLED;
} else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
DBusHandlerResult subsection_com_redflag_handler(DBusConnection* conn,
DBusMessage* msg, void* data) {
if ( strncmp(dbus_message_get_path(msg), objectPaths[2],
strlen(objectPaths[2])) != 0 ) {
printf("subsection_com_redflag_handler: something wrong.\n");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if ( strncmp(dbus_message_get_member(msg), "pseudo", 6) == 0 ) {
reply_to_method_call(msg, conn);
printf("subsection_com_redflag_handler: handled.\n");
return DBUS_HANDLER_RESULT_HANDLED;
} else
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
DBusHandlerResult object_dbus_handler(DBusConnection* conn,
DBusMessage* msg, void* data) {
if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
printf("object_dbus_handler: method_return.\n");
int ret = handle_method_return( conn, msg );
printf("object_dbus_handler: handled.\n");
return ret;
}
fprintf(stdout, "object_dbus_handler: cannot handle.\n");
fprintf(stdout, "\t%s\n", _verbose_message(msg));
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
DBusHandlerResult object_csy_handler(DBusConnection* conn,
DBusMessage* msg, void* data) {
switch (dbus_message_get_type(msg)) {
case DBUS_MESSAGE_TYPE_SIGNAL:
case DBUS_MESSAGE_TYPE_ERROR:
fprintf(stdout, "object_csy_handler(error/signal):\n\t%s\n",
_verbose_message(msg));
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
return handle_method_return(conn, msg);
case DBUS_MESSAGE_TYPE_METHOD_CALL:
if ( strstr(dbus_message_get_path(msg), "sycao") != NULL ) {
printf("object_csy_handler(call): "
"cannot handle, through to tree.");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if ( !strcmp(dbus_message_get_member(msg), "pseudo") ) {
reply_to_method_call(msg, conn);
printf("object_csy_handler(call): handled.\n");
return DBUS_HANDLER_RESULT_HANDLED;
} else {
fprintf(stdout, "object_csy_handler(call): cannot handle.\n");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
}
}
void object_unregister_handler(DBusConnection* conn, void* data) {
printf("object_unregister_handler:\n");
}
/**----------------testing routines ----------------------------------*/
/**
* test send signal method call, send certain signal randomly
*/
void send_random_signal(DBusConnection *conn) {
DBusMessage *msg;
char *msg_args[] = {
"/org/freedesktop/DBus\0org.freedesktop.DBus\0NotExist",
"/org/freedesktop/HAL\0org.freedesktop.HAL\0Device",
"/com/redflag/csy\0com.redflag.csy\0NewSignal",
"\0", "\0"
};
char *pargs = msg_args[rand()%(sizeof(msg_args)/sizeof(msg_args[0]))];
if (!*pargs)
return;
long chance = 0x0fffffff;
if ((rand() % chance) > 100)
return;
char *path = pargs;
char *intf, *member;
while( *pargs++ );
intf = pargs;
while( *pargs++ );
member = pargs;
// printf("%s,%s,%s\n", path, intf, member);
dbus_uint32_t serial;
msg = dbus_message_new_signal(path, intf, member);
if (NULL == msg)
err_quit("no enough memory.\n");
if ( !dbus_connection_send(conn, msg, &serial) )
fprintf( stderr, "no enough memory to send signal.\n" );
fprintf(stdout, "SendSignal(%d): %s\n", serial, _verbose_message(msg));
}
void pendingCallNotify(DBusPendingCall *pending, void *data) {
// one can process the pending and then read the reply message
// alternatively, one can leave it to message handlers
DBusMessage *msg = dbus_pending_call_steal_reply(pending);
printf( "pendingCallNotify: %s\n", _verbose_message(msg) );
}
void send_random_method_expecting_reply(DBusConnection *conn) {
DBusMessage *msg;
char *msg_args[] = {
"org.freedesktop.DBus\0/org/freedesktop/DBus\0org.freedesktop.DBus\0ListNames",
"org.freedesktop.DBus\0/org/freedesktop/DBus\0org.freedesktop.DBus.Peer\0Ping",
"org.freedesktop.DBus\0/org/freedesktop/DBus\0org.freedesktop.DBus.Peer\0GetMachineId",
"\0" // add possibility of not sending any msg this turn
};
long chance = 0x0fffffff;
if ((rand() % chance) > 100)
return;
char *pargs = msg_args[rand()%(sizeof(msg_args)/sizeof(msg_args[0]))];
if (!*pargs)
return;
char *dest, *path, *intf, *member;
dest = pargs;
while( *pargs++ );
path = pargs;
while( *pargs++ );
intf = pargs;
while( *pargs++ );
member = pargs;
msg = dbus_message_new_method_call(dest, path, intf, member);
if (NULL == msg)
err_quit("no enough memory.\n");
DBusPendingCall *pendingCall;
if (!dbus_connection_send_with_reply(conn, msg, &pendingCall, 10000))
fprintf( stderr, "no memory to send method_call.\n" );
dbus_pending_call_set_notify(pendingCall, pendingCallNotify, NULL, NULL);
struct seriallist_t *l;
l = dbus_new(struct seriallist_t, 1);
l->serial = dbus_message_get_serial(msg);
l->next = seriallist;
seriallist = l;
fprintf(stdout, "SendMethod: %s\n", _verbose_message(msg));
}
int dbus_process_msg_loop() {
DBusConnection *conn;
DBusError *perr;
// 1. connection to bus
perr = dbus_new(DBusError, 1);
dbus_error_init(perr);
conn = dbus_bus_get(DBUS_BUS_SESSION, perr);
if ( dbus_error_is_set(perr) )
err_quit("connection failed.\n");
// 2. request well-known name
int ret = dbus_bus_request_name( conn, "com.redflag.csy",
DBUS_NAME_FLAG_REPLACE_EXISTING, perr );
if ( DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret )
err_quit("not primary owner.\n");
// 3. register any handler
dbus_bus_add_match( conn,
"type='method_call',path='/org/freedesktop/csy'"
",interface='com.redflag.sycao',member='faked'"
, perr);
if (dbus_error_is_set(perr))
err_quit("add match failed.\n");
dbus_connection_add_filter( conn, msgFilter, NULL, NULL);
dbus_connection_set_watch_functions( conn,
watchAddNotify, watchRemoveNotify, watchToggleNotify,
NULL, NULL );
dbus_connection_set_timeout_functions( conn,
timeoutAddNotify, timeoutRemoveNotify, timeoutToggleNotify,
NULL, NULL );
int i;
for (i = 0; i sizeof(objectPaths)/sizeof(objectPaths[0]); i++) {
if (i 3)
dbus_connection_register_fallback( conn,
objectPaths, &objectPathVTable, NULL );
else
dbus_connection_register_object_path( conn,
objectPaths, &objectPathVTable, NULL);
}
printf("main: registered %d objectHandlers.\n", i);
// 4. main loop: wait and process incoming msgs
// there are several steps :
// a. check if any watch is ready for read (incoming data prepared)
// or write (outgoing data prepared), and process it.
// b. check if any timeout occurred ( i know now method_call that
// needs a reply will set a timeout ), and process it.
// c. call dispatch will do:
// i. parse incoming raw data if has.
// ii. process any pending call ( bind with a reply message );
// ii. call any filter registered.
// iv. call any object path handler registered.
// ps: a single dispatch call processes at most one message.
//
while( 1 ) {
DBusDispatchStatus status;
while( 1 ) {
watchHandler();
timeoutHandle();
status = dbus_connection_dispatch( conn );
switch( status ) {
case DBUS_DISPATCH_DATA_REMAINS:
// there may be more msgs remain, go on
printf("DBUS_DISPATCH_DATA_REMAINS\n");
continue;
case DBUS_DISPATCH_COMPLETE:
break;
case DBUS_DISPATCH_NEED_MEMORY:
fprintf( stderr, "more memory is needed.\n" );
}
break;
}
send_random_signal( conn );
send_random_method_expecting_reply( conn );
}
dbus_free(perr);
return 0;
}
int main(int argc, char **argv) {
return dbus_process_msg_loop();
}
编译的时候带上:
CFLAGS=-Wall `pkg-config dbus-1 --cflags`
LDFLAGS=`pkg-config dbus-1 --libs`
可以用下面的脚本测试下不同的对象路径、接口和方法的影响:
#!/bin/bash
if [ $# == 0 ]; then
loop=10
else
loop=$1
fi
loop2=$loop
loop3=$loop
loop4=$loop
while (( loop > 0 ))
do
dbus-send --session --type="method_call" --print-reply --dest="com.redflag.csy" "/com/redflag/csy" "com.redflag.csy.pseudo" int32:$loop string:"hello,dbus1"
let "loop = loop - 1"
done
while (( loop2 > 0 ))
do
dbus-send --session --type="method_call" --print-reply --dest="com.redflag.csy" "/org/freedesktop/csy" "com.redflag.csy.pseudo" int32:$loop2 string:"hello,dbus2"
let "loop2 = loop2 - 1"
done
while (( loop3 > 0 ))
do
dbus-send --session --type="method_call" --print-reply --dest="com.redflag.csy" "/com/redflag/sycao" "com.redflag.sycao.pseudo" int32:$loop3 string:"hello,dbus3"
let "loop3 = loop3 - 1"
done
while (( loop4 > 0 ))
do
dbus-send --session --type="method_call" --print-reply --dest="com.redflag.csy" "/org/freedesktop/csy" "com.redflag.csy.ignore" int32:$loop4 string:"hello,dbus4"
let "loop4 = loop4 - 1"
done
shell
在当前目录下的寻找包含"socket"的.c, .h文件
grep socket `find . -name *.[ch]` | awk -F":" '{print $1}' | uniq
Emacs Tips n Tricks for Everybody
Emacs Tips n Tricks for Everybody
This web-page is not yet complete. Several pieces of LISP-code require explanations — I'll add them as I get time.
To try out a code snippet, just add it to your .emacs, re-start emacs and see if any error messages appear (visit the *messages* window in emacs. Alternately, if you're familiar with the basic syntax of LISP, you could position your cursor after a closing parenthesis (the end of a LISP expression) and press C-x C-e (ctrl-x followed by ctrl-e). This would "evaluate" the expression, thereby "executing" the command. Of course, to execute the command automatically every time you start emacs, you would have to add it to your .emacs.
Removing Annoyances
(setq inhibit-startup-message t) will inhibit startup messages.
(setq require-final-newline t) will make the last line end in a carriage return.
(fset 'yes-or-no-p 'y-or-n-p) will allow you to type just "y" instead of "yes" when you exit.
(setq next-line-add-newlines nil) will disallow creation of new lines when you press the "arrow-down key" at end of the buffer.
General Embellishments
(setq modifier-keys-are-sticky t) will make ctrl, shift, alt "sticky" in xemacs. (Does anybody know how to do this in emacs?) With sticky-keys, you dont have to hold down the ctrl/shift/alt key and another key simultaneosly. For example, to type ctrl-a, you'd have to tap on the 'ctrl'-key, followed by a tap on the 'a'-key. This feature is useful for folks with sore wrists/hands.
(setq message-log-max 512) will reduce the number of messages that appear in the "*Messages*" window to 512.
(setq display-time-day-and-date t) (display-time) will make the display of date and time persistent.
(require 'paren) (show-paren-mode t) will highlight matching parentheses next to cursor.
(setq-default indent-tabs-mode nil) will introduce spaces instead of tabs by default.
(setq-default truncate-lines t) will trucate lines if they are too long.
(setq-default truncate-partial-width-windows nil) will trucate even when screen is split into multiple windows.
(require 'auto-show) (auto-show-mode 1) (setq-default auto-show-mode t) load auto-show (shows lines when cursor moves to right of long line).
(auto-show-make-point-visible) will position the cursor to end of output in shell mode.
(auto-show-make-point-visible) will position cursor to end of output in shell mode automatically.
(transient-mark-mode t) will highlight region between point and mark.
(setq query-replace-highlight t) will highlight during query.
(setq search-highlight t) highlight incremental search
(setq default-major-mode 'text-mode) will make text-mode default.
(global-font-lock-mode t t) means that we want fontification in all modes.
(setq font-lock-maximum-decoration t) denotes our interest in maximum possible fontification.
(type-break-mode) get intermittent messages to stop typing
(setq enable-recursive-minibuffers t) ;; allow recursive editing in minibuffer (resize-minibuffer-mode 1) ;; minibuffer gets resized if it becomes too big (follow-mode t) ;; follow-mode allows easier editing of long files |
(require 'uniquify) (setq uniquify-buffer-name-style 'reverse) |
; Moving cursor down at bottom scrolls only a single line, not half page (setq scroll-step 1) (setq scroll-conservatively 5) (global-set-key [delete] 'delete-char) |
(set-background-color "dark slate gray") (set-foreground-color "blanched almond") |
C-mode with Fewer Key-strokes
(setq kill-whole-line t) will make "Ctrl-k" kills an entire line if the cursor is at the beginning of line -- very useful.
(setq c-hungry-delete-key t) will delete "hungrily" in C mode! Use it to see what it does -- very useful.
(setq c-auto-newline 1) will let emacs put in a "carriage-return" for you automatically after left curly braces, right curly braces, and semi-colons in "C mode" -- very useful.
(add-hook 'c-mode-common-hook '(lambda () (turn-on-auto-fill) (setq fill-column 80) (setq comment-column 60) (modify-syntax-entry ?_ "w") ; now '_' is not considered a word-delimiter (c-set-style "ellemtel") ; set indentation style (local-set-key [(control tab)] ; move to next tempo mark 'tempo-forward-mark) )) |
(setq auto-mode-alist (append '(("\\.h$" . c++-mode)) auto-mode-alist)) |
(global-set-key [f12] 'dabbrev-expand) (define-key esc-map [f12] 'dabbrev-completion) |
With the above global key-bindings, it is possible to press 'f12' (you could change this to any other key you want) and automagically, a partial string gets expanded. For example, if you have typed "mylon" and press 'F12', "mylon" will get replaced by "MyLongIdentifierName" if that string exists somewhere in your set of buffers.
;; "funky stuff" ;; proceed with caution (setq my-key-pairs '((?! ?1) (?@ ?2) (?# ?3) (?$ ?4) (?% ?5) (?^ ?6) (?& ?7) (?* ?8) (?( ?9) (?) ?0) (?- ?_) (?\" ?') (?{ ?[) (?} ?]) ; (?| ?\\) )) (defun my-key-swap (key-pairs) (if (eq key-pairs nil) (message "Keyboard zapped!! Shift-F10 to restore!") (progn (keyboard-translate (caar key-pairs) (cadar key-pairs)) (keyboard-translate (cadar key-pairs) (caar key-pairs)) (my-key-swap (cdr key-pairs)) ) )) (defun my-key-restore (key-pairs) (if (eq key-pairs nil) (message "Keyboard restored!! F10 to Zap!") (progn (keyboard-translate (caar key-pairs) (caar key-pairs)) (keyboard-translate (cadar key-pairs) (cadar key-pairs)) (my-key-restore (cdr key-pairs)) ) )) |
The above function "swaps" the numeric keys with the respective characters (e.g., '*' with '8' and '(' with '9'). Why? It appears to me that the upper row of characters is used more often in C/C++. So we need fewer key-strokes (the 'Shift' is avoided) with the swappings in place. 'F10' and 'Shift-F10' allow u to toggle between these key-swaps.
;; "funky stuff" ;; proceed with caution (defun my-editing-function (first last len) (interactive) (if (and (boundp 'major-mode) (member major-mode (list 'c-mode 'c++-mode 'gud-mode 'fundamental-mode 'ruby-mode)) (= len 0) (> (point) 4) (= first (- (point) 1))) (cond ((and (string-equal (buffer-substring (point) (- (point) 2)) "__") (not (string-equal (buffer-substring (point) (- (point) 3)) "___"))) (progn (delete-backward-char 2) (insert-char ?- 1) (insert-char ?> 1))) ((string-equal (buffer-substring (point) (- (point) 3)) "->_") (progn (delete-backward-char 3) (insert-char ?_ 3))) ((and (string-equal (buffer-substring (point) (- (point) 2)) "..") (not (string-equal (buffer-substring (point) (- (point) 3)) "..."))) (progn (delete-backward-char 2) (insert-char ?[ 1) (insert-char ?] 1) (backward-char 1))) ((and (> (point-max) (point)) (string-equal (buffer-substring (+ (point) 1) (- (point) 2)) "[.]")) (progn (forward-char 1) (delete-backward-char 3) (insert-char ?. 1) (insert-char ?. 1) )) ) nil)) (add-hook 'after-change-functions 'my-editing-function) |
The function above is funky but useful. Having swapped the pairs ('[', '{'), ('-', '_') and (']', '}'), in order to type "->", we need to type four characters ('Shift' followed by '-' followed by 'Shift' followed by '>'). With the above code, all you need to type is two underscores: '__'). Automagically, they are converted into '->'). Similarly, two successive dots '..' are translated into '[]' (for array indexing). I find that these combinations improve my code-typing speed significantly.
shell-mode Improvements
(setq comint-buffer-maximum-size 10240) set maximum-buffer size for shell-mode (useful if some program that you're debugging spews out large amounts of output).
(add-hook 'comint-output-filter-functions 'comint-truncate-buffer) will truncate shell buffer to comint-buffer-maximum-size.
(add-hook 'comint-output-filter-functions 'comint-watch-for-password-prompt) will disalllow passwords to be shown in clear text (this is useful, for example, if you use the shell and then, login/telnet/ftp/scp etc. to other machines).
(add-hook 'comint-output-filter-functions 'comint-strip-ctrl-m) will remove ctrl-m from shell output.
(add-hook 'shell-mode-hook '(lambda () (local-set-key [home] ; move to beginning of line, after prompt 'comint-bol) (local-set-key [up] ; cycle backward through command history '(lambda () (interactive) (if (comint-after-pmark-p) (comint-previous-input 1) (previous-line 1)))) (local-set-key [down] ; cycle forward through command history '(lambda () (interactive) (if (comint-after-pmark-p) (comint-next-input 1) (forward-line 1)))) )) |
;; shell-toggle.el stuff (autoload 'shell-toggle "shell-toggle" "Toggles between the *shell* buffer and whatever buffer you are editing." t) (autoload 'shell-toggle-cd "shell-toggle" "Pops up a shell-buffer and insert a \"cd |
;; protbuf.el (load "protbuf") (protect-process-buffer-from-kill-mode 1 "shell-first") |
;; ssh.el
(load "ssh") will load ssh.el
gud-mode (debugging with gdb)
(add-hook 'gud-mode-hook '(lambda () (local-set-key [home] ; move to beginning of line, after prompt 'comint-bol) (local-set-key [up] ; cycle backward through command history '(lambda () (interactive) (if (comint-after-pmark-p) (comint-previous-input 1) (previous-line 1)))) (local-set-key [down] ; cycle forward through command history '(lambda () (interactive) (if (comint-after-pmark-p) (comint-next-input 1) (forward-line 1)))) )) |
Global key-bindings (Hot-keys)
;; global key bindings (global-set-key [C-delete] 'kill-word) (global-set-key [C-backspace] 'backward-kill-word) (global-set-key [home] 'beginning-of-line) (global-set-key [end] 'end-of-line) (global-set-key [C-home] 'beginning-of-buffer) (global-set-key [C-end] 'end-of-buffer) (global-set-key [f1] 'find-file) (global-set-key [f2] '(lambda () (interactive) (progn (fill-paragraph 1) (save-buffer)))) (global-set-key [f3] 'manual-entry) (global-set-key [f4] 'shell) (global-set-key [f5] '(lambda () (interactive) (kill-buffer (current-buffer)))) (global-set-key [S-f7] 'compile) (global-set-key [f7] 'next-error) (global-set-key [C-f7] 'kill-compilation) (global-set-key [f8] 'other-window) (global-set-key [S-right] 'other-window) (global-set-key [S-left] 'other-window) (global-set-key [f9] 'save-buffer) (global-set-key [f10] '(lambda () (interactive) (my-key-swap my-key-pairs))) (global-set-key [S-f10] '(lambda () (interactive) (my-key-restore my-key-pairs))) (global-set-key [f12] 'dabbrev-expand) (define-key esc-map [f12] 'dabbrev-completion) ; for my pc @ home (global-set-key [M-backspace] 'dabbrev-expand) ;; (global-set-key [S-f12] 'my-vm-without-new-frame) (global-set-key [C-f12] 'save-buffers-kill-emacs) ;; some machines have SunF37 instead of f12 (global-set-key [SunF37] 'dabbrev-expand) (define-key esc-map [SunF37] 'dabbrev-completion) ;; (global-set-key [S-SunF37] 'my-vm-without-new-frame) (global-set-key [C-SunF37] 'save-buffers-kill-emacs) (global-set-key "\C-x\C-b" 'electric-buffer-list) ; Make Emacs use "newline-and-indent" when you hit the Enter key so ; that you don't need to keep using TAB to align yourself when coding. (global-set-key "\C-m" 'newline-and-indent) ; capitalize current word (for example, C constants) (global-set-key "\M-u" '(lambda () (interactive) (backward-word 1) (upcase-word 1))) |
;; pager.el stuff (require 'pager) (global-set-key "\C-v" 'pager-page-down) (global-set-key [next] 'pager-page-down) (global-set-key "\ev" 'pager-page-up) (global-set-key [prior] 'pager-page-up) (global-set-key '[M-up] 'pager-row-up) (global-set-key '[M-kp-8] 'pager-row-up) (global-set-key '[M-down] 'pager-row-down) (global-set-key '[M-kp-2] 'pager-row-down) |
Using Abbreviations and Auto-Capitalization
;; Abbreviations ;; M-x edit-abbrevs allows editing of abbrevs ;; M-x write-abbrev-file will save abbrevs to file ;; C-x a i l allows us to define a local abbrev ;; M-x abbrev-mode turns abbrev-mode on/off ;; set name of abbrev file with .el extension (setq abbrev-file-name "~/.abbrevs.el") (setq-default abbrev-mode t) (setq save-abbrevs t) ;; we want abbrev mode in all modes (does not seem to work) ;; (abbrev-mode 1) ;; quietly read the abbrev file ;; (quietly-read-abbrev-file) (if (file-exists-p abbrev-file-name) (quietly-read-abbrev-file abbrev-file-name)) |
; auto-capitalize stuff (autoload 'auto-capitalize-mode "auto-capitalize" "Toggle `auto-capitalize' minor mode in this buffer." t) (autoload 'turn-on-auto-capitalize-mode "auto-capitalize" "Turn on `auto-capitalize' minor mode in this buffer." t) (autoload 'enable-auto-capitalize-mode "auto-capitalize" "Enable `auto-capitalize' minor mode in this buffer." t) (add-hook 'text-mode-hook 'turn-on-auto-capitalize-mode) (setq auto-capitalize-words '("I" "Rajeev" "Nautiyal" "Sanjeev" "Uma")) |
Latex Improvements
(setq auto-mode-alist (cons '("\\.tex$" . latex-mode) auto-mode-alist)) means that .tex files should be handled by latex-mode.
(setq tex-mode-hook '(lambda () (auto-fill-mode 1) )) |
(setq latex-mode-hook '(lambda () (auto-fill-mode 1) )) |
(autoload 'reftex-mode "reftex" "RefTeX Minor Mode" t) (autoload 'turn-on-reftex "reftex" "RefTeX Minor Mode" t) (add-hook 'LaTeX-mode-hook 'turn-on-reftex) ; with AUCTeX LaTeX mode ; (add-hook 'latex-mode-hook 'turn-on-reftex)) ; with Emacs latex mode (setq reftex-enable-partial-scans t) (setq reftex-save-parse-info t) (setq reftex-use-multiple-selection-buffers t) ;; To integrate with AUCTeX, use (setq reftex-plug-into-AUCTeX t) |
text-mode Improvements
(add-hook 'text-mode-hook '(lambda () (turn-on-auto-fill) (auto-fill-mode 1) )) |
ruby-mode Improvements
(autoload 'ruby-mode "ruby-mode" "Mode for editing ruby source files") (setq auto-mode-alist (append '(("\\.rb$" . ruby-mode)) auto-mode-alist)) (setq interpreter-mode-alist (append '(("ruby" . ruby-mode)) interpreter-mode-alist)) (autoload 'run-ruby "inf-ruby" "Run an inferior Ruby process") (autoload 'inf-ruby-keys "inf-ruby" "Set local key defs for inf-ruby in ruby-mode") (add-hook 'ruby-mode-hook '(lambda () (inf-ruby-keys) )) (autoload 'rubydb "rubydb3x" "Ruby debugger" t) |
VM-mode Improvements
; ----- VM -------- (autoload 'vm "~/emacs/vm" "Start VM on your primary inbox." t) (autoload 'vm-visit-folder "~/emacs/vm" "Start VM on an arbitrary folder." t) (autoload 'vm-mail "~/emacs/vm" "Send a mail message using VM." t) (autoload 'vm-submit-bug-report "~/emacs/vm" "Send a bug report about VM." t) (setq vm-preview-lines nil) (setq vm-highlighted-header-regexp '"^From\\|^Subject") (setq vm-preview-read-messages t) |
Emacs-lisp-mode Improvements
(add-hook 'emacs-lisp-mode-hook '(lambda () (modify-syntax-entry ?- "w") ; now '-' is not considered a word-delimiter )) |
tempo-mode for Faster Coding in C/C++
;; This is a way to hook tempo into cc-mode (defvar c-tempo-tags nil "Tempo tags for C mode") (defvar c++-tempo-tags nil "Tempo tags for C++ mode") ;;; C-Mode Templates and C++-Mode Templates (uses C-Mode Templates also) (require 'tempo) (setq tempo-interactive t) (add-hook 'c-mode-hook '(lambda () (local-set-key [f11] 'tempo-complete-tag) (tempo-use-tag-list 'c-tempo-tags) )) (add-hook 'c++-mode-hook '(lambda () (local-set-key [f11] 'tempo-complete-tag) (tempo-use-tag-list 'c-tempo-tags) (tempo-use-tag-list 'c++-tempo-tags) )) ;;; Preprocessor Templates (appended to c-tempo-tags) (tempo-define-template "c-include" '("include <" r ".h>" > n ) "include" "Insert a #include <> statement" 'c-tempo-tags) (tempo-define-template "c-ifdef" '("ifdef " (p "ifdef-clause: " clause) > n> p n "#else /* !(" (s clause) ") */" n> p n "#endif /* " (s clause)" */" n> ) "ifdef" "Insert a #ifdef #else #endif statement" 'c-tempo-tags) (tempo-define-template "c-ifndef" '("ifndef " (p "ifndef-clause: " clause) > n "#define " (s clause) n> p n "#endif /* " (s clause)" */" n> ) "ifndef" "Insert a #ifndef #define #endif statement" 'c-tempo-tags) ;;; C-Mode Templates (tempo-define-template "c-if" '(> "if (" (p "if-clause: " clause) ")" n> "{" > n> > r n "}" > n> ) "if" "Insert a C if statement" 'c-tempo-tags) (tempo-define-template "c-else" '(> "else" n> "{" > n> > r n "}" > n> ) "else" "Insert a C else statement" 'c-tempo-tags) (tempo-define-template "c-if-else" '(> "if (" (p "if-clause: " clause) ")" n> "{" > n > r n "}" > n "else" > n "{" > n> > r n "}" > n> ) "ifelse" "Insert a C if else statement" 'c-tempo-tags) (tempo-define-template "c-while" '(> "while (" (p "while-clause: " clause) ")" > n> "{" > n > r n "}" > n> ) "while" "Insert a C while statement" 'c-tempo-tags) (tempo-define-template "c-for" '(> "for (" (p "for-clause: " clause) ")" > n> "{" > n > r n "}" > n> ) "for" "Insert a C for statement" 'c-tempo-tags) (tempo-define-template "c-for-i" '(> "for (" (p "variable: " var) " = 0; " (s var) " < "(p "upper bound: " ub)"; " (s var) "++)" > n> "{" > n > r n "}" > n> ) "fori" "Insert a C for loop: for(x = 0; x < ..; x++)" 'c-tempo-tags) (tempo-define-template "c-main" '(> "int main(int argc, char *argv[])" > n> "{" > n> > r n > "return 0 ;" n> > "}" > n> ) "main" "Insert a C main statement" 'c-tempo-tags) (tempo-define-template "c-if-malloc" '(> (p "variable: " var) " = (" (p "type: " type) " *) malloc (sizeof(" (s type) ") * " (p "nitems: " nitems) ") ;" n> > "if (" (s var) " == NULL)" n> > "error_exit (\"" (buffer-name) ": " r ": Failed to malloc() " (s var) " \") ;" n> ) "ifmalloc" "Insert a C if (malloc...) statement" 'c-tempo-tags) (tempo-define-template "c-if-calloc" '(> (p "variable: " var) " = (" (p "type: " type) " *) calloc (sizeof(" (s type) "), " (p "nitems: " nitems) ") ;" n> > "if (" (s var) " == NULL)" n> > "error_exit (\"" (buffer-name) ": " r ": Failed to calloc() " (s var) " \") ;" n> ) "ifcalloc" "Insert a C if (calloc...) statement" 'c-tempo-tags) (tempo-define-template "c-switch" '(> "switch (" (p "switch-condition: " clause) ")" n> "{" > n> "case " (p "first value: ") ":" > n> p n "break;" > n> p n "default:" > n> p n "break;" > n "}" > n> ) "switch" "Insert a C switch statement" 'c-tempo-tags) (tempo-define-template "c-case" '(n "case " (p "value: ") ":" > n> p n "break;" > n> p ) "case" "Insert a C case statement" 'c-tempo-tags) (tempo-define-template "c++-class" '("class " (p "classname: " class) p > n> " {" > n "public:" > n "" > n "protected:" > n "" > n "private:" > n "" > n "};" > n ) "class" "Insert a class skeleton" 'c++-tempo-tags) |
Startup
(split-window-horizontally) ;; want two windows at startup (other-window 1) ;; move to other window (shell) ;; start a shell (rename-buffer "shell-first") ;; rename it (other-window 1) ;; move back to first window (my-key-swap my-key-pairs) ;; zap keyboard |
The above code will split the Emacs window into two, and start the shell in one of them. The last line invokes (my-key-swap my-key-pairs) which toggles some keys (search for "my-key-swap" on this page to locate its definition).
Last update: 11 Mar 2004 by Gurmeet Singh Manku
Emacs Tips & Tricks
Emacs Tips & Tricks
Power tools for Emacs users
Basic Tips & Tricks
Emacs initializationWhen Emacs is launched, a specific initialization file is read. The initialization file can contain Emacs personal preferences like the functions and key bindings examples in this document. In addition it can contain default state settings for Emacs variables. Some examples are given below.
In the MS-Windows environment the initialization file should be called _EMACS and should be put in the root directory. In UNIX based systems the file should be called .emacs and should be put in the personal home directory.
Key bindingsFor maximum typing and editing efficiency, as many keyboard keys as possible should be bound directly to Emacs functions. Some are already bound when Emacs is installed, but the great variety of keyboards and operating systems available makes it difficult for the Emacs team to pre bind functions in general. To bind a function to a key, include a statement of the form
in the initialization file. key-name is either given as an actual name within brackets like [f1] for the F1 function key, or as "\C-q" or "\M-q" for a Control-key or Escape-key sequence respectively. Remember that function that are not bound to a specific key or key sequence (actually this includes most functions) can always be accessed by typing
This document gives many examples of possible Emacs key bindings. Since the name of the keys may differ between keyboards and operating systems, the following approach can be used to find the name of a specific key. Enter
and hit the key (or key combination) in question. This will reveal the name of the key as well as the function it is currently bound to.
Complete wordEmacs includes a very useful concept called complete word. Whenever in the minibuffer, it is always possible to hit the space bar to let Emacs try to complete the current input. This can save many key strokes and a lot of time. For instance when looking for a file one can give one letter of a directory or a file name and, in case it is unique, Emacs will fill in the rest automatically. If it is not unique, Emacs will fill in as much as possible and return the different possible completions to let you choose between them.
The Emacs regionEmacs includes a powerful concept called a region. A region is like a marked area in a conventional word processor, but more useful in the way Emacs functions can be applied to it directly, for instance for sorting just a few lines of a file, or for printing a portion of a file. A region is set implicitly by many Emacs functions, but can be set manually as well using Ctrl-Space. This specifies the current cursor position as one end of the region, called the mark. The cursor can then be moved around and its position will specify the other end of the region.
Cut, copy and paste key bindingsBased on the description of a region above, defining the common functions Cut, Copy and Paste on the region becomes very useful. The functions should be bound to keys for easy access, for instance like:
An element that is cut or copied this way is put into something called the kill-ring. Calling the yank function always pastes the last entry from the kill-ring. However, after the yank function as called, it is possible to replace the pasted text by the second last entry in the ring and so on by calling the yank-pop (by default boud to the Esc-y key) function repeatedly. In effect, all cut/copy operations of the session is registered, and are easily accessible through this simple sequence of key strokes.
Column handlingA feature that are missing in most editors and word processors is the possibility to cut/copy and paste a vertical region of a file. In Emacs this is simple. The column to cut or copy is marked as a region as described above. It can then be cut calling the function kill-rectangle and re-pasted by yank-rectangle
Navigation key bindingsThese are the basic navigation keys. Many keyboard come with two sets of these keys, so each function is bound once for each key. Some of these functions are usually already bound when Emacs is installed.
UndoEmacs has the most amazing undo facility. Anything you have typed since your session began is registered and can be undone. Since this obviously is a very useful function it should be bound directly to a key. Using the keypad [-] (subtraction) key for this purpose is convenient since it indicates the nature of the operation as well as being located so far away that it isn't easily accessed by accident.
Insert/overwrite modeBy default Emacs run in insert mode. This state can be toggled with overwrite mode, and is best bound to the Insert key(s) on the keyboard.
Goto lineTo go to a specific line can be useful, especially when Emacs is used for programming. This common function can be bound to Ctrl-L by:
Window splittingThe Emacs window can be split so that is can display two or more buffers at the same time. This can be useful when comparing two files, or when editing two files simultaneously. Functions for managing window splits:
FramesAs well as displaying more than one buffer in a window, Emacs can also display more than one window (or frames as it is called in Emacs terminology). Creating and deleting frames can conveniently be done by binding the the functions as follows:
Getting informationEmacs contains lots of features and possibilities and it can be difficult to comprehend and utilize it all. However, Emacs comes with full documentation included. One of the more useful functions is apropos:
This command prompts for a name or a concept and will return list of all functions or variables that relates to the entry given. This is a very useful approach for getting to know Emacs and the possibilities it contains. Given an Emacs function or a variable it can sometimes be difficult to know exactly what it does. An explanation of a function or a variable can be obtained by:
They will prompt for a name and return a comprehensive description of it. Similarly, as described above
prompts for a key and returns the name of the key and the function it is currently bound to.
File FinderThis extension to the powerful Emacs complete-word facility is the major time saver for the frequent Emacs user. It is used within the find-file function and makes it possible to enter a given directory in the minibuffer by just entering a predefined two- to four letter sequence followed by the space key. Three different paths are given in the example below. The list can however be extended indefinetly.
The function is made an extension to the minibuffer complete-word function by:
Buffer SwitcherAfter a file has been loaded once, it is available in an Emacs buffer. Emacs is installed with lots of clever functions for fast retrieval of buffers. The function below is different however. It makes it possible to browse through the buffer list by single key-strokes only. It is also clever in its handling of the buffer stack in the way that the most frequent visited buffers (i.e. files) always are at the top of the stack.
The function is bound to a function key (for instance F4) by:
When you are definitely done with a buffer (i.e. a file) it can be convinient to remove it from the buffer stack. To silently remove the current buffer from the stack (and retrieve the next one on the stack), bind the following function to the Ctrl-Del key.
Bind by:
NavigatorFor fast navigation within an Emacs buffer it is necessary to be able to move swiftly between words. The functions below change the default Emacs behavour on this point slightly, to make them a lot more usable. Note the way that the underscore character is treated. This is convinient behaviour in programming. Other domains may have different requirements, and these functions should be easy to modify in this respect.
Bind the functions to Ctrl-Left and Ctrl-Right with:
ScrollerScrolling without moving the cursor can be achieved by the functions:
Bind the functions to the /-key and the *-key (on the numeric keypad) with:
Emacs for Programmers
ModesDuring editing Emacs can be set in different modes. The mode has some knowledge about the structure of the document the user is working on, and can assist on the organization, formatting and editing of this. This is particularly helpful when Emacs is used for programming, since programming languages in general has rigid sets of restrictions regarding document structure. For instance, programming statements are normally indented according to specific rules, and if the mode know the rules, it can do the indentation for the user automatically. There exists modes for all major and minor programming languages and most types of documents such as HTML, Perl scripts, shell scripts, Unix Makefiles, CSS etc. The mode is automatically set by Emacs based on the name or the extension of the file edited.
Color codingA very useful feature is Emacs' ability to render text with different colors and fonts. Emacs packages for color coding analyse the structure of the text and color the text according to the structure. For instance can programming comments get one color, reserved words a different color, function definitions yet another and so on. A popular color coding package that comes with the standard Emacs distribution is hilite. To use it, include the following statement in the initialization file:
During editing, it is in general impossible for the color coding package to color the text since the structure of the edited part of the document may not yet be complete. Beacuase of this, it is handy to be able to refresh the color coding manually. This is done with the function hilit-highlight-buffer which can be bound to a key combination for convenience.
Including predefined element skeletonsMany of the text elements that are used in programming has a standard form based on common skeletons. The ability to include skeletons like these with a single keystroke can be very useful and time efficient. For instance, declaration and documentation comments in Java follow the rules dictated by the javadoc automatic documentation system. A typical javadoc method comment will look something like this:
To create a skeleton that can be included directly into a java source file, include the following LISP function in the Emacs initialization file:
The function can be bound to a key by for instance:
So hitting F9 will in effect include a javadoc method comment skeleton at the cursor position and move the cursor to the position within the comment where the description should start. Along the same lines it is easy to predefine a large number of code elements, for instance while loops, if-then-else constructions, file headers etc. and make them available by single key strokes or key combinations.
Emacs Links
|
建立自己的库函数
> cat add.c:
{
return x + y;
}
> gcc -c add.c ====> 得到add.o
> ar -q libadd.a add.o ====> 得到静态库libadd.a
> cat test.c:
int add(int, int);
int main()
{
printf("%d\n", add(3, 4));
return 0;
}
> gcc test.c -ladd -L/home/xwzss/test ====> /home/xwzss/test为库所在路径
> ./a.out
7
使用truss、strace或ltrace诊断软件问题
进程无法启动,软件运行速度突然变慢,程序的"Segment Fault"等等都是让每个Unix系统用户头痛的问题,本文通过三个实际案例演示如何使用truss、strace和ltrace这三个常用的调试工具来快速诊断软件的"疑难杂症"。 举两个实例演示如何利用这三个调试工具诊断软件的"疑难杂症": -------------------------------------------------------------------------------------------------------------------------------------------------------------------- lsof 显示进程打开的文件
truss和strace用来跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;而strace最初是为SunOS系统编写的,ltrace最早出现在GNU/Debian Linux中。这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。
你不仅可以从命令行调试一个新开始的程序,也可以把truss、strace或ltrace绑定到一个已有的PID上来调试一个正在运行的程序。三个调试工具的基本使用方法大体相同,下面仅介绍三者共有,而且是最常用的三个命令行参数:
-o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。
-p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。
使用上述三个参数基本上就可以完成大多数调试任务了,下面举几个命令行例子:
strace -f -o vim.strace vim: 跟踪vim及其子进程的运行,将输出信息写到文件vim.strace。
ltrace -p 234: 跟踪一个pid为234的已经在运行的进程。
三个调试工具的输出结果格式也很相似,以strace为例:
brk(0x8063000) = 0x8063000
mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x92f) = 0x40016000
每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 truss、strace和ltrace的工作原理大同小异,都是使用ptrace系统调用跟踪调试运行中的进程,详细原理不在本文讨论范围内,有兴趣可以参考它们的源代码。
案例一:运行clint出现Segment Fault错误
操作系统:FreeBSD-5.2.1-release
clint是一个C++静态源代码分析工具,通过Ports安装好之后,运行:
Segmentation fault (core dumped)
在Unix系统中遇见"Segmentation Fault"就像在MS Windows中弹出"非法操作"对话框一样令人讨厌。OK,我们用truss给clint"把把脉":
Segmentation fault (core dumped)
# tail clint.truss
739: read(0x6,0x806f000,0x1000) = 4096 (0x1000)
739: fstat(6,0xbfbfe4d0) = 0 (0x0)
739: fcntl(0x6,0x3,0x0) = 4 (0x4)
739: fcntl(0x6,0x4,0x0) = 0 (0x0)
739: close(6) = 0 (0x0)
739: stat("/root/.clint/plugins",0xbfbfe680) ERR#2 'No such file or directory'
SIGNAL 11
SIGNAL 11
Process stopped because of: 16
process exit, rval = 139
我们用truss跟踪clint的系统调用执行情况,并把结果输出到文件clint.truss,然后用tail查看最后几行。注意看clint执行的最后一条系统调用(倒数第五行):stat("/root/.clint/plugins",0xbfbfe680) ERR#2 'No such file or directory',问题就出在这里:clint找不到目录"/root/.clint/plugins",从而引发了段错误。怎样解决?很简单:mkdir -p /root/.clint/plugins,不过这次运行clint还是会"Segmentation Fault"9。继续用truss跟踪,发现clint还需要这个目录"/root/.clint/plugins/python",建好这个目录后clint终于能够正常运行了。
案例二:vim启动速度明显变慢
操作系统:FreeBSD-5.2.1-release
vim版本为6.2.154,从命令行运行vim后,要等待近半分钟才能进入编辑界面,而且没有任何错误输出。仔细检查了.vimrc和所有的vim脚本都没有错误配置,在网上也找不到类似问题的解决办法,难不成要hacking source code?没有必要,用truss就能找到问题所在:
这里-D参数的作用是:在每行输出前加上相对时间戳,即每执行一条系统调用所耗费的时间。我们只要关注哪些系统调用耗费的时间比较长就可以了,用less仔细查看输出文件vim.truss,很快就找到了疑点:
735: 0.000014248 setsockopt(0x4,0x6,0x1,0xbfbfe3c8,0x4) = 0 (0x0)
735: 0.000013688 setsockopt(0x4,0xffff,0x8,0xbfbfe2ec,0x4) = 0 (0x0)
735: 0.000203657 connect(0x4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 'Connection refused'
735: 0.000017042 close(4) = 0 (0x0)
735: 1.009366553 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0x0)
735: 0.000019556 socket(0x2,0x1,0x0) = 4 (0x4)
735: 0.000013409 setsockopt(0x4,0x6,0x1,0xbfbfe3c8,0x4) = 0 (0x0)
735: 0.000013130 setsockopt(0x4,0xffff,0x8,0xbfbfe2ec,0x4) = 0 (0x0)
735: 0.000272102 connect(0x4,{ AF_INET 10.57.18.27:6000 },16) ERR#61 'Connection refused'
735: 0.000015924 close(4) = 0 (0x0)
735: 1.009338338 nanosleep(0xbfbfe468,0xbfbfe460) = 0 (0x0)
vim试图连接10.57.18.27这台主机的6000端口(第四行的connect()),连接失败后,睡眠一秒钟继续重试(第6行的nanosleep())。以上片断循环出现了十几次,每次都要耗费一秒多钟的时间,这就是vim明显变慢的原因。可是,你肯定会纳闷:"vim怎么会无缘无故连接其它计算机的6000端口呢?"。问得好,那么请你回想一下6000是什么服务的端口?没错,就是X Server。看来vim是要把输出定向到一个远程X Server,那么Shell中肯定定义了DISPLAY变量,查看.cshrc,果然有这么一行:setenv DISPLAY ${REMOTEHOST}:0,把它注释掉,再重新登录,问题就解决了。
案例三:用调试工具掌握软件的工作原理
操作系统:Red Hat Linux 9.0
用调试工具实时跟踪软件的运行情况不仅是诊断软件"疑难杂症"的有效的手段,也可帮助我们理清软件的"脉络",即快速掌握软件的运行流程和工作原理,不失为一种学习源代码的辅助方法。下面这个案例展现了如何使用strace通过跟踪别的软件来"触发灵感",从而解决软件开发中的难题的。
大家都知道,在进程内打开一个文件,都有唯一一个文件描述符(fd:file descriptor)与这个文件对应。而本人在开发一个软件过程中遇到这样一个问题:已知一个fd ,如何获取这个fd所对应文件的完整路径?不管是Linux、FreeBSD或是其它Unix系统都没有提供这样的API,怎么办呢?我们换个角度思考:Unix下有没有什么软件可以获取进程打开了哪些文件?如果你经验足够丰富,很容易想到lsof,使用它既可以知道进程打开了哪些文件,也可以了解一个文件被哪个进程打开。好,我们用一个小程序来试验一下lsof,看它是如何获取进程打开了哪些文件。
#include
将testlsof放入后台运行,其pid为3125。命令lsof -p 3125查看进程3125打开了哪些文件,我们用strace跟踪lsof的运行,输出结果保存在lsof.strace中:
# ./testlsof &
[1] 3125
# strace -o lsof.strace lsof -p 3125
我们以"/tmp/foo"为关键字搜索输出文件lsof.strace,结果只有一条:
readlink("/proc/3125/fd/3", "/tmp/foo", 4096) = 8
原来lsof巧妙的利用了/proc/nnnn/fd/目录(nnnn为pid):Linux内核会为每一个进程在/proc/建立一个以其pid为名的目录用来保存进程的相关信息,而其子目录fd保存的是该进程打开的所有文件的fd。目标离我们很近了。好,我们到/proc/3125/fd/看个究竟:
# ls -l
total 0
lrwx------ 1 root root 64 Nov 5 09:50 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov 5 09:50 1 -> /dev/pts/0
lrwx------ 1 root root 64 Nov 5 09:50 2 -> /dev/pts/0
lr-x------ 1 root root 64 Nov 5 09:50 3 -> /tmp/foo
# readlink /proc/3125/fd/3
/tmp/foo
答案已经很明显了:/proc/nnnn/fd/目录下的每一个fd文件都是符号链接,而此链接就指向被该进程打开的一个文件。我们只要用readlink()系统调用就可以获取某个fd对应的文件了,代码如下:
出于安全方面的考虑,在FreeBSD 5 之后系统默认已经不再自动装载proc文件系统,因此,要想使用truss或strace跟踪程序,你必须手工装载proc文件系统:mount -t procfs proc /proc;或者在/etc/fstab中加上一行:
lslk 显示进程锁定的文件
strace 调试及跟踪 , 对进程有 strace /truss 等
od 输出文件的内容
FTP 信息代码全解
110 Restart marker reply. In this case, the text is exact and not left to the particular implementation; it must read: MARK yyyy = mmmm where yyyy is User-process data stream marker, and mmmm server's equivalent marker (note the spaces between markers and "=").
重新启动标志回应。这种情况下,信息是精确的并且不用特别的处理;可以这样看:标记 yyyy = mmm 中 yyyy是 用户进程数据流标记,mmmm是服务器端相应的标记(注意在标记和等号间的空格)
120 Service ready in nnn minutes.
服务在NNN时间内可用
125 Data connection already open; transfer starting.
数据连接已经打开,开始传送数据.
150 File status okay; about to open data connection.
文件状态正确,正在打开数据连接.
200 Command okay.
命令执行正常结束.
202 Command not implemented, superfluous at this site.
命令未被执行,此站点不支持此命令.
211 System status, or system help reply.
系统状态或系统帮助信息回应.
212 Directory status.
目录状态信息.
213 File status.
文件状态信息.
214 Help message.On how to use the server or the meaning of a particular non-standard command. This reply is useful only to the human user. 帮助信息。关于如何使用本服务器或特殊的非标准命令。此回复只对人有用。
215 NAME system type. Where NAME is an official system name from the list in the Assigned Numbers document.
NAME系统类型。
220 Service ready for new user.
新连接的用户的服务已就绪
221 Service closing control connection.
控制连接关闭
225 Data connection open; no transfer in progress.
数据连接已打开,没有进行中的数据传送
226 Closing data connection. Requested file action successful (for example, file transfer or file abort).
正在关闭数据连接。请求文件动作成功结束(例如,文件传送或终止)
227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
进入被动模式
230 User logged in, proceed. Logged out if appropriate.
用户已登入。 如果不需要可以登出。
250 Requested file action okay, completed.
被请求文件操作成功完成
257 "PATHNAME" created.
路径已建立
331 User name okay, need password.
用户名存在,需要输入密码
332 Need account for login.
需要登陆的账户
350 Requested file action pending further information
对被请求文件的操作需要进一步更多的信息
421 Service not available, closing control connection.This may be a reply to any command if the service knows it must shut down.
服务不可用,控制连接关闭。这可能是对任何命令的回应,如果服务认为它必须关闭
425 Can't open data connection.
打开数据连接失败
426 Connection closed; transfer aborted.
连接关闭,传送中止。
450 Requested file action not taken.
对被请求文件的操作未被执行
451 Requested action aborted. Local error in processing.
请求的操作中止。处理中发生本地错误。
452 Requested action not taken. Insufficient storage space in system.File unavailable (e.g., file busy).
请求的操作没有被执行。 系统存储空间不足。 文件不可用
500 Syntax error, command unrecognized. This may include errors such as command line too long.
语法错误,不可识别的命令。 这可能是命令行过长。
501 Syntax error in parameters or arguments.
参数错误导致的语法错误
502 Command not implemented.
命令未被执行
503 Bad sequence of commands.
命令的次序错误。
504 Command not implemented for that parameter.
由于参数错误,命令未被执行
530 Not logged in.
没有登录
Linux环境下的网络编程
本文介绍了在Linux环境下的socket编程常用函数用法及socket编程的一般规则和客户/服务器模型的编程应注 意的事项和常遇问题的解决方法,并举了具体代 码实例。要理解本文所谈的技术问题需要读者具有一定C语言的编程经验和TCP/IP方面的基本知识。要实习本文的示例,需要Linux下的gcc编译平台 支持。
Socket定义 |
网 络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用—Socket(),该 函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用 的Socket类型有两种:流式Socket—SOCK_STREAM和数据报式Socket—SOCK_DGRAM。流式是一种面向连接的 Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。 |
Socket编程相关数据类型定义 |
计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Intenet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。 |
我们要讨论的第一个结构类型是:struct sockaddr,该类型是用来保存socket信息的: |
struct sockaddr { |
unsigned short sa_family; /* 地址族, AF_xxx */ |
char sa_data[14]; /* 14 字节的协议地址 */ }; |
sa_family一般为AF_INET;sa_data则包含该socket的IP地址和端口号。 |
另外还有一种结构类型: |
struct sockaddr_in { |
short int sin_family; /* 地址族 */ |
unsigned short int sin_port; /* 端口号 */ |
struct in_addr sin_addr; /* IP地址 */ |
unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ |
}; |
这 个结构使用更为方便。sin_zero(它用来将sockaddr_in结构填充到与struct sockaddr同样的长度)应该用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向 sockaddr_in的指针转换为指向sockaddr的指针;或者相反。sin_family通常被赋AF_INET;in_port和 sin_addr应该转换成为网络字节优先顺序;而sin_addr则不需要转换。 |
我们下面讨论几个字节顺序转换函数: |
htons()--"Host to Network Short" ; htonl()--"Host to Network long" |
ntohs()--"Network to Host Short" ; ntohl()--"Network to Host Long" |
在这里, h表示"host" ,n表示"network",s 表示"short",l表示 "long"。 |
打开socket 描述符、建立绑定并建立连接 |
socket函数原型为: |
int socket(int domain, int type, int protocol); |
domain 参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM;protocol通常赋值“0”。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。一旦通过 socket调用返回一个socket描述符,你应该将该socket与你本机上的一个端口相关联(往往当你在设计服务器端程序时需要调用该函数。随后你 就可以在该端口监听服务请求;而客户端一般无须调用该函数)。 Bind函数原型为 : |
int bind(int sockfd,struct sockaddr *my_addr, int addrlen); |
Sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。 |
最后,对于bind 函数要说明的一点是,你可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号: |
my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */ |
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */ |
通 过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr 置为INADDR_ANY,系统会自动填入本机IP地址。Bind()函数在成功被调用时返回0;遇到错误时返回“-1”并将errno置为相应的错误 号。另外要注意的是,当调用函数时,一般不要将端口号置为小于1024的值,因为1~1024是保留端口号,你可以使用大于1024中任何一个没有被占用 的端口号。 |
Connect()函数用来与远端服务器建立一个TCP连接,其函数原型为: |
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); |
Sockfd 是目的服务器的sockt描述符;serv_addr是包含目的机IP地址和端口号的指针。遇到错误时返回-1,并且errno中包含相应的错误码。进行 客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,内核会自动选择一个 未被占用的端口供客户端来使用。 |
Listen()——监听是否有服务请求 |
在服务器端程序中,当socket与某一端口捆绑以后,就需要监听该端口,以便对到达的服务请求加以处理。 |
int listen(int sockfd, int backlog); |
Sockfd是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。cklog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。 |
当listen遇到错误时返回-1,errno被置为相应的错误码。 |
故服务器端程序通常按下列顺序进行函数调用: |
socket(); bind(); listen(); /* accept() goes here */ |
accept()——连接端口的服务请求。 |
当 某个客户端试图与服务器监听的端口连接时,该连接请求将排队等待服务器accept()它。通过调用accept()函数为其建立一个连 接,accept()函数将返回一个新的socket描述符,来供这个新连接来使用。而服务器可以继续在以前的那个socket上监听,同时可以在新的 socket描述符上进行数据send()(发送)和recv()(接收)操作: |
int accept(int sockfd, void *addr, int *addrlen); |
sockfd 是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个 端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值。 |
Send()和recv()——数据传输 |
这两个函数是用于面向连接的socket上进行数据传输。 |
Send()函数原型为: |
int send(int sockfd, const void *msg, int len, int flags); |
Sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。 |
Len是以字节为单位的数据的长度。flags一般情况下置为0(关于该参数的用法可参照man手册)。 |
char *msg = "Beej was here!"; int len, bytes_sent; ... ... |
len = strlen(msg); bytes_sent = send(sockfd, msg,len,0); ... ... |
Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。所以需要对send()的返回值进行测量。当send()返回值与len不匹配时,应该对这种情况进行处理。 |
recv()函数原型为: |
int recv(int sockfd,void *buf,int len,unsigned int flags); |
Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。 |
Sendto()和recvfrom()——利用数据报方式进行数据传输 |
在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,sendto()函数原型为: |
int sendto(int sockfd, const void *msg,int len,unsigned int flags, const struct sockaddr *to, int tolen); |
该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。 |
Recvfrom()函数原型为: |
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen); |
from 是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或 当出现错误时返回-1,并置相应的errno。 |
应 注意的一点是,当你对于数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然 是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。 |
Close()和shutdown()——结束数据传输 |
当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:close(sockfd); |
你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。 |
int shutdown(int sockfd,int how); |
Sockfd的含义是显而易见的,而参数 how可以设为下列值: |
·0-------不允许继续接收数据 |
·1-------不允许继续发送数据 |
·2-------不允许继续发送和接收数据,均为允许则调用close () |
shutdown在操作成功时返回0,在出现错误时返回-1(并置相应errno)。 |
DNS——域名服务相关函数 |
由于IP地址难以记忆和读写,所以为了读写记忆方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数gethostbyname()就是完成这种转换的,函数原型为: |
struct hostent *gethostbyname(const char *name); |
函数返回一种名为hosten的结构类型,它的定义如下: |
struct hostent { |
char *h_name; /* 主机的官方域名 */ |
char **h_aliases; /* 一个以NULL结尾的主机别名数组 */ |
int h_addrtype; /* 返回的地址类型,在Internet环境下为AF-INET */ |
int h_length; /*地址的字节长度 */ |
char **h_addr_list; /* 一个以0结尾的数组,包含该主机的所有地址*/ |
}; |
#define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/ |
当 gethostname()调用成功时,返回指向struct hosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,而应该使用herror()函数来输出。 |
面向连接的客户/服务器代码实例 |
这个服务器通过一个连接向客户发送字符串"Hello,world!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。 |
该服务器软件代码见程序1: |
#include stdio.h |
#include stdlib.h |
#include errno.h |
#include string.h |
#include sys/types.h |
#include netinet/in.h |
#include sys/socket.h |
#include sys/wait.h |
#define MYPORT 3490 /*服务器监听端口号 */ |
#define BACKLOG 10 /* 最大同时连接请求数 */ |
main() |
{ |
intsock fd,new_fd; /* 监听socket: sock_fd,数据传输socket:new_fd* |
/ |
struct sockaddr_in my_addr; /* 本机地址信息 */ |
struct sockaddr_in their_addr; /* 客户地址信息 */ |
n_size; |
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /*错误检测 |
*/ |
perror("socket"); exit(1); } |
my_addr.sin_family=AF_INET; |
my_addr.sin_port=htons(MYPORT); |
my_addr.sin_addr.s_addr = INADDR_ANY; |
bzero(&(my_addr.sin_zero),8); |
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockad |
dr)) |
== -1) {/*错误检测*/ |
perror("bind"); exit(1); } |
if (listen(sockfd, BACKLOG) == -1) {/*错误检测*/ |
perror("listen"); exit(1); } |
while(1) { /* main accept() loop */ |
sin_size = sizeof(struct sockaddr_in); |
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, |
&sin_size)) == -1) { |
perror("accept"); continue; } |
printf("server: got connection from %s |
", |
inet_ntoa(their_addr.sin_addr)); |
if (!fork()) { /* 子进程代码段 */ |
if (send(new_fd, "Hello, world! |
", 14, 0) == -1) |
perror("send"); close(new_fd); exit(0); } |
close(new_fd); /* 父进程不再需要该socket */ |
waitpid(-1,NULL,WNOHANG) > 0 /*等待子进程结束,清除子进程所占用资源 |
*/ |
} |
} |
(程序1) |
服 务器首先创建一个Socket,然后将该Socket与本地地址/端口号捆绑,成功之后就在相应的socket上监听,当accpet捕捉到一个连接服务 请求时,就生成一个新的socket,并通过这个新的socket向客户端发送字符串"Hello,world!",然后关闭该socket。 |
fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。 |
客户端软件代码部分见程序2: |
#includestdio.h |
#include stdlib.h |
#include errno.h |
#include string.h |
#include netdb.h |
#include sys/types.h |
#include netinet/in.h |
#include sys/socket.h |
#define PORT 3490 |
#define MAXDATASIZE 100 /*每次最大数据传输量 */ |
int main(int argc, char *argv[]) |
{ |
int sockfd, numbytes; |
char buf[MAXDATASIZE]; |
struct hostent *he; |
struct sockaddr_in their_addr; |
if (argc != 2) { |
fprintf(stderr,"usage: client hostname |
"); exit(1); } |
if((he=gethostbyname(argv[1]))==NULL) { |
herror("gethostbyname"); exit(1); } |
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { |
perror("socket"); exit(1); } |
their_addr.sin_family=AF_INET; |
their_addr.sin_port=htons(PORT); |
their_addr.sin_addr = *((struct in_addr *)he->h_addr); |
bzero(&(their_addr.sin_zero),8); |
if (connect(sockfd, (struct sockaddr *)&their_addr, |
sizeof(struct sockaddr)) == -1) {/*错误检测*/ |
perror("connect"); exit(1); } |
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { |
perror("recv"); exit(1); } |
buf[numbytes] = ''; |
printf("Received: %s",buf); |
close(sockfd); |
return 0; |
} |
(程序2) |
客户端代码相对来说要简单一些,首先通过服务器域名获得其IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket,结束程序。 |
无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。 |
关 于阻塞(blocking)的概念和select()函数当服务器运行到accept语句时,而没有客户连接服务请求到来,那么会发生什么情况?这时服务 器就会停止在accept语句上等待连接服务请求的到来;同样,当程序运行到接收数据语句时,如果没有数据可以读取,则程序同样会停止在接收语句上。这种 情况称为blocking。但如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接;否则就继续做其他事情,则可以通过将Socke设置为非 阻塞方式来实现:非阻塞socket在没有客户在等待时就使accept调用立即返回。 |
#include unistd.h |
#include fcntl.h |
. . . . ; sockfd = socket(AF_INET,SOCK_STREAM,0); |
fcntl(sockfd,F_SETFL,O_NONBLOCK); . . . . . |
通 过设置socket为非阻塞方式,可以实现“轮询”若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,并 且返回值置为-1,并且errno置为EWOULDBLOCK。但是这种“轮询”会使CPU处于忙等待方式,从而降低性能。考虑到这种情况,假设你希望服 务器监听连接服务请求的同时从已经建立的连接读取数据,你也许会想到用一个accept语句和多个recv()语句,但是由于accept及recv都是 会阻塞的,所以这个想法显然不会成功。 |
调 用非阻塞的socket会大大地浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一 组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选 出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销。Select函数原型为: |
int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exeptfds,struct timeval *timeout); |
其 中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标 准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值是需 要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件描述 符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏: |
FD_ZERO(fd_set *set)----清除一个文件描述符集; |
FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中; |
FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除 |
; |
FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。 |
Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为: |
struct timeval { |
int tv_sec; /* seconds */ |
int tv_usec; /* microseconds */ |
}; |
我们通过程序3来说明: |
#include sys/time.h |
#include sys/types.h |
#include unistd.h |
#define STDIN 0 /*标准输入文件描述符*/ |
main() |
{ |
struct timeval tv; |
fd_set readfds; |
tv.tv_sec = 2; |
tv.tv_usec = 500000; |
FD_ZERO(&readfds); |
FD_SET(STDIN,&readfds); |
/* 这里不关心写文件和异常处理文件描述符集合 */ |
select(STDIN+1, &readfds, NULL, NULL, &tv); |
if (FD_ISSET(STDIN, &readfds)) printf("A key was pressed! |
"); |
else printf("Timed out. |
"); |
} |
(程序3) |
select()在被监视端口等待2.5秒钟以后,就从select返回 |
几个重要的Linux系统内核文件介绍
在网络中,不少服务器采用的是Linux系统。为了进一步提高服务器的性能,可能需要根据特定的硬件及需求重新编译Linux内核。编译Linux内核,需要根据规定的步骤进行,编译内核过程中涉及到几个重要的文件。比如对于RedHat Linux,在/boot目录下有一些与Linux内核有关的文件,进入/boot执行:ls –l。编译过RedHat Linux内核的人对其中的System.map 、vmlinuz、initrd-2.4.7-10.img印象可能比较深刻,因为编译内核过程中涉及到这些文件的建立等操作。那么这几个文件是怎么产生的?又有什么作用呢?本文对此做些介绍。
一、vmlinuz
vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核,它位于/boot/vmlinuz,它一般是一个软链接。
vmlinuz的建立有两种方式。一是编译内核时通过“make zImage”创建,然后通过:
“cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage /boot/vmlinuz”产生。zImage适用于小内核的情况,它的存在是为了向后的兼容性。二是内核编译时通过命令make bzImage创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz”产生。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起误解,bz表示“big zImage”。 bzImage中的b是“big”意思。
zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于,老的zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么可以采用zImage 或bzImage之一,两种方式引导的系统运行时是相同的。大的内核采用bzImage,不能采用zImage。
vmlinux是未压缩的内核,vmlinuz是vmlinux的压缩文件。
二、 initrd-x.x.x.img
initrd是“initial ramdisk”的简写。initrd一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。比如,使用的是scsi硬盘,而内核vmlinuz中并没有这个scsi硬件的驱动,那么在装入scsi模块之前,内核不能加载根文件系统,但scsi模块存储在根文件系统的/lib/modules下。为了解决这个问题,可以引导一个能够读实际内核的initrd内核并用initrd修正scsi引导问题。initrd-2.4.7-10.img是用gzip压缩的文件,下面来看一看这个文件的内容。
initrd实现加载一些模块和安装文件系统等。
initrd映象文件是使用mkinitrd创建的。mkinitrd实用程序能够创建initrd映象文件。这个命令是RedHat专有的。其它Linux发行版或许有相应的命令。这是个很方便的实用程序。具体情况请看帮助:man mkinitrd
下面的命令创建initrd映象文件:
三、 System.map
System.map是一个特定内核的内核符号表。它是你当前运行的内核的System.map的链接。
内核符号表是怎么创建的呢? System.map是由“nm vmlinux”产生并且不相关的符号被滤出。对于本文中的例子,编译内核时,System.map创建在/usr/src/linux-2.4/System.map。像下面这样:
nm /boot/vmlinux-2.4.7-10 > System.map 下面几行来自/usr/src/linux-2.4/Makefile: nm vmlinux | grep -v '(compiled)|(.o$$)|( [aUw] )|(..ng$$)|(LASH[RL]DI)' | sort > System.map 然后复制到/boot: cp /usr/src/linux/System.map /boot/System.map-2.4.7-10 |
在进行程序设计时,会命名一些变量名或函数名之类的符号。Linux内核是一个很复杂的代码块,有许许多多的全局符号。
Linux内核不使用符号名,而是通过变量或函数的地址来识别变量或函数名。比如不是使用size_t BytesRead这样的符号,而是像c0343f20这样引用这个变量。
对于使用计算机的人来说,更喜欢使用那些像size_t BytesRead这样的名字,而不喜欢像c0343f20这样的名字。内核主要是用c写的,所以编译器/连接器允许我们编码时使用符号名,当内核运行时使用地址。
然而,在有的情况下,我们需要知道符号的地址,或者需要知道地址对应的符号。这由符号表来完成,符号表是所有符号连同它们的地址的列表。Linux 符号表使用到2个文件:
/proc/ksyms
System.map
/proc/ksyms是一个“proc file”,在内核引导时创建。实际上,它并不真正的是一个文件,它只不过是内核数据的表示,却给人们是一个磁盘文件的假象,这从它的文件大小是0可以看出来。然而,System.map是存在于你的文件系统上的实际文件。当你编译一个新内核时,各个符号名的地址要发生变化,你的老的System.map具有的是错误的符号信息。每次内核编译时产生一个新的System.map,你应当用新的System.map来取代老的System.map。
虽然内核本身并不真正使用System.map,但其它程序比如klogd, lsof和ps等软件需要一个正确的System.map。如果你使用错误的或没有System.map,klogd的输出将是不可靠的,这对于排除程序故障会带来困难。没有System.map,你可能会面临一些令人烦恼的提示信息。
另外少数驱动需要System.map来解析符号,没有为你当前运行的特定内核创建的System.map它们就不能正常工作。
Linux的内核日志守护进程klogd为了执行名称-地址解析,klogd需要使用System.map。System.map应当放在使用它的软件能够找到它的地方。执行:man klogd可知,如果没有将System.map作为一个变量的位置给klogd,那么它将按照下面的顺序,在三个地方查找System.map:
/boot/System.map /System.map /usr/src/linux/System.map |
System.map也有版本信息,klogd能够智能地查找正确的映象(map)文件。
shell判断某一目录下是否有文件存在
du -s dir 返回第一个参数为0,可判断为空目录,没有文件,或者文件为0字节
ls -al|wc -l
use find:
[[ -z $(find -type d -empty -name dirname) ]]&&echo No empty||echo empty
another try (in Linux):
! ls -A d1 | grep -q '.' && echo empty || echo not empty