Tinyhttpd项目学习及httpd学习

2023-10-30

在参考网络有关Tinyhttpd的内容后,我打算写下自己的学习过程与自己的理解,我会在结尾附上参考的链接。我把代码放在了gitee:Tinyhttpd学习

1.Tinyhttpd是一个轻量级的HTTP服务器。

2.学习该项目可以学习web服务器在收到静态页面请求和CGI请求的一些基本的处理逻辑。

在开始这一切之前,我并不了解http,所以我先从分析程序开始,到了最后面再写http相关。

在源码httpd.c中,主要有以下函数

void accept_request(void *);                //线程函数

void bad_request(int);                //出错

void cat(int, FILE *);                //send 文件描述符中的数据。

void cannot_execute(int);                //出错

void error_die(const char *);                //出错

void execute_cgi(int, const char *, const char *, const char *);        //执行CGI脚本的函数

int get_line(int, char *, int);                //读取socket文件描述符中的数据

void headers(int, const char *);        //send 相关数据

void not_found(int);                //出错

void serve_file(int, const char *);                //send 普通文件

int startup(u_short *);        //该函数主要创建socket bind,并且进行监听,并返回socket 函数的返回值(文件描述符)。该函数的传参是u_short类型的端口号。

void unimplemented(int);                //出错

 下面的图片是源程序的结构

 该图片来源与网络,但我根据我的阅读,添加了些许内容,详情请看参考链接。

当执行CGI脚本时候程序的运行结构(图片来源与网络): 

以下是源程序,我对代码进行了部分的注释: 

/* J. David's webserver */
/* This is a simple webserver.
 * Created November 1999 by J. David Blackstone.
 * CSE 4344 (Network concepts), Prof. Zeigler
 * University of Texas at Arlington
 */
/* This program compiles for Sparc Solaris 2.6.
 * To compile for Linux:
 *  1) Comment out the #include <pthread.h> line.
 *  2) Comment out the line that defines the variable newthread.
 *  3) Comment out the two lines that run pthread_create().
 *  4) Uncomment the line that runs accept_request().
 *  5) Remove -lsocket from the Makefile.
 */
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdint.h>

#define ISspace(x) isspace((int)(x))

#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
#define STDIN   0
#define STDOUT  1
#define STDERR  2

void accept_request(void *);
void bad_request(int);
void cat(int, FILE *);
void cannot_execute(int);
void error_die(const char *);
void execute_cgi(int, const char *, const char *, const char *);
int get_line(int, char *, int);
void headers(int, const char *);
void not_found(int);
void serve_file(int, const char *);
int startup(u_short *);
void unimplemented(int);

/**********************************************************************/
/* A request has caused a call to accept() on the server port to
 * return.  Process the request appropriately.
 * Parameters: the socket connected to the client */
/**********************************************************************/
void accept_request(void *arg)
{
    int client = (intptr_t)arg;
    char buf[1024];
    size_t numchars;
    char method[255];
    char url[255];
    char path[512];
    size_t i, j;
    struct stat st;
    int cgi = 0;      /* becomes true if server decides this is a CGI
                       * program */
    char *query_string = NULL;
    //从client 中读取内容到buf,numchars是字符串中的字符个数。
    numchars = get_line(client, buf, sizeof(buf));
    i = 0; j = 0;
    while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
    {
        //将buf的内容读取到method,最多255个
        method[i] = buf[i];
        i++;
    }
    
    j=i;

    method[i] = '\0';
    printf("method==%s\n",method);
    //比较两个字符串,忽略大小写
    if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    {
        //if不是get和post,

        unimplemented(client);
        return;
    }
    //如果是POST请求,cgi=1
    if (strcasecmp(method, "POST") == 0)
        cgi = 1;

    i = 0;
    //这里的buf[j]是上面没有写入method[]中的内容,即第256个字符之外的内容。
    //这里主要是为了过滤空格。
    while (ISspace(buf[j]) && (j < numchars))
        j++;

    while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
    {
        //吧buf中的url传给数组url[]
        url[i] = buf[j];
        i++; j++;
    }
    url[i] = '\0';
    //如果是GET请求
    printf("url==%s\n",url);
    if (strcasecmp(method, "GET") == 0)
    {
        
        query_string = url;
        //在url中找除'?'和'\0'
        while ((*query_string != '?') && (*query_string != '\0'))
            query_string++;
        if (*query_string == '?')
        {
            //如果有'?',cgi=1,'?'变'\0'
            cgi = 1;
            printf("iiiii\n");
            *query_string = '\0';
            query_string++;
        }
    }

    sprintf(path, "htdocs%s", url);
    printf("path=%s\n",path);
    if (path[strlen(path) - 1] == '/')
        strcat(path, "index.html");
    printf("path2%s\n",path);
    if (stat(path, &st) == -1) {
        //请求的页面为找到
        while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
            numchars = get_line(client, buf, sizeof(buf));
        not_found(client);
        //结束线程
    }
    else
    {
        //如果能够找到
        if ((st.st_mode & S_IFMT) == S_IFDIR)
            strcat(path, "/index.html");
        if ((st.st_mode & S_IXUSR) ||
                (st.st_mode & S_IXGRP) ||
                (st.st_mode & S_IXOTH)    )
            cgi = 1;
        printf("cgi=%d\n",cgi);
        if (!cgi)
        {
            
            serve_file(client, path);
        }
        else
            execute_cgi(client, path, method, query_string);
    }

    close(client);
}

