状況: 私のコードは、基本的に Linux カーネルのドライバーにハッキングされています。重要な生イベントがメイン システムに送信される前に、ユーザー空間のアプリケーションに通知したいと考えています。
解決の手順: カーネル空間から UDP パケットを送信する良い例を見つけました: http://kernelnewbies.org/Simple_UDP_Server。彼らは INADDR_LOOPBACK をターゲットアドレスとして使用します。これはまさに私が望むものです。
これは割り込みコンテキストであるため、ワーク キューを使用してパケットを送信することにしました (BUG: Scheduling while atomic without it が発生しました)。したがって、私の送信コードは、メイン プロセスの INIT_WORK と schedule_work で起動されるワーク キュー構造体にラップされた kernelnewbies コードに基づいています。私は自分の作業キューを宣言していません。
この質問は、ローカルホストとの間でデータを送受信できないことを示唆しているため、Netpoll API は使用していません。「自分宛には送信できません」
問題: カーネルから送信され、UDP レシーバーから受信されたデータがほとんど一致しません。なぜこれが起こるのか分かりません。
ワーク キューの構造体の定義を含む、テスト用のダミー データのコード:
static struct socket *sock_send;
static struct sockaddr_in addr_send;
static struct ksocket_workmessage {
unsigned char *buf;
int len;
struct work_struct workmessage;
} workmsg;
unsigned char testmsg[] = {'T', 'e', 's', 't', 'i', 'n', 'g', 'm', 's', 'g', '\0'};
workmsg.buf = testmsg;
workmsg.len = 11;
INIT_WORK(&workmsg.workmessage, handle_workmessage);
schedule_work(&workmsg.workmessage);
実際のパケットの送信は、kernelnewbies の例の「int ksocket_send」のようなものです。唯一の違いは、send_socket が静的であり、ワーク キューから container_of を使用して buf と len を取得する必要があることです。私は完全に静的なコンテキストで作業しています。私の handle_workmessage メソッドも静的です。
static void handle_workmessage(struct work_struct *work)
{
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
int size = 0;
struct ksocket_workmessage *workmsg = container_of(work, struct ksocket_workmessage, workmessage);
if (sock_send->sk==NULL)
return;
iov.iov_base = workmsg->buf;
iov.iov_len = workmsg->len;
msg.msg_flags = 0;
msg.msg_name = &addr_send;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
oldfs = get_fs();
set_fs(KERNEL_DS);
size = sock_sendmsg(sock_send,&msg,workmsg->len);
set_fs(oldfs);
}
受信側は次のようになります。
int main(int argc, char**argv)
{
int sockfd,n;
struct sockaddr_in servaddr;
socklen_t len;
unsigned char mesg[1000];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(REC_PORT);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
for (;;)
{
n = recv(sockfd,mesg,1000,0);
printf("-------------------------------------------------------\n");
mesg[n] = 0;
printf("Received the following: %d bytes\n", n);
printf("%s",mesg);
printf("%c",mesg[0]);
printf(",%c",mesg[1]);
printf(",%c",mesg[2]);
printf(",%c",mesg[3]);
printf(",%c",mesg[4]);
printf(",%c",mesg[5]);
printf(",%c",mesg[6]);
printf(",%c",mesg[7]);
printf(",%c",mesg[8]);
printf(",%c\n",mesg[9]);
//printf("%c\n",mesg[0]);
printf("-------------------------------------------------------\n");
memset(mesg, 0, sizeof(mesg));
}
}
テスト目的で常にまったく同じメッセージを送信しているにもかかわらず、出力が破損しているように見えます。
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
����d����,�,�,�,d,�,�,�,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,�,�,�,�,2,k,�,�,�
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�<����,<,�,�,�,,,,
,=
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,,%,�,,,,,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
TestingmsgT,e,s,t,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
����Vk��1k ,�,�,�,�,V,k,�,�,1
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
TestingmsgT,e,s,t,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,,,,,�,,�,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,,
,�,,,,,�,<
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�}|�ingmsg�,},|,�,i,n,g,m,s,g
-------------------------------------------------------
この理由は何でしょうか?期待される出力 "TestingmsgT,e,s,t,i,n,g,m,s,g" で動作する場合があるため、技術的な制限ではありません。11 バイトしか送信しないため、パケットの断片化も発生しないはずです。パケットロスもありません。パケットを送信するたびに、受信もされます。
更新: IT WORKS ..しかし、なぜ最初に、alk からのコメントのおかげで、明らかなことを忘れてしまったのかわかりません。データが送信される直前にログを記録します。schedule_work を呼び出す前にログを記録しました。これで、iov から void * ポインターに格納される前に、send メソッド workmsg->buf に直接ログインするようになりました。データはすでに破損しています。
構造体 ksocket_workmessage には char * があり、私のデータは char [] で、構造体のポインターに割り当てられました。
私が今行ったことは、構造体 ksocket_workmessage 内のデータ型を変更することです。
struct ksocket_workmessage {
unsigned char buf[11];
int len;
struct work_struct workmessage;
} workmsg;
ポインターがなくなったため、unsigned char testmsg[] を作成できなかったため、buf を直接割り当てました。
workmsg.buf[0] = 'T';
workmsg.buf[1] = 'e';
workmsg.buf[2] = 's';
workmsg.buf[3] = 't';
workmsg.buf[4] = 'i';
workmsg.buf[5] = 'n';
workmsg.buf[6] = 'g';
workmsg.buf[7] = 'm';
workmsg.buf[8] = 's';
workmsg.buf[9] = 'g';
workmsg.buf[10] = '\0';
私の最初のアプローチが失敗した場所を誰かが教えてくれれば、それを正しい答えとして喜んで受け入れます。