精华内容
下载资源
问答
  • 但是当我们在Visual Studio创建Win32 GUI程序的时候,WinMain变成程序的入口函数,而该函数并没有"argc" 和"argv"参数,那我们怎样给Windows程序传入命令行参数呢?Windows程序又怎样取得这些传入的参数呢? ...

    这些参数帮助我们为程序传入命令行参数。"argc"为命令行参数的个数,"argv"则为传入参数的数组列表。但是当我们在Visual Studio中创建Win32 GUI程序的时候,WinMain变成程序的入口函数,而该函数并没有"argc" 和"argv"参数,那我们怎样给Windows程序传入命令行参数呢?Windows程序中又怎样取得这些传入的参数呢?

    lpCmdLine 参数

    第一个方案就来自WinMain函数自身。让我们看一个典型的WinMain函数声明:

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)

    如声明所示,WinMain函数有一个类型为"LPSTR(char*)"的参数"lpCmdLine". 这个变量存放着命令行中除程序自身名字外的剩下所有部分。例如,我们有一个名字为"test.exe"的应用程序,当用下面的命令行运行该程序时
    test.exe Some arguments here

    变量lpCmdLine的值即为"Some arguments here"。尽管该方法不像"argc"和"argv"一样非常方便,但它是获取命令行参数的方法之一。我们需要自己写程序去分析lpCmdLine字符串,这增加了程序的复杂度。

    【译注:Windows程序代码同时ANSI版本和UNICODE版本接口。其中WinMain函数为ANSI版本,wWinMain为UNICODE版本。从Visual Studio创建出来的代码主函数命名为_tWinMain。这个函数名会根据当前工程有没有定义_UNICODE宏而在编译时翻译成上面两个不同版本。当翻译成WinMain函数时候,lpCmdLine的类型为LPSTR,而当翻译成wWinMain函数时候,lpCmdLine的类型为 LPWSTR,即宽字符数组】

    GetCommandLine()函数

    另外一个方法就是使用GetCommandLine() API。这个函数返回整个命令行,它把程序自身名称(包括程序的绝对路径)和所有参数放在一个字符串中。该函数非常类似于对lpCmdLine的直接访问。但它的一个好处是能够根据你当前工程的设置自动映射到GetCommandLineA()或者GetCommandLineW()函数。因此解决了访问Unicode命令行输入的问题。但是它还是既没有提供命令行参数数目,也没有类似argv那样把参数自动分割成独立变量的能力。

    CommandLineToArgvW()函数

    最后一个我要讨论的方法是CommandLineToArgvW函数。这个函数只有Unicode宽字符版本,没有对应的CommandLineToArgvA函数。它的声明如下:

    LPWSTR *CommandLineToArgvW(LPCWSTR lpCmdLine, int *pNumArgs)

    该函数和’argc’/'argv’一样简单,但是它并不是在Windows程序中直接访问argc和argv变量。如声明所示,函数接受两个参数,一个是需要解析的Unicode命名行字符串,另外一个是指向整型变量的指针。函数在返回时把参数数目存到这个整型变量中。

    函数返回一个类似于’argv’的字符串数组。让我们看一个例子:

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
        LPWSTR *szArgList;
        int argCount;
    
        szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
        if (szArgList == NULL)
        {
            MessageBox(NULL, L"Unable to parse command line", L"Error", MB_OK);
            return 10;
        }
    
        for(int i = 0; i < argCount; i++)
        {
            MessageBox(NULL, szArgList[i], L"Arglist contents", MB_OK);
        }
    
        LocalFree(szArgList);
        return 0;
    }

    如上所示,通过这个函数,我们可以取得命令行参数的数目(argc)和一个字符串列表(argv)。这里唯一要注意的事情是该函数会给返回参数列表分配一块内存。当我们用完列表后需要手动地释放该内存,否则就会有内存泄露。



    扩展:

    因为Windows没有给出CommandLineToArgvA的实现,所以我们自己实现一个,如下:

    commonLineHelp.cpp

    #ifdef _WIN32
    #include <windows.h>
    #include <string>
    using namespace std;
    
    string wstr_to_str(const wstring  & wstr, UINT CodePage = CP_OEMCP);
    
    string wstr_to_str(const wstring  & wstr, UINT CodePage)
    {
    	string str;
    	int buffer_size = 0;	
    	char* pchar_str = NULL;
    	buffer_size = WideCharToMultiByte(CodePage, NULL,wstr.c_str(), -1, 0, 0, NULL, FALSE);
    	pchar_str = new char[buffer_size + 1];
    	if (pchar_str == NULL)
    	{
    		return str;
    	}
    	memset(pchar_str, 0, buffer_size + 1);
    	if (WideCharToMultiByte(CodePage, NULL, wstr.c_str(), -1, pchar_str, buffer_size, NULL, FALSE))
    	{		
    		str = pchar_str;
    	}
    	delete pchar_str;	
    	return str;
    }
    
    #endif
    
    
    LPSTR *  CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
    {
    	LPWSTR *szArgList;
    	int argCount;
    
    	if ( NULL ==  lpCmdLine)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
    	 
    
    	szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
    	if (szArgList == NULL)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
     
    	char  **szArgListA = NULL;
    
    	szArgListA = (char**)LocalLock(LocalAlloc(LMEM_FIXED, sizeof(char*)*argCount));
    	for(int i = 0; i < argCount; i++)
    	{
    		string str =  wstr_to_str(szArgList[i]);
    		szArgListA[i] = (char*)LocalLock(LocalAlloc(LMEM_FIXED, str.length()));
    		strcpy(szArgListA[i], str.c_str()); 
    	}
    	
    	LocalFree(szArgList);
    
    	*pNumArgs = argCount;
    	
    	return szArgListA;
    }
    

    commonHelp.h

    #ifndef __COMMONLINEHELP__
    
    #define __COMMONLINEHELP__
    
    /**
     * @brief 将应用程序的命令行转化为命令行参数列表(返回字符中是窄字节) 
     *
     * @param[in] lpCmdLine 等转化的命令行字符串  
     * @param[out] pNumArgs 转化后的命令行参数个数      
     *
     * @return  命令行参数列表
     * 
     * @note
     *  返回值在使用完后记得调用LocalFree释放掉
     */
    LPSTR *  CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs);
    
    
    
    #endif

    ==============================================================================================================
    2013-04-17 对commonHelp.cpp文件的改进
    改进原因:
    上面的函数在VS调试时通过VS的调试指定命令行是没有问题的,可是当另一应用程序通过::CreateProcess给此应用程序传递命令行时就会报错了。
    这时因为使用了LocalAlloc,把它改为Alloc后问题就没了。
    但最根本的原因是什么呢?我也没搞清楚,有哪位读者知道答案后请告诉我,谢谢


    改进后的函数:
    LPSTR *  CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
    {
    	LPWSTR *szArgList;
    	int argCount;
    	BOOL bRet = FALSE;
    
    	if ( NULL ==  lpCmdLine)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
    	 
    	szArgList = CommandLineToArgvW(lpCmdLine, &argCount);
    	if (szArgList == NULL)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
     
    	char  **szArgListA = NULL;
    	HLOCAL hLocalList = NULL, hLocalListItem = NULL;
    	
    #if 0
    	hLocalList = LocalAlloc(LMEM_FIXED, sizeof(char*)*argCount);
    	if ( !hLocalList )
    	{
    		goto T_OUT;
    	}
    	
    
    	szArgListA = (char**)LocalLock(hLocalList);
    #endif
    	szArgListA = (char**)malloc(sizeof(char*)*argCount);
    	for(int i = 0; i < argCount; i++)
    	{
    		string str =  wstr_to_str(szArgList[i]);
    #if 0
    		hLocalListItem = LocalAlloc(LMEM_FIXED, str.length());
    		szArgListA[i] = (char*)LocalLock(hLocalListItem);
    #endif
    		szArgListA[i] = (char*)malloc(str.length());
    		strcpy(szArgListA[i], str.c_str());
    		//LocalUnlock(hLocalListItem);
    		
    	}
    	
    	LocalFree(szArgList);
    
    	*pNumArgs = argCount;
    	if ( FALSE == (bRet = LocalUnlock(hLocalList)) )
    	{
    		
    	}
    
    T_OUT:
    
    	return szArgListA;
    }
    

    ==============================================================================================================
    2013-04-18 对commonHelp.cpp文件的改进

    之前版本为每个命令行的字符串申请空间(malloc)时,指定大小是str.length(),这是字符串不包括\0的串长,这样会导致字符串的\0不能复制过来,而导致串没有结束,

    新生的后果非常严重:

    1、打印信息如下:

    我在上一版本中加入打印信息,代码如:

    char**  CommandLineToArgvA(LPCWSTR lpCmdLine, __out int* pNumArgs)
    {
    	LPWSTR *szArgList;
    	int argCount;
    	BOOL bRet = FALSE;
    
    	if ( NULL ==  lpCmdLine)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
    #if 1
    	OutputDebugString("==========Conver String.Original Status\n");
    	OutputDebugStringW(lpCmdLine);
    #endif
    	szArgList = CommandLineToArgvW(lpCmdLine, &argCount);
    	if (szArgList == NULL)
    	{
    		*pNumArgs = 0;
    		return NULL;
    	}
     
    	char  **szArgListA = NULL;
    	HLOCAL hLocalList = NULL, hLocalListItem = NULL;
    	
    	szArgListA = (char**)malloc(sizeof(char*)*argCount);
    	memset(szArgListA, 0x0, sizeof(char*)*argCount);
    	OutputDebugString("==========Conver String ...\n");
    	for(int i = 0; i < argCount; i++)
    	{
    		string str =  wstr_to_str(szArgList[i]);
    
    		szArgListA[i] = (char*)malloc(str.length());
    		memset(szArgListA[i], 0x0, str.length());
    		strncpy(szArgListA[i], str.c_str(), str.length());
    		szArgListA[i][str.length()] = '\0';
    #if 1
    		
    		OutputDebugString(str.c_str());
    		OutputDebugString(szArgListA[i]);
    		//L_TRACE("len=%d\n", strlen(szArgListA[i]));
    #endif
    		//LocalUnlock(hLocalListItem);
    		
    	}
    	
    #if 1
    	OutputDebugString("==========Convert String result:\n");
    	for (int i = 0; i < argCount; i++)
    	{
    		OutputDebugString(szArgListA[i]);
    	}
    #endif  // 0
    
    
    	LocalFree(szArgList);
    
    	*pNumArgs = argCount;
    
    
    	if ( FALSE == (bRet = LocalUnlock(hLocalList)) )
    	{
    		
    	}
    
    T_OUT:
    
    	return szArgListA;
    }
    




    后得到的打印如下:

    00001088	2.38102412	[1180] ==========Conver String.Original Status	
    00001089	2.38104796	[1180] "D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe" -g 1080*768 -u xred -p xred 192.168.4.151	
    00001090	2.38108373	[1180] ==========Conver String ...	
    00001091	2.38111877	[1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe	
    00001092	2.38114262	[1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe	
    00001093	2.38116908	[1180] -g	
    00001094	2.38119221	[1180] -g	
    00001095	2.38121915	[1180] 1080*768	
    00001096	2.38124251	[1180] 1080*768	
    00001097	2.38126826	[1180] -u	
    00001098	2.38129187	[1180] -u	
    00001099	2.38131785	[1180] xred	
    00001100	2.38134098	[1180] xred	
    00001101	2.38136673	[1180] -p	
    00001102	2.38138986	[1180] -p	
    00001103	2.38141584	[1180] xred	
    00001104	2.38143897	[1180] xred	
    00001105	2.38146520	[1180] 192.168.4.151	
    00001106	2.38148880	[1180] 192.168.4.151	
    00001107	2.38151193	[1180] ==========Convert String result:	
    00001108	2.38153529	[1180] D:\Company_Centerm\linux\modules\xred\thunk\base\client\src\WindowsCode\Release\XredClient.exe	
    00001109	2.38155818	[1180] -g	
    00001110	2.38158154	[1180] 1080*768]	
    00001111	2.38160467	[1180] -u	
    00001112	2.38162780	[1180] xred	
    00001113	2.38165069	[1180] -p	
    00001114	2.38167381	[1180] xred	
    00001115	2.38169694	[1180] 192.168.4.151	

    从打印信息看,在转化时串都是正确的;可是都转化完后再打印一次,发现有一个字符串多了个  ‘]’   。


    这样导致了我们解析字符中出错而已。


    2、除1、中所讲的危害外,还会引起整个进程的意常发生,时不时会提示“地址XXXX不能被读”。

    (1)在CreateThread时不进入线程回调函数了,且弹出系统访问内存错误码的提示。

    (2)



    修改方法:

    只修改一行代码:

    szArgListA[i] = (char*)malloc(str.length());

    改为:

    szArgListA[i] = (char*)malloc(str.length()+1);
    原因:自然不用说了,很明显。!!!!
    至于为什么会影响到其他内存的空间时,可能是因为我们申请的空间过小,导致此串没有结束,而我们可能会对串进行操作,如进行字符成员访问,

    因为没有结束符,所以可能访问到不是我们申请的空间,这时没有报错。而这块空间可能后来被别人申请了,这样就会导致别人的内存被我们修改了。






    展开全文
  • wget命令使用及参数详解

    千次阅读 2019-08-13 11:14:13
    命令参数:启动参数:记录和输入文件参数:下载参数:目录参数:HTTP 选项参数:FTP 选项参数:递归下载参数:4.使用实例:实例1:使用wget下载单个文件实例2:使用wget -O下载并以不同的文件名保存实例3:使用wget...


    Linux系统中的wget是一个下载文件的工具,它用在命令行下。对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器。wget支持HTTP,HTTPS和FTP协议,可以使用HTTP代理。所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后退出系统,wget将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与,这省去了极大的麻烦。

    wget 可以跟踪HTML页面上的链接依次下载来创建远程服务器的本地版本,完全重建原始站点的目录结构。这又常被称作”递归下载”。在递归下载的时候,wget 遵循Robot Exclusion标准(/robots.txt). wget可以在下载的同时,将链接转换成指向本地文件,以方便离线浏览。

    wget 非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性.如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。

    1.命令格式:

    wget [参数] [URL地址]

    2.命令功能:

    用于从网络上下载资源,没有指定目录,下载资源回默认为当前目录。wget虽然功能强大,但是使用起来还是比较简单:

    • 支持断点下传功能:这一点,也是网络蚂蚁和FlashGet当年最大的卖点,现在wget也可以使用此功能,那些网络不是太好的用户可以放心了;
    • 同时支持FTP和HTTP下载方式:尽管现在大部分软件可以使用HTTP方式下载,但是有些时候,仍然需要使用FTP方式下载软件;
    • 支持代理服务器:对安全强度很高的系统而言,一般不会将自己的系统直接暴露在互联网上,所以支持代理是下载软件必须有的功能;
    • 设置方便简单:可能习惯图形界面的用户已经不是太习惯命令行了,但是命令行在设置上其实有更多的优点,鼠标可以少点很多次,也不要担心是否错点鼠标;
    • 程序小,完全免费:程序小可以考虑不计,因为现在的硬盘实在太大了;完全免费就不得不考虑了,即使网络上有很多所谓的免费软件,但是这些软件的广告却不是我们喜欢的。

    3.命令参数

    启动参数

    短格式长格式说明
    -V–version显示wget的版本后退出
    -h–help打印语法帮助
    -b–background启动后转入后台执行
    -e–execute=COMMAND执行`.wgetrc’格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc

    记录和输入文件参数

    短格式长格式说明
    -o–output-file=FILE把记录写到FILE文件中
    -a–append-output=FILE把记录追加到FILE文件中
    -d–debug打印调试输出
    -q–quiet安静模式(没有输出)
    -v–verbose冗长模式(这是缺省设置)
    -nv–non-verbose关掉冗长模式,但不是安静模式
    -i–input-file=FILE下载在FILE文件中出现的URLs
    -F–force-html把输入文件当作HTML格式文件对待
    -B–base=URL将URL作为在-F -i参数指定的文件中出现的相对链接的前缀
    –sslcertfile=FILE可选客户端证书
    –sslcertkey=KEYFILE可选客户端证书的KEYFILE
    –egd-file=FILE指定EGD socket的文件名

    下载参数

    短格式长格式说明
    –bind-address=ADDRESS指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)
    -t–tries=NUMBER设定最大尝试链接次数(0 表示无限制).
    -O–output-document=FILE把文档写到FILE文件中
    -nc–no-clobber不要覆盖存在的文件或使用.#前缀
    -c–continue接着下载没下载完的文件
    –progress=TYPE设定进程条标记
    -N–timestamping不要重新下载文件除非比本地文件新
    -S–server-response打印服务器的回应
    –spider不下载任何东西
    -T–timeout=SECONDS设定响应超时的秒数
    -w–wait=SECONDS两次尝试之间间隔SECONDS秒
    –waitretry=SECONDS在重新链接之间等待1…SECONDS秒
    –random-wait在下载之间等待0…2*WAIT秒
    -Y–proxy=on/off打开或关闭代理
    -Q–quota=NUMBER设置下载的容量限制
    –limit-rate=RATE限定下载速率

    目录参数

    短格式长格式说明
    -nd–no-directories不创建目录
    -x–force-directories强制创建目录
    -nH–no-host-directories不创建主机目录
    -P–directory-prefix=PREFIX将文件保存到目录 PREFIX/…
    –cut-dirs=NUMBER忽略NUMBER层远程目录

    HTTP 选项参数

    短格式长格式说明
    –http-user=USER设定HTTP用户名为 USER.
    –http-passwd=PASS设定http密码为 PASS
    -C–cache=on/off允许/不允许服务器端的数据缓存 (一般情况下允许)
    -E–html-extension将所有text/html文档以.html扩展名保存
    –ignore-length忽略 `Content-Length’头域
    –header=STRING在headers中插入字符串 STRING
    –proxy-user=USER设定代理的用户名为 USER
    –proxy-passwd=PASS设定代理的密码为 PASS
    –referer=URL在HTTP请求中包含 `Referer: URL’头
    -s–save-headers保存HTTP头到文件
    -U–user-agent=AGENT设定代理的名称为 AGENT而不是 Wget/VERSION
    –no-http-keep-alive关闭 HTTP活动链接 (永远链接)
    –cookies=off不使用 cookies
    –load-cookies=FILE在开始会话前从文件 FILE中加载cookie
    –save-cookies=FILE在会话结束后将 cookies保存到 FILE文件中

    FTP 选项参数

    短格式长格式说明
    -nr–dont-remove-listing不移走 .listing文件
    -g–glob=on/off打开或关闭文件名的globbing机制
    –passive-ftp使用被动传输模式 (缺省值).
    –active-ftp使用主动传输模式
    –retr-symlinks在递归的时候,将链接指向文件(而不是目录)

    递归下载参数

    短格式长格式说明
    -r–recursive递归下载--慎用!
    -l–level=NUMBER最大递归深度 (inf 或 0 代表无穷)
    –delete-after在现在完毕后局部删除文件
    -k–convert-links转换非相对链接为相对链接
    -K–backup-converted在转换文件X之前,将之备份为 X.orig
    -m–mirror等价于 -r -N -l inf -nr
    -p–page-requisites下载显示HTML文件的所有图片

    递归下载中的包含和不包含(accept/reject):

    短格式长格式说明
    -A–accept=LIST分号分隔的被接受扩展名的列表
    -R–reject=LIST分号分隔的不被接受的扩展名的列表
    -D–domains=LIST分号分隔的被接受域的列表
    –exclude-domains=LIST分号分隔的不被接受的域的列表
    –follow-ftp跟踪HTML文档中的FTP链接
    –follow-tags=LIST分号分隔的被跟踪的HTML标签的列表
    -G–ignore-tags=LIST分号分隔的被忽略的HTML标签的列表
    -H–span-hosts当递归时转到外部主机
    -L–relative仅仅跟踪相对链接
    -I–include-directories=LIST允许目录的列表
    -X–exclude-directories=LIST不被包含目录的列表
    -np–no-parent不要追溯到父目录
    wget -S –spider url  ## 不下载只显示过程
    

    4.使用实例

    实例1:使用wget下载单个文件

    以下的例子是从网络下载一个文件并保存在当前目录,在下载的过程中会显示进度条,包含下载完成百分比,已经下载的字节,当前下载速度和剩余下载时间:

    wget http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    

    实例2:使用wget -O下载并以不同的文件名保存

    wget默认会以最后一个符合”/”的后面的字符来命名,对于动态链接的下载通常文件名会不正确
    下面的例子会下载一个文件并以名称download.aspx?id=1080保存,即使下载的文件是zip格式,它仍然以download.php?id=1080命令:

    wget http://www.minjieren.com/download?id=1
    

    为了解决这个问题,我们可以使用参数-O来指定一个文件名:

    wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080
    

    实例3:使用wget –limit -rate限速下载

    当你执行wget的时候,它默认会占用全部可能的宽带下载。但是当你准备下载一个大文件,而你还需要下载其它文件时就有必要限速了:

    wget --limit-rate=300k http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    

    实例4:使用wget -c断点续传

    使用wget -c重新启动下载中断的文件,对于我们下载大文件时突然由于网络等原因中断非常有帮助,我们可以继续接着下载而不是重新下载一个文件。需要继续中断的下载时可以使用-c参数

    wget -c http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    

    实例5:使用wget -b后台下载

    对于下载非常大的文件的时候,我们可以使用参数-b进行后台下载

    wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    
    wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    Continuing in background, pid 1840.
    Output will be written to `wget-log'.
    

    你可以使用以下命令来察看下载进度:

    tail -f wget-log
    

    实例6:伪装代理名称下载

    有些网站能通过根据判断代理名称不是浏览器而拒绝你的下载请求。不过你可以通过–user-agent参数伪装。

    wget --user-agent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16" http://www.minjieren.com/wordpress-3.1-zh_CN.zip
    

    实例7:使用wget –spider测试下载链接

    当你打算进行定时下载,你应该在预定时间测试下载链接是否有效。我们可以增加–spider参数进行检查。

    wget --spider URL
    

    如果下载链接正确,将会显示:

    wget --spider URL
    
    Spider mode enabled. Check if remote file exists.
    HTTP request sent, awaiting response... 200 OK
    Length: unspecified [text/html]
    Remote file exists and could contain further links,
    but recursion is disabled -- not retrieving.
    

    这保证了下载能在预定的时间进行,但当你给错了一个链接,将会显示如下错误:

    wget --spider url
    
    Spider mode enabled. Check if remote file exists.
    HTTP request sent, awaiting response... 404 Not Found
    Remote file does not exist -- broken link!!!
    

    你可以在以下几种情况下使用spider参数:

    • 定时下载之前进行检查
    • 间隔检测网站是否可用
    • 检查网站页面的死链接

    实例8:使用wget –tries增加重试次数

    如果网络有问题或下载一个大文件也有可能失败。wget默认重试20次连接下载文件。如果需要,你可以使用–tries增加重试次数:

    wget --tries=40 URL
    

    实例9:使用wget -i下载多个文件

    首先,保存一份下载链接文件:

    cat > filelist.txt
    url1
    url2
    url3
    url4
    

    接着使用这个文件和参数-i下载:

    wget -i filelist.txt
    

    实例10:使用wget –mirror镜像网站

    下载整个网站到本地:

    wget --mirror -p --convert-links -P ./LOCAL URL
    

    说明:

    –miror:开户镜像下载

    -p:下载所有为了html页面显示正常的文件

    –convert-links:下载后,转换成本地的链接

    -P ./LOCAL:保存所有文件和目录到本地指定目录

    实例11:使用wget –reject过滤指定格式下载

    下载一个网站,但你不希望下载图片,可以使用以下命令:

    wget --reject=gif ur
    

    实例12:使用wget -o把下载信息存入日志文件

    不希望下载信息直接显示在终端而是在一个日志文件,可以使用:

    wget -o download.log URL
    

    实例13:使用wget -Q限制总下载文件大小

    命令:

    wget -Q5m -i filelist.txt
    

    说明:

    当你想要下载的文件超过5M而退出下载,你可以使用。注意:这个参数对单个文件下载不起作用,只能递归下载时才有效。

    实例14:使用wget -r -A下载指定格式文件

    Example:

    wget -r -A.pdf url
    

    可以在以下情况使用该功能:

    • 下载一个网站的所有图片
    • 下载一个网站的所有视频
    • 下载一个网站的所有PDF文件

    实例15:使用wget FTP下载

    使用wget匿名ftp下载:

    wget ftp-url
    

    使用wget用户名和密码认证的ftp下载

    wget --ftp-user=USERNAME --ftp-password=PASSWORD url
    

    编译安装

    使用如下命令编译安装:

    # tar zxvf wget-1.9.1.tar.gz 
    # cd wget-1.9.1 
    # ./configure 
    # make 
    # make install 
    
    展开全文
  • linuxtouch命令参数不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件,以下是linuxtouch命令参数的使用方法: touch [-acm][-r ref_file(参照文件)|-t time(时间值)] file...

    linux中touch命令参数不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件,以下是linux中touch命令参数的使用方法:

     

      touch [-acm][-r ref_file(参照文件)|-t time(时间值)] file(文件名)

     

      example:

      touch file1.txt 更新file1.txt的存取和修改时间

      touch -c file1.txt 如果file1.txt不存在,不创建文件

      touch -r ref_file file1.txt 更新file1.txt的时间戳和ref+file相同

      touch -t 0811142234.50 file1.txt 设定文件的时间错为08年11月14日22点34分40秒

     

      -a或--time=atime或--time=access或--time=use  只更改存取时间。

      -c或--no-create  不建立任何文档。

      -d  使用指定的日期时间,而非现在的时间。

      -f  此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题。

      -m或--time=mtime或--time=modify  只更改变动时间。

      -r  把指定文档或目录的日期时间,统统设成和参考文档或目录的日期时间相同。

     

      -t time

      使用指定的时间值 time 作为指定文件 file 相应时间戳记的新值.此处的 time 规定为如下形式的十进制数∶

      [[CC]YY]MMDDhhmm[.SS]

     

      这里,CC为年数中的前两位,即”世纪数”;YY为年数的后两位,即某世纪中的年数.如果不给出CC的值,则linux中touch命令参数将把年数CCYY限定在1969--2068之内.MM为月数,DD为天将把年数CCYY限定在1969--2068之内.MM为月数,DD为天数,hh 为小时数(几点),mm为分钟数,SS为秒数.此处秒的设定范围是0--61,这样可以处理闰秒.这些数字组成的时间是环境变量TZ指定的时区中的一个时间.由于系统的限制,早于1970年1月1日的时间是错误的.

    展开全文
  • ioctl命令参数传递

    万次阅读 2013-11-05 11:42:17
    虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件...

    一、ioctl的简介:

    虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动,想要一个弹出光驱的操作,这种操作并不是所有的字符设备都需要的,所以文件操作结构体也不会有对应的函数操作。


    出于这样的原因,ioctl就有它的用处了————一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。


    来个图来说一下应用层与驱动函数的ioctl之间的联系:

    上面的图可以看出,fd通过内核后找到对应的inodefile结构体指针并传给驱动函数,而另外两个参数却没有修改(类型改了没什么关系)


    简单介绍一下函数:

    int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);

    参数:

    1)inodefileioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改

    文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。

    2)cmd:命令,接下来要长篇大论地说。

    3)arg:参数,接下来也要长篇大论。

    返回值:

    1)如果传入的非法命令,ioctl返回错误号-EINVAL

    2)内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会傻乎乎的认为这是正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误号了。

    Ioctl里面多个不同的命令,那就要看它函数的实现来决定返回值了。打个比方,如果ioctl里面有一个类似read的函数,那返回值也就可以像read一样返回。

    当然,不返回也是可以的。


    二、ioctlcmd


    说白了,cmd就是一个数,如果应用层传来的数值在驱动中有对应的操作,这样就就可以了。


    来个最简单的ioctl实现:3rd_char_4/1st


    1)要先定义个命令,就用一个简单的0,来个命令的头文件,驱动和应用函数都要包含这个头文件

    /*test_cmd.h*/

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_CLEAR 0

    5

    6 #endif /*_TEST_CMD_H*/

    2)驱动实现ioctl

    命令TEST_CLEAR的操作就是清空驱动中的kbuf

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 switch(cmd){

    128 case TEST_CLEAR:

    129 memset(dev->kbuf, 0, DEV_SIZE);

    130 dev->cur_size = 0;

    131 filp->f_pos = 0;

    132 ret = 0;

    133 break;

    134 default: /*命令错误时的处理*/

    135 P_DEBUG("error cmd!\n");

    136 ret = - EINVAL;

    137 break;

    138 }

    139

    140 return ret;

    141 }


    3)再来个应用程序:

    1 #include <stdio.h>

    2 #include <sys/types.h>

    3 #include <sys/stat.h>

    4 #include <fcntl.h>

    5 #include <sys/ioctl.h>

    6 #include "test_cmd.h"

    7

    8 int main(void)

    9 {

    10 char buf[20];

    11 int fd;

    12 int ret;

    13

    14 fd = open("/dev/test", O_RDWR);

    15 if(fd < 0)

    16 {

    17 perror("open");

    18 return -1;

    19 }

    20

    21 write(fd, "xiao bai", 10); //1先写入

    22

    23 ioctl(fd, TEST_CLEAR); //2再清空

    24

    25 ret = read(fd, buf, 10); //3再验证

    26 if(ret < 0)

    27 {

    28 perror("read");

    29 }

    30

    31 close(fd);

    32 return 0;

    33 }

    注:这里为了read返回出错,我修改了驱动的readwrite函数的开始时的第一个

    判断,一看就知道了。


    4)验证一下:

    [root: 1st]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 1st]# mknod /dev/test c 253 0

    [root: 1st]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    read: No such device or address //哈哈!出错了!因为没数据读取。



    按照上面的方法来定义一个命令是完全可以的,但内核开发人员发现这样有点不对劲。

    如果有两个不同的设备,但它们的ioctlcmd却一样的,哪天有谁不小心打开错了,并且调用ioctl,这样就完蛋了。因为这个文件里面同样有cmd对应实现。

    为了防止这样的事情发生,内核对cmd又有了新的定义,规定了cmd都应该不一样。


    三、ioctl中的cmd


    一个cmd被分为了4个段,每一段都有各自的意义,cmd的定义在<linux/ioctl.h>。注:但实际上<linux/ioctl.h>中只是包含了<asm/ioctl.h>,这说明了这是跟平台相关的,ARM的定义在<arch/arm/include/asm/ioctl.h>,但这文件也是包含别的文件<asm-generic/ioctl.h>,千找万找,终于找到了。


    <asm-generic/ioctl.h>中,cmd拆分如下:

    解释一下四部分,全部都在<asm-generic/ioctl.h>ioctl-number.txt这两个文档有说明。

    1)幻数:说得再好听的名字也只不过是个0~0xff的数,占8bit(_IOC_TYPEBITS)。这个数是用来区分不同的驱动的,像设备号申请的时候一样,内核有一个文档给出一些推荐的或者已经被使用的幻数。

    /*Documentation/ioctl/ioctl-number.txt*/

    164 'w' all CERN SCI driver

    165 'y' 00-1F packet based user level communications

    166 <mailto:zapman@interlan.net>

    167 'z' 00-3F CAN bus card

    168 <mailto:hdstich@connectu.ulm.circular.de>

    169 'z' 40-7F CAN bus card

    170 <mailto:oe@port.de>

    可以看到'x'是还没有人用的,我就拿这个当幻数!


    2)序数:用这个数来给自己的命令编号,占8bit(_IOC_NRBITS),我的程序从1开始排序。


    3)数据传输方向:占2bit(_IOC_DIRBITS)。如果涉及到要传参,内核要求描述一下传输的方向,传输的方向是以应用层的角度来描述的。

    1)_IOC_NONE:值为0,无数据传输。

    2)_IOC_READ:值为1,从设备驱动读取数据。

    3)_IOC_WRITE:值为2,往设备驱动写入数据。

    4)_IOC_READ|_IOC_WRITE:双向数据传输。


    4)数据大小:与体系结构相关ARM下占14bit(_IOC_SIZEBITS),如果数据是int,内核给这个赋的值就是sizeof(int)


    强调一下,内核是要求按这样的方法把cmd分类,当然你也可以不这样干,这只是为了迎合内核的要求,让自己的程序看上去很正宗。上面我的程序没按要求照样运行。


    既然内核这样定义cmd,就肯定有方法让用户方便定义:

    _IO(type,nr) //没有参数的命令

    _IOR(type,nr,size) //该命令是从驱动读取数据

    _IOW(type,nr,size) //该命令是从驱动写入数据

    _IOWR(type,nr,size) //双向数据传输

    上面的命令已经定义了方向,我们要传的是幻数(type)、序号(nr)和大小(size)。在这里szie的参数只需要填参数的类型,如int,上面的命令就会帮你检测类型的正确然后赋值sizeof(int)


    有生成cmd的命令就必有拆分cmd的命令:

    _IOC_DIR(cmd) //从命令中提取方向

    _IOC_TYPE(cmd) //从命令中提取幻数

    _IOC_NR(cmd) //从命令中提取序数

    _IOC_SIZE(cmd) //从命令中提取数据大小


    越讲就越复杂了,既然讲到这,随便就讲一下预定义命令。

    预定义命令是由内核来识别并且实现相应的操作,换句话说,一旦你使用了这些命令,你压根也不要指望你的驱动程序能够收到,因为内核拿掉就把它处理掉了。


    分为三类:

    1)可用于任何文件的命令

    2)只用于普通文件的命令

    3)特定文件系统类型的命令


    其实上面的我三类我也没搞懂,反正我自己随便编了几个数当命令都没出错,如果真的怕出错,那就不要用别人已经使用的幻数就行了。


    讲了这么多,终于要上程序了,修改一下上一个程序,让它看起来比较有内涵。

    /3rd_char/3rd_char_4/2nd

    1)先改一下命令:

    /*test_cmd.h*/

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_MAGIC 'x' //定义幻数

    5 #define TEST_MAX_NR 1 //定义命令的最大序数,只有一个命令当然是1

    6

    7 #define TEST_CLEAR _IO(TEST_MAGIC, 0)

    8

    9 #endif /*_TEST_CMD_H*/


    2)既然这么辛苦改了cmd,在驱动函数当然要做一些参数检验:

    /*test.c*/

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130

    131 switch(cmd){

    132 case TEST_CLEAR:

    133 memset(dev->kbuf, 0, DEV_SIZE);

    134 dev->cur_size = 0;

    135 filp->f_pos = 0;

    136 ret = 0;

    137 break;

    138 default: /*命令错误时的处理*/

    139 P_DEBUG("error cmd!\n");

    140 ret = - EINVAL;

    141 break;

    142 }

    143

    144 return ret;

    145 }

    每个参数的传入都会先检验一下幻数还有序数是否正确。


    3)应用程序的验证

    结果跟上一个完全一样,因为命令的操作没有修改

    [root: 2nd]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 2nd]# mknod /dev/test c 253 0

    [root: 2nd]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    read: No such device or address


    五、ioctl中的arg之整数传参。



    上面讲的例子都没有使用ioctl的传参。这里先要说一下ioctl传参的方式。


    应用层的ioctl的第三个参数是"...",这个跟printf"..."可不一样,printf中是意味这你可以传任意个数的参数,而ioctl最多也只能传一个,"..."的意思是让内核不要检查这个参数的类型。也就是说,从用户层可以传入任何参数,只要你传入的个数是1.


    一般会有两种的传参方法:

    1)整数,那可是省力又省心,直接使用就可以了。

    2)指针,通过指针的就传什么类型都可以了,当然用起来就比较烦。


    先说简单的,使用整数作为参数:

    例子,实现个命令,通过传入参数更改偏移量,虽然llseek已经实现,这里只是想验证一下正数传参的方法。


    1)先加个命令:

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 #define TEST_MAGIC 'x' //定义幻数

    5 #define TEST_MAX_NR 2 //定义命令的最大序数

    6

    7 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    8 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    9

    10 #endif /*_TEST_CMD_H*/

    这里有人会问了,明明你是要传入参数,为什么不用_IOW而用_IO定义命令呢?

    原因有二:

    1)因为定义数据的传输方向是为了好让驱动的函数验证数据的安全性,而一般指针才需要检验安全性,因为有人会恶意传参(回想一下copy_to_user)

    2)个人喜好,方便我写程序介绍另一种传参方法,说白了命令也只是一个数,只要不要跟预定义命令冲突就可以了。


    2)更新test_ioctl

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130

    131 switch(cmd){

    132 case TEST_CLEAR:

    133 memset(dev->kbuf, 0, DEV_SIZE);

    134 dev->cur_size = 0;

    135 filp->f_pos = 0;

    136 ret = 0;

    137 break;

    138 case TEST_OFFSET: //根据传入的参数更改偏移量

    139 filp->f_pos += (int)arg;

    140 P_DEBUG("change offset!\n");

    141 ret = 0;

    142 break;

    143 default: /*命令错误时的处理*/

    144 P_DEBUG("error cmd!\n");

    145 ret = - EINVAL;

    146 break;

    147 }

    148

    149 return ret;

    150 }

    TSET_OFFSET命令就是根据传参更改偏移量,不过这里要注意一个问题,那就是参数的类型,驱动函数必须要知道从应用传来的参数是什么类型,不然就没法使用。在这个函数里,从应用层传来的参数是int,因此在驱动中也得用int


    3)再改一下应用程序:

    1 #include <stdio.h>

    2 #include <sys/types.h>

    3 #include <sys/stat.h>

    4 #include <fcntl.h>

    5 #include <sys/ioctl.h>

    6

    7 #include "test_cmd.h"

    8

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 fd = open("/dev/test", O_RDWR);

    16 if(fd < 0)

    17 {

    18 perror("open");

    19 return -1;

    20 }

    21

    22 write(fd, "xiao bai", 10); //先写入

    23

    24 ioctl(fd, TEST_OFFSET, -10); //再改偏移量

    25

    26 ret = read(fd, buf, 10); //再读数据

    27 printf("<app> buf is [%s]\n", buf);

    28 if(ret < 0)

    29 {

    30 perror("read");

    31 }

    32

    33 close(fd);

    34 return 0;

    35 }


    4)验证一下

    [root: 3rd]# insmod test.ko

    major[253] minor[0]

    hello kernel

    [root: 3rd]# mknod /dev/test c 253 0

    [root: 3rd]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_ioctl]change offset! //更改偏移量

    <kernel>[test_read]read 10 bytes, cur_size:[0] //没错误,成功读取!

    <app> buf is [xiao bai]


    上面的传参很简单把,接下来说一下以指针传参。

    考虑到参数不可能永远只是一个正数这么简单,如果要传多一点的东西,譬如是结构体,那就得用上指针了。


    六、ioctl中的arg之指针传参。


    一讲到从应用程序传来的指针,就得想起我邪恶的传入了非法指针的例子。所以,驱动程序中任何与应用层打交道的指针,都得先检验指针的安全性。


    说到这检验又有两种方法:

    1)用的时候才检验。

    2)一进来ioctl就检验。


    先说用的时候检验,说白了就是用copy_xx_user系列函数,下面实现一下:

    1)先定义个命令

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 struct ioctl_data{

    5 unsigned int size;

    6 char buf[100];

    7 };

    8

    9 #define DEV_SIZE 100

    10

    11 #define TEST_MAGIC 'x' //定义幻数

    12 #define TEST_MAX_NR 3 //定义命令的最大序数

    13

    14 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    15 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    16 #define TEST_KBUF _IO(TEST_MAGIC, 3)

    17

    18 #endif /*_TEST_CMD_H*/

    这里有定义多了一个函数,虽然这个命令是涉及到了指针的传参,但我还是_IOW,还是那一句,现在还不需要用上。

    该命令的操作是传进一个结构体指针,驱动根据结构体的内容修改kbufcur_size和偏移量。


    2)来个实现函数:

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126 struct ioctl_data val;

    127

    128 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    129 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    130 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    131

    132 switch(cmd){

    133 case TEST_CLEAR:

    134 memset(dev->kbuf, 0, DEV_SIZE);

    135 dev->cur_size = 0;

    136 filp->f_pos = 0;

    137 ret = 0;

    138 break;

    139 case TEST_OFFSET: //根据传入的参数更改偏移量

    140 filp->f_pos += (int)arg;

    141 P_DEBUG("change offset!\n");

    142 ret = 0;

    143 break;

    144 case TEST_KBUF: //修改kbuf

    145 if(copy_from_user(&val, (struct ioctl_data *)arg, sizeof(struct ioctl_data))){

    146 ret = - EFAULT;

    147 goto RET;

    148 }

    149 memset(dev->kbuf, 0, DEV_SIZE);

    150 memcpy(dev->kbuf, val.buf, val.size);

    151 dev->cur_size = val.size;

    152 filp->f_pos = 0;

    153 ret = 0;

    154 break;

    155 default: /*命令错误时的处理*/

    156 P_DEBUG("error cmd!\n");

    157 ret = - EINVAL;

    158 break;

    159 }

    160

    161 RET:

    162 return ret;

    163 }

    145行,因为指针是从用户程序传来,所以必须检查安全性。


    3)来个应用程序

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 struct ioctl_data my_data= {

    16 .size = 10,

    17 .buf = "123456789"

    18 };

    19

    20 fd = open("/dev/test", O_RDWR);

    21 if(fd < 0)

    22 {

    23 perror("open");

    24 return -1;

    25 }

    26

    27 write(fd, "xiao bai", 10);

    28

    29 ioctl(fd, TEST_KBUF, &my_data);

    30

    31 ret = read(fd, buf, 10);

    32 printf("<app> buf is [%s]\n", buf);

    33 if(ret < 0)

    34 {

    35 perror("read");

    36 }

    37

    38 close(fd);

    39 return 0;

    40 }


    4)再来验证一下:

    [root: 4th]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_read]read 10 bytes, cur_size:[0]

    <app> buf is [123456789] //成功!

    注:类似copy_xx_user的函数含有put_userget_user等,我就不细说了。

    下面说第二种方法:进入ioctl后使用access_ok检测。

    声明一下:下面的验证方法是不正确的。如果不想看下去的话,今天的内容已经讲完了。


    先说一下access_ok的使用

    access_ok(type, addr, size)

    使用:检测地址的安全性

    参数:

    type:用于指定数据传输的方向,VERIFY_READ表示要读取应用层数据,VERIFT_WRITE表示要往应用层写如数据。注意:这里和IOR IOW的方向相反。如果既读取又写入,那就使用VERIFY_WRITE

    addr:用户空间的地址

    size:数据的大小

    返回值:

    成功返回1,失败返回0


    既然知道怎么用,就直接来程序了:

    1)定义命令

    1 #ifndef _TEST_CMD_H

    2 #define _TEST_CMD_H

    3

    4 struct ioctl_data{

    5 unsigned int size;

    6 char buf[100];

    7 };

    8

    9 #define DEV_SIZE 100

    10

    11 #define TEST_MAGIC 'x' //定义幻数

    12 #define TEST_MAX_NR 3 //定义命令的最大序数

    13

    14 #define TEST_CLEAR _IO(TEST_MAGIC, 1)

    15 #define TEST_OFFSET _IO(TEST_MAGIC, 2)

    16 #define TEST_KBUF _IOW(TEST_MAGIC, 3, struct ioctl_data)

    17

    18 #endif /*_TEST_CMD_H*/

    这里终于要用_IOW了!


    2)实现ioctl

    122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)

    123 {

    124 int ret = 0;

    125 struct _test_t *dev = filp->private_data;

    126

    127 /*既然这么费劲定义了命令,当然要检验命令是否有效*/

    128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;

    129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;

    130 /*根据提取命令指定的方向判断指针的安全性*/

    131 if(_IOC_DIR(cmd) & _IOC_READ)

    132 ret = access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

    133 else if(_IOC_DIR(cmd) & _IOC_WRITE)

    134 ret = access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

    135 if(!ret) return - EFAULT;

    136

    137 switch(cmd){

    138 case TEST_CLEAR:

    139 memset(dev->kbuf, 0, DEV_SIZE);

    140 dev->cur_size = 0;

    141 filp->f_pos = 0;

    142 ret = 0;

    143 break;

    144 case TEST_OFFSET: //根据传入的参数更改偏移量

    145 filp->f_pos += (int)arg;

    146 P_DEBUG("change offset!\n");

    147 ret = 0;

    148 break;

    149 case TEST_KBUF: //修改kbuf

    150 memset(dev->kbuf, 0, DEV_SIZE);

    151 memcpy(dev->kbuf, ((struct ioctl_data *)arg)->buf,

    152 ((struct ioctl_data *)arg)->size);

    153 dev->cur_size = ((struct ioctl_data *)arg)->size;

    154 filp->f_pos = 0;

    155 ret = 0;

    156 break;

    157 default: /*命令错误时的处理*/

    158 P_DEBUG("error cmd!\n");

    159 ret = - EINVAL;

    160 break;

    161 }

    162

    163 return ret;

    164 }

    上面并没有用copy_to_user,而是通过access_ok来检测。


    3)再来个应用程序:

    9 int main(void)

    10 {

    11 char buf[20];

    12 int fd;

    13 int ret;

    14

    15 struct ioctl_data my_data= {

    16 .size = 10,

    17 .buf = "123456789"

    18 };

    19

    20 fd = open("/dev/test", O_RDWR);

    21 if(fd < 0)

    22 {

    23 perror("open");

    24 return -1;

    25 }

    26

    27 write(fd, "xiao bai", 10);

    28

    29 ret = ioctl(fd, TEST_KBUF, &my_data);

    30 if(ret < 0)

    31 {

    32 perror("ioctl");

    33 }

    34

    35 ret = read(fd, buf, 10);

    36 printf("<app> buf is [%s]\n", buf);

    37 if(ret < 0)

    38 {

    39 perror("read");

    40 }

    41

    42 close(fd);

    43 return 0;

    44 }


    4)验证一下:效果和上一个一样

    [root: 5th]# ./app

    <kernel>[test_write]write 10 bytes, cur_size:[10]

    <kernel>[test_write]kbuf is [xiao bai]

    <kernel>[test_read]read 10 bytes, cur_size:[0]

    <app> buf is [123456789]


    下面就要如正题了,这个驱动是有问题的,那就是验证安全性完全不起作用!当我传入非法指针时,驱动同样会输出,不信可以自己传个邪恶地址(void *)0进去试一下。


    修改应用程序一样代码:

    29 ret = ioctl(fd, TEST_KBUF, &my_data);


    上面是我做的错误实现,我本来想验证,只要经过access_ok检验,数据就会安全,没想到经过access_ok检验之后照样会出错。

    但是,copy_to_user同样是先调用access_ok再调用memcpy,它却没出错。这个我事情我现在都没搞明白,如果谁知道了麻烦指点一下。


    我查了设备驱动第三版,在144页有这样的说法:

    1.access_ok并没有做完的所有的内存检查,

    2.大多数的驱动代码都不是用access_ok的,后面的内存管理会讲述。


    在这里书本上有这样的约定:(都是我自己的理解)

    1.传入指针需要检查安全性。memcpy函数尽量不要在内核中使用。

    2.copy_to_user.copy_from_user.get_user.put_user函数会再拷贝数据前检测指针的安全性。不需要access_ok

    3.如果在ioctl函数开头使用了accsee_ok检验数据,接下来的代码可以使用__put_user__get_user这些不需要检测的函数(书上有例子)


    虽然还有写东西还没搞懂,但个人觉得,如果使用个access_ok要这么麻烦的话,那我就不用好了,以后我就使用copy_xx_user函数,省力又省心。




    七、总结:


    这次讲了ioctl的实现:

    1)命令是怎么定义。

    2)参数怎么传递。

    展开全文
  • sqoop 导入导出数据命令参数详解

    千次阅读 2018-08-18 16:25:01
    sqoop 导入导出数据命令参数详解 1. 从关系数据库导入到hdfs sqoop import \ --connect&lt;jdbc-uri&gt; 指的是连接地址,这里面是mysql服务器的地址; --username&lt;username&gt; ...
  • 对于ps命令有一定的介绍,为了加强自己的理解,所以我在自己的虚拟机上进行了实验,发现输出的和书上讲的不一样,可能是我理解的了?以下就展开对此的讨论。 在讨论之前,我们先了解以下ps命令的用法。 一...
  • 自己整理的Linux常用命令参数含义

    千次阅读 2017-05-06 09:12:41
    Linux常用命令参数(RHEL6.5)  最常用命令:  cp:复制文件;  参数:  -R或-r:对目录进行复制操作,以递归的方式,将指定目录及其子目录的所有文件复制到指定的目录;若给出的源文件是一个目录,此时...
  • SQL Server BCP命令参数详解

    千次阅读 2016-12-04 23:56:43
    相信大家对SQL Server的bcp命令都非常熟悉了,那么bcp命令参数如何解析呢,下面我们逐步解析。 1、bcp命令详解 bcp 实用工具在 Microsoft? SQL Server? 2000 实例和数据文件之间以用户指定的格式...
  • 目录argparse介绍argparse使用——代码示例1、创建一个解析器——创建 ArgumentParser() 对象描述description2、添加参数——调用 add_...argparse 模块是 Python 内置的一个用于命令项选项与参数解析的模块,argpa
  • Sqoop导入导出数据命令参数详解

    千次阅读 2019-07-01 13:46:22
    sqoop 导入导出数据命令参数详解 1. 从关系数据库导入到hdfs sqoop import \ --connect<jdbc-uri> 指的是连接地址,这里面是mysql服务器的地址; --username<username> 数据...
  • getopt命令并不是bash...getopts 是 shell 内建命令, getopt 是一个独立外部工具getopts 使用语法简单,getopt 使用语法复杂getopts 不支持长参数(长选项,如 --option), getopt 支持getopts 不会重排所有参数的顺
  • 我们在使用git进行版本管理的时候,如果遇到需要回退代码的情况,一般会用 git reset 命令,不过这个命令还有几个参数,这篇文章就来详解一下。 先来了解一下 git 大致的工作流程,配合
  • 什么用?原理?优点缺点? 容器化技术简单来说只不过是利用内核的六种名称空间技术来实现的程序运行环境的隔离 镜像含有启动容器所需要的文件系统及其内容,因此用于创建并启动容器,采用分成构建机制 整个...
  • sed命令参数之-r -i

    万次阅读 2019-02-25 15:49:00
    对于初学linux的朋友来说,能记住命令附带的一大帮参数就以及非常不容易了。好不容易把该用的参数都想全了。sed -irns 后面一大片脚本 ,一执行出错了 what!!!! 创建一下测试环境 head /etc/passwd > 1 cat 1...
  • linux export命令参数及用法详解--linux设置环境变量命令 功能说明:设置或显示环境变量。 语 法:export [-fnp][变量名称]=[变量设置值] 补充说明:在shell执行程序时,shell会提供一组环境变量。...
  • fsck命令参数及用法

    千次阅读 2013-11-15 18:46:23
    在Linux系统,为了增加系统性能,通常系统默认一些数据写在内存,并不会直接将数据写入硬盘,这是因为内存速度要比硬盘快若干倍。但是个问题, 万一由于“断电”或者其他未知原因,造成系统死机,怎么办?...
  • 做数据库的常识,不要拼接SQL语句,应使用命令参数更安全,不需要转义。 import sqlite //打开数据库连接 var sqlConnection = sqlite("/testParameters.db") //创建表 if( not sqlConnection.existsTable(...
  •  很多人运行完批处理命令发现dos窗口不能自动关闭,那是因为你在bat文件直接执行了需要运行的某些外部程序,如果希望dos窗口自动关闭,那么,你可以使用start命令来执行该外部程序,而不是直接写上一行来执行。...
  • linux more命令参数及用法详解

    千次阅读 2011-10-26 10:17:55
     说明:类似 cat ,不过会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会往回(back)一页显示,而且还有搜寻字串的功能(与 vi 相似),使用
  • 一、常用命令ls 显示文件或目录 -l 列出文件详细信息l(list) -a 列出当前目录下所有文件及目录,包括隐藏的a(all) ll 会列出该文件下的所有文件信息,包括隐藏的文件的文件详细信息,使用ls -l -a也可以达到...
  • sqlplussql命令时不能回撤,不能修改,只能回车重新写,非常麻烦。 看到 一位朋友写了,我也记录一下。 1.安装rlwrap软件 安装rlwrap之前需要readline。 readline可以直接在安装光盘找到 ,也可以自己上传...
  • 都知道删除用户用userdel -r USERNAME,-r代表把用户相对应的目录一并删除,那么,假如疏忽了忘记加参数,会什么结果,今天我尝试了一下。#首先创建用户user2,删除后再重新添加:[root@localhost~]#useradduser2 ...
  • Chkdsk—磁盘查修复命令

    千次阅读 2013-02-21 15:48:58
    如果不带任何参数, chkdsk 将显示当前驱动器的磁盘状态。 语法 chkdsk [volume:][[Path] FileName] [/f] [/v] [/r] [/x] [/i] [/c] [/l[:size]] 参数 volume: 指定驱动器号
  • docker 常用命令总结

    万次阅读 多人点赞 2019-01-27 18:38:01
    注意 stop,restart后的容器,或者commit后的镜像启动环境变量需要重新加载,比如 source /etc/source 我们运行docker容器的时候,使用了-d参数,把容器在后台...上述命令执行某些命令回报Failed to get D-Bus co...
  • latex 中文书籍常见命令

    千次阅读 2016-12-09 17:06:02
    包含 否则会报 % e.g 航姿参考系统包括基于~MEMS~的三轴陀螺仪 其实直接空格就行 -_- 代码高亮 \usepackage {xcolor} % 用于Laex和pdfLatex的独立驱动颜色扩展 \usepackage {listings...
  • shutdown 命令

    千次阅读 2018-11-15 19:13:18
    命令简介: 该命令可以安全关闭或者重新启动系统。你没有看,shutdown命令不仅可以关闭系统、也可以重启Linux系统。 命令语法: /sbin/shutdown [-t sec] [-arkhncfFHP] time ...命令参数: 参数 ...
  • Linux最危险的10个命令

    千次阅读 2013-12-09 09:46:44
    Linux最危险的10个命令 对于编程人员或或者Linux系统管理员来说,操作Linux系统最常见的方法就是使用命令行。当然,Linux命令行佷有用、很高效,但时候也很危险,尤其是在你误操作或者不确定你自己在正在做什么...
  • Linuxdd命令详解

    万次阅读 多人点赞 2016-11-23 16:56:17
    一、dd命令dd:用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。注意:指定数字的地方若以下列字符结尾,则乘以相应的数字:b=512;c=1;k=1024;w=2参数注释:if=文件名:输入文件名,缺省为标准输入。...
  • 常用Linux日志查看命令

    万次阅读 多人点赞 2017-06-04 15:26:07
    常用Linux日志查看命令 欢迎关注公众号【程猿薇茑】 查看Web应用程序的运行日志解决运行时异常、出错是一项基本功。在Web程序我们通过日志系统(比如log4j)向外部文件(比如log.txt)写入了程序运行时的某些...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 183,758
精华内容 73,503
关键字:

命令中的参数有错