/**********************************************************************/
/* Inform the client that a request it has made has a problem.
 * Parameters: client socket */
/**********************************************************************/
void bad_request(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
    send(client, buf, sizeof(buf), 0);
    sprintf(buf, "Content-type: text/html\r\n");
    send(client, buf, sizeof(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, sizeof(buf), 0);
    sprintf(buf, "<P>Your browser sent a bad request, ");
    send(client, buf, sizeof(buf), 0);
    sprintf(buf, "such as a POST without a Content-Length.\r\n");
    send(client, buf, sizeof(buf), 0);
}

/**********************************************************************/
/* Put the entire contents of a file out on a socket.  This function
 * is named after the UNIX "cat" command, because it might have been
 * easier just to do something like pipe, fork, and exec("cat").
 * Parameters: the client socket descriptor
 *             FILE pointer for the file to cat */
/**********************************************************************/
void cat(int client, FILE *resource)
{
    char buf[1024];

    fgets(buf, sizeof(buf), resource);
    while (!feof(resource))
    {
        send(client, buf, strlen(buf), 0);
        fgets(buf, sizeof(buf), resource);
    }
}

/**********************************************************************/
/* Inform the client that a CGI script could not be executed.
 * Parameter: the client socket descriptor. */
/**********************************************************************/
void cannot_execute(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
    send(client, buf, strlen(buf), 0);
}

/**********************************************************************/
/* Print out an error message with perror() (for system errors; based
 * on value of errno, which indicates system call errors) and exit the
 * program indicating an error. */
/**********************************************************************/
void error_die(const char *sc)
{
    perror(sc);
    exit(1);
}

/**********************************************************************/
/* Execute a CGI script.  Will need to set environment variables as
 * appropriate.
 * Parameters: client socket descriptor
 *             path to the CGI script */
/**********************************************************************/
void execute_cgi(int client, const char *path,
        const char *method, const char *query_string)
{
    printf("client=%d\n",client);
    char buf[1024];
    int cgi_output[2];
    int cgi_input[2];
    pid_t pid;
    int status;
    int i;
    char c;
    int numchars = 1;
    int content_length = -1;

    buf[0] = 'A'; buf[1] = '\0';
    //这里还有GET请求
    if (strcasecmp(method, "GET") == 0)
    {
        printf("the get\n");
        while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
            numchars = get_line(client, buf, sizeof(buf));
    }
    else if (strcasecmp(method, "POST") == 0) /*POST*/
    {
        printf("the post\n");
        numchars = get_line(client, buf, sizeof(buf));
        printf("post buf %s\n",buf);
        while ((numchars > 0) && strcmp("\n", buf))
        {
            buf[15] = '\0';
            if (strcasecmp(buf, "Content-Length:") == 0)
                content_length = atoi(&(buf[16]));
            numchars = get_line(client, buf, sizeof(buf));
        }
        if (content_length == -1) {
            bad_request(client);
            return;
        }
    }
    else/*HEAD or other*/
    {
    }

    //管道
    printf("%d\n",__LINE__);
    if (pipe(cgi_output) < 0) {
        cannot_execute(client);
        return;
    }
    if (pipe(cgi_input) < 0) {
        cannot_execute(client);
        return;
    }

    if ( (pid = fork()) < 0 ) {
        cannot_execute(client);
        return;
    }
    //响应请求
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);
    if (pid == 0)  /* child: CGI script */
    {
        char meth_env[255];
        char query_env[255];
        char length_env[255];
        // sprintf(meth_env, "REQUEST_METHOD=%s", method);
        // printf("meth_env=%s\n",meth_env);
        dup2(cgi_output[1], STDOUT);
        dup2(cgi_input[0], STDIN);
        close(cgi_output[0]);
        close(cgi_input[1]);
        sprintf(meth_env, "REQUEST_METHOD=%s", method);
        printf("meth_env=%s\n",meth_env);
        //putenv改变或增加一个环境变量
        putenv(meth_env);
        if (strcasecmp(method, "GET") == 0) {
            sprintf(query_env, "QUERY_STRING=%s", query_string);
            putenv(query_env);
        }
        else {   /* POST */
            sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
            putenv(length_env);
        }
        //执行CGI脚本。
        execl(path, NULL);
        exit(0);
    } else {    /* parent */
        close(cgi_output[1]);
        close(cgi_input[0]);
        if (strcasecmp(method, "POST") == 0)
            for (i = 0; i < content_length; i++) {
                recv(client, &c, 1, 0);
                //从client来的请求发送给子进程
                printf("c=%c\n",c);
                write(cgi_input[1], &c, 1);
            }
        while (read(cgi_output[0], &c, 1) > 0)
        {
            printf("cc=%c\n",c);
            //将从管道中读取(子进程的来的数据)的数据发给client,也就是网页。
            send(client, &c, 1, 0);
        }
        close(cgi_output[0]);
        close(cgi_input[1]);
        waitpid(pid, &status, 0);
    }
}

/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
 * carriage return, or a CRLF combination.  Terminates the string read
 * with a null character.  If no newline indicator is found before the
 * end of the buffer, the string is terminated with a null.  If any of
 * the above three line terminators is read, the last character of the
 * string will be a linefeed and the string will be terminated with a
 * null character.
 * Parameters: the socket descriptor
 *             the buffer to save the data in
 *             the size of the buffer
 * Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
            if (c == '\r')
            {
                //MSG_PEEK,此标志使接收操作从接收队列的开头返回数据,而无需从队列中删除该数据。
                //因此,随后的接收呼叫将返回相同的数据。
                n = recv(sock, &c, 1, MSG_PEEK);
                /* DEBUG printf("%02X\n", c); */
                if ((n > 0) && (c == '\n'))
                    //如果\r后面是\n 则继续读取数据,(这里有点奇怪)
                    recv(sock, &c, 1, 0);
                else
                    //if \r 后面不是\n ,那就让他是\n,不久后循环退出。
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';

    return(i);
}

/**********************************************************************/
/* Return the informational HTTP headers about a file. */
/* Parameters: the socket to print the headers on
 *             the name of the file */
/**********************************************************************/
void headers(int client, const char *filename)
{
    char buf[1024];
    (void)filename;  /* could use filename to determine file type */

    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    send(client, buf, strlen(buf), 0);
    printf("buf1=%s\n",buf);
    strcpy(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    printf("buf2=%s\n",buf);
    
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    printf("buf3=%s\n",buf);
    strcpy(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    printf("buf4=%s\n",buf);

}

/**********************************************************************/
/* Give a client a 404 not found status message. */
/**********************************************************************/
void not_found(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the resource specified\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "is unavailable or nonexistent.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
}

/**********************************************************************/
/* Send a regular file to the client.  Use headers, and report
 * errors to client if they occur.
 * Parameters: a pointer to a file structure produced from the socket
 *              file descriptor
 *             the name of the file to serve */
/**********************************************************************/
void serve_file(int client, const char *filename)
{
    FILE *resource = NULL;
    int numchars = 1;
    char buf[1024];

    buf[0] = 'A'; buf[1] = '\0';
    //没有看到这个while的用处
    while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
        numchars = get_line(client, buf, sizeof(buf));

    resource = fopen(filename, "r");
    if (resource == NULL)
        not_found(client);
    else
    {
        headers(client, filename);
        cat(client, resource);
    }
    fclose(resource);
}

/**********************************************************************/
/* This function starts the process of listening for web connections
 * on a specified port.  If the port is 0, then dynamically allocate a
 * port and modify the original port variable to reflect the actual
 * port.
 * Parameters: pointer to variable containing the port to connect on
 * Returns: the socket */
/**********************************************************************/
int startup(u_short *port)
{
    int httpd = 0;
    
    int on = 1;
    struct sockaddr_in name;
    //PF_INET is AF_INET 
    httpd = socket(PF_INET, SOCK_STREAM, 0);
    if (httpd == -1)
        error_die("socket");
    //init name value
    memset(&name, 0, sizeof(name));
    name.sin_family = AF_INET;
    name.sin_port = htons(*port);
    //INADDR_ANY=0.0.0.0
    name.sin_addr.s_addr = htonl(INADDR_ANY);
   
    //设置socket属性
    if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)  
    {  
        error_die("setsockopt failed");
    }
    //绑定
    if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
        error_die("bind");
    //
    //动态的分配端口,从对端得到sin_port
    if (*port == 0)  /* if dynamically allocating a port */
    {
        socklen_t namelen = sizeof(name);
        if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
            error_die("getsockname");
        *port = ntohs(name.sin_port);
    }
    if (listen(httpd, 5) < 0)
        error_die("listen");
    return(httpd);
}

/**********************************************************************/
/* Inform the client that the requested web method has not been
 * implemented.
 * Parameter: the client socket */
/**********************************************************************/
void unimplemented(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
}

/**********************************************************************/

int main(void)
{
    int server_sock = -1;

    //u_short port = 4000;
    u_short port = 9734;
    //刚开始看程序还有点懵,这client_sock怎么是-1呢,后来看到534行,就明白了
    int client_sock = -1;
    struct sockaddr_in client_name;
    socklen_t  client_name_len = sizeof(client_name);
    pthread_t newthread;
    /*建立socket,bind,listen,返回socket的文件描述符*/
    server_sock = startup(&port);//自定义函数,
    printf("httpd running on port %d\n", port);
    /********************************************/
    while (1)
    {
        client_sock = accept(server_sock,   
                (struct sockaddr *)&client_name,
                &client_name_len);
        if (client_sock == -1)
            error_die("accept");
        /* accept_request(&client_sock); */
        if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
            perror("pthread_create");
    }

    close(server_sock);

    return(0);
}

 以下是GET和POST,以及CGI脚本的相关知识:

http请求:http请求由三部分组成,分别是:起始行、消息报头、请求正文

Request Line<CRLF>
 
Header-Name: header-value<CRLF>
 
Header-Name: header-value<CRLF>
 
//一个或多个,均以<CRLF>结尾
 
<CRLF>
 
body//请求正文

1、起始行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:

Method Request-URI HTTP-Version CRLF

其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。 

2、请求方法(所有方法全为大写)有多种,各个方法的解释如下:

  • GET 请求获取Request-URI所标识的资源
  • POST 在Request-URI所标识的资源后附加新的数据
  • HEAD 请求获取由Request-URI所标识的资源的响应消息报头
  • PUT 请求服务器存储一个资源,并用Request-URI作为其标识
  • DELETE 请求服务器删除Request-URI所标识的资源
  • TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
  • CONNECT 保留将来使用
  • OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求

 对应到程序中,void accept_request(void *arg)中有一个method[256]数组,他就是用来储存请求方法的。在源程序中,有GET和POST请求方法。

下面通过运行代码来理解这个过程(这个程序该如何在linux上运行,你需要注意makefile中pthread链接选项,在执行cgi脚本时,你需要注意脚本的解析器在在哪,which perl,这个时候可能还是会有问题,但是不要烦躁,百度一下)。

执行./httpd,然后在浏览器中输入127.0.0.1:9734,注意9734是我自己选的端口号,你的根据你自己程序中定义的来(我是在window下打开的浏览器,因为我使用的是WSL2+ubuntu,在vscode中写程序),输入red点击提交就可以看到。

当输入127.0.0.0:9734回车后响应GET请求,执行serve_file(client, path),打开html页面。打印他们的值如图所示。

 打印headers()函数中的数组buf

可以得到如下内容

 源代码中的cat()函数主要是发送index.html中的内容,我没有打印cat中发送的内容,有需要自己打印一下。

这样一来你就能看到HTTP对响应GET请求的,他的报文的回复格式

 图片来源与网络。

到这里我想我差不都对GET请求有了基本的了解。

一个意外:意外的在GET后得到了下面的结果(待研究,不太清楚是什么原因搞出来的).

 输入red,点击提交是POST请求,打印如图所示

我一直奇怪这color.cgi到底是怎么发送给s端的,后来我在index.html中看到了答案。

现在还剩下最后一个问题,那就是管道在程序中的作用是什么。

在程序中子进程中有一行execl(path, NULL),这个是用来执行CGI脚本的。

什么是CGI脚本呢?

看完这个,我认为CGI脚本(子进程)通过cgi_input[0]也即STDIN来从父进程cgi_input[1]获取数据,通过cgi_output[1]也即STDOUT发送数据给父进程cgi_output[0].但是直到现在我还是不能特别清楚的解释那两个环境变量的作用。

对于那两个环境变量的问题,可能需要学习CGI脚本。如果以后有机会接触,在解决这个问题。

如果你有什么疑问或者你认为我写错了,或者知道我在文中遗留问题的答案,欢迎联系我909244296@qq.com 

参考文献:

Tinyhttpd精读解析 - nengm - 博客园 (cnblogs.com)

Tinyhttpd项目解析_changfei_1995的博客-CSDN博客_tinyhttpd

HTTP服务器的本质:tinyhttpd源码分析及拓展_IT 哈的博客-CSDN博客_http服务器的本质:tinyhttpd

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tinyhttpd项目学习及httpd学习 的相关文章

  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 从 ttyUSB0 写入和读取,无法得到响应

    我对 Linux tty 不太有经验 我的环境是带有丰富 USB 串行的 Raspbian 什么有效 stty F dev ttyUSB0 38400 cu l dev ttyUSB0 s 38400 cu to dev ttyUSB0作品
  • GMail 421 4.7.0 稍后重试,关闭连接

    我试图找出为什么它无法使用 GMail 从我的服务器发送邮件 为此 我使用 SwiftMailer 但我可以将问题包含在以下独立代码中
  • C 语言的符号表

    我目前正在开发一种执行模式匹配的静态分析工具 我在用Flex https github com westes flex生成词法分析器 我编写了代码来管理符号表 我不太有经验C 所以我决定将符号表实现为线性链表 include
  • 尽管 if 语句,Visual Studio 仍尝试包含 Linux 标头

    我正在尝试创建一个强大的头文件 无需更改即可在 Windows 和 Linux 上进行编译 为此 我的包含内容中有一个 if 语句 如下所示 if defined WINDOWS include
  • 使用非规范地址检索内存数据会导致 SIGSEGV 而不是 SIGBUS

    我无法使用以下汇编代码产生 总线错误 这里我使用的内存地址不是合法的 规范地址 那么 我怎样才能触发该错误呢 我在带有 NASM 2 14 02 的 Ubuntu 20 04 LTS 下运行这段代码 但它会导致负载出现 SIGSEGV 分段
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • 如何使用waf构建共享库?

    我想使用构建一个共享库waf http code google com p waf 因为它看起来比 GNU 自动工具更容易 更简洁 到目前为止 我实际上有几个与我开始编写的 wscript 有关的问题 VERSION 0 0 1 APPNA
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac
  • 绕过 dev/urandom|random 进行测试

    我想编写一个功能测试用例 用已知的随机数值来测试程序 我已经在单元测试期间用模拟对其进行了测试 但我也希望用于功能测试 当然不是全部 最简单的方法是什么 dev urandom仅覆盖一个进程 有没有办法做类似的事情chroot对于单个文件并
  • 如何在 Mac OSX Mavericks 中正确运行字符串工具?

    如何在 Mac OSX Mavericks 中正确运行字符串工具 我尝试按照我在网上找到的示例来运行它 strings a UserParser class 但我收到此错误 错误 Applications Xcode app Content
  • 尽管我已在 python ctypes 中设置了信号处理程序,但并未调用它

    我尝试过使用 sigaction 和 ctypes 设置信号处理程序 我知道它可以与python中的信号模块一起使用 但我想尝试学习 当我向该进程发送 SIGTERM 时 但它没有调用我设置的处理程序 只打印 终止 为什么它不调用处理程序
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • Linux下显卡内存使用情况

    Linux下有哪些工具可以监控显卡内存使用情况 NVIDIA 性能套件 http developer nvidia com content nvidia perfkit有Linux版本 可以实时监控各种显卡属性 包括显卡内存使用情况 显然
  • [A-Z] 表示 [A-Za-z] 是怎么回事?

    我已经注意到 至少在我使用的一些基于 Unix 的系统上 ls A Z 已经给了我预期的结果ls A Za z 让我无法轻松获得以大写字母开头的该死的文件列表 我刚刚遇到了同样的事情grep 我无法让它停止与小写字母匹配 A Z 直到我最终
  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • 使用awk将列中的值替换为txt文件中的另一个值

    我是 Linux 和 awk 脚本编写的新手 我有 tab delim txt 文件 如下所示 AAA 134 145 Sat 150 167 AAA 156 167 Sat 150 167 AAA 175 187 Sat 150 167
  • 在 C 中运行 setuid 程序的正确方法

    我有一个权限为4750的进程 我的Linux系统中存在两个用户 root 用户和 appz 用户 该进程继承以 appz 用户身份运行的进程管理器的权限 我有两个基本惯例 void do root void int status statu

随机推荐

  • C# Json的添加方式

    json的几种添加方式 1 只能添加JProperty类的Object 可以一次性添加多个 Add方法添加只能针对于json中的第一层 第二层添加目前还无法得知 但是有其他方法进行弥补 string json ReqCode errcode
  • 【报错】Python:WARNING: You are using pip version 21.1.3; however, version 23.0.1 is available.

    想使用pip安装Scrapy时 报了这个错误 WARNING You are using pip version 21 1 3 however version 23 0 1 is available You should consider
  • 《深入理解Java虚拟机 3》类加载机制与字节码执行引擎

    本系列是用来记录 深入理解Java虚拟机 这本书的读书笔记 方便自己查看 也方便大家查阅 欲速则不达 欲达则欲速 第六章 类文件结构 讲完了自动内存管理 我们来说说执行子系统 执行子系统讲解的是JVM如何执行程序 Class文件概述 这篇我
  • QEMU-KVM基本操作

    本文主要介绍KVM虚拟机的一些基本实践操作 对KVM虚拟机的管理操作主要是基于libvirt的命令行工具virsh进行的 一 安装与启动 1 KVM模块检查 1 查看当前Linux系统核心是否包含KVM模块 Linux内核2 6 20及以上
  • Cookie和Session

    Cookie和Session Cookie是浏览器给HTTP协议提供的一个持久化存储数据的方案 由于当前浏览器站在安全的角度考虑 不敢让页面直接来访问文件系统 Cookie是存储的键值对 不能存复杂的对象 只能存字符串 Cookie是按照域
  • [每周知识碎片] 2

    使用DistributedDataParallel 在Ctrl C 退出后留下许多僵尸进程 kill之后显卡掉了 类似情况1 2 解决方法 使用 ps aux grep python 查看python进程 然后按照顺序执行 kill 9 P
  • pycharm问题一(No module named 'selenium')

    pycharm上搭建python selenium自动化测试环境 背景 小白刚尝试摸门 明明安装了selenium pycharm中还是报No module named selenium 1 首先 竟然报没有selenium 就安装呗 cm
  • 期货开户金融市场非常残酷

    大多数交易者喜欢从零基础开始他们的交易旅程 毕竟 对于它们来说 点击鼠标确认交易真的非常简单 这个数字时代使我们易于交易 交易过程看起来非常简单 但是金融市场却非常残酷 在交易市场当中 所谓的 聪明 根本没有用 市场会一次又一次地给予你教训
  • 计算机vcruntime140.dll丢失的解决方法,重新安装教程

    vcruntime140 dll是Microsoft Visual C Redistributable文件中的一个动态链接库 DLL 这个文件是由Microsoft开发的 用于支持C 编程语言的运行环境 vcruntime140 dll是W
  • Linux的设置地区

    2023年7月21日 周五上午 本来想试试把这篇文章设置成VIP可见的 因为我挺好奇设置了VIP可见后会发生什么 但后来想想觉得这有违自己写博客的初心 于是就放弃了 我写博客的初心就是传递其他人写博客的那种无私的分享精神 为社会 中文社区和
  • python同一文件内class类的调用

    class 类名 def init self self a None self b None def 函数 self x y self x x self y y A 类名 B 类名 A 函数 10 20 print B x B y 将会输出
  • python实现二分查找的四种变体

    本文用python3实现了二分查找的四种变体 一 查找第一个值等于给定值的元素 二 查找最后一个值等于给定值的元素 三 查找第一个大于等于给定值的元素 四 查找最后一个小于等于给定值的元素 python3 一 查找第一个值等于给定值的元素
  • git rebase

    目录 一 开发分支落后于主干分支 个人修复用的分支落后于被修复分支 模拟环境 开始rebase操作 二 本地分支落后于远程分支 多人共用一个分支的情况下其他人有提交 在本地模拟环境 熟悉的可以跳过 比较啰嗦 详细步骤 开始模拟情景 解决 u
  • 【Java学习笔记(一百零七)】之字节码执行引擎,栈帧结构

    本文章由公号 开发小鸽 发布 欢迎关注 老规矩 妹妹镇楼 一 字节码执行引擎 一 概述 物理机和虚拟机都有代码执行能力 物理机的执行引擎建立在处理器 缓存机 指令集和操作系统之上 而虚拟机的执行引擎则是由软件实现的 不会受到物理条件制约地定
  • 稀疏数组(尚硅谷课程的笔记)

    2 稀疏数组 文章来自于听了尚硅谷的课自己所敲 https www bilibili com video BV1E4411H73v p 10 2 1 稀疏数组 我们来看一个实际问题 此时就需要稀疏数组来压缩 稀疏数组基本介绍 当一个数组中大
  • pycharm配置解释器

    因为没有系统学习过pycharm的使用 所以在换了新电脑之后重新配置pycharm一头雾水 查了很多资料 此文用来自己记录 1 解释器选择 virtual Enviroment 第一个是虚拟解释器 我的理解是 直接从pycharm里下载一个
  • 安徽旅游可视化

    安徽旅游可视化 此系统有详细的录屏 下面只是部分截图 需要看完整录屏联系博主 系统开发语言python 框架为django 数据库mysql 分为爬虫和可视化分析
  • 没看错!selenium自动化集成REST api实践!

    01 问题 当我们描述一个 好的自动化测试用例 时 经常出现标准是 精确 自动化测试用例应该测试一件事 只有一件事 与测试用例无关的应用程序的某个部分中的错误不应导致测试用例失败 独立 自动化测试用例不应该受测试套件中任何其他测试用例影响
  • 计算机视觉 相机标定

    目录 一 相机标定原理 1 相机标定简介 2 求解原理 2 1 针孔相机模型 2 2 畸变现象 2 3 像主点偏移 2 4 单应性矩阵H 二 相机标定策略 2 1 相关策略 2 2 棋盘格标定 三 实验内容 1 实验数据 2 实验代码 3
  • Tinyhttpd项目学习及httpd学习

    在参考网络有关Tinyhttpd的内容后 我打算写下自己的学习过程与自己的理解 我会在结尾附上参考的链接 我把代码放在了gitee Tinyhttpd学习 1 Tinyhttpd是一个轻量级的HTTP服务器 2 学习该项目可以学习web服务