新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 利用API在Windows下创建进程和线程[转帖] 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4880 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 利用API在Windows下创建进程和线程[转帖] 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 利用API在Windows下创建进程和线程[转帖]

    前言:

      谈到在Windows创建线程的例子,在网上的很多的参考都是基于MFC的。其实,就操作系统实验这个前提而言,大可不必去碰那个大型的MFC的框架。[1]中提到了在Windows命令控制台下创建进程及线程的方法,本文对[1]中的方法加以了实现,并做些简单的进程及线程的测试[URL=http://dev.yesky.com/]程序[/URL]。

      1、实验准备:

      要实验的Windows下的多线程实验,应做如下准备:

      a) 在新建中选”Win32 Console Application”àAn empty project

      b) 选”工程”à”设置”选项,在”设置”中选择“C/C++”标签,在”Project Option”中,将”MLd”参数改成“MTd”(如图1)。


    按此在新窗口浏览图片
    图1

      以上两步对实验成功至关重要,否则,即是代码无误,在连接时同样会出现问题。

      2、Windows下进程的创建:

      在[1]中,Windows的进程和线程模型被描述成”多进程,基于单进程的多线程”。

      在创建一个线程时,Windows会做大量的工作---创建一个新的地址空间,为进程分配资源以及创建一个基线程。

      CreateProcess函数的原型如下:

    CreateProcess(
     LPCTSTR lpApplicationName,
     //pointer to name of executable moudle
     LPTSTR cmdLine,
     //pointer to command line string
     LPSECURITY_ATTRIBUTES lpProcessAttributes,
     //pointer to process security attributes
     LPSECURITY_ATTRIBUTES lpThreadAttributes,
     //pointer to theread security attributes
     BOOL bInheritHandle ,
     //handle inheritance flag
     DWORD dwCreationFlag,//various creation flags
     LPVOID lpEnviroment,//Enviroment variable
     LPCTSTR lpCurrentDirectory, //Child's current directory
     LPSTARTUPINFO lpStartupInfo, //pointer to StartupInfo
     LPPROCESS_INFORMATION lpProcessInformation
     //pointer to PROCESS_INFORMATION
    )

      虽然有很多参数,不过在现阶段的实验级别,大多数参数只要用默认值即可。

      下面要做的关于Windows使用进程的实验,在Linux系统下,可以使用类似:

      execve(char* cmdName , char* cmdArgu)的语句从一个程序中去执行其它的程序。

      而如果在Windows下,当使用CreateProcess去执行相应的功能时,只要去改变cmdLine中的内容即可,其它的参数使用默认值,具体见代码1:

      代码1执行的功能是从命令行中启动这个名叫的launch的测试程序,在launch后面应加上保存有需要打开程序路径的文件名:

      如在命令行中键入:

    >launch set.txt

      而set.txt中的内容为:

    C:\\WINDOWS\\SYSTEM32\\CALC.EXE
    C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE NEW.TXT
    C:\\WINDOWS\\SYSTEM32\\CHARMAP.EXE

      路径的前半部分为”C:\\WINDOWS\\”,这当然要视你的Windows系统的类型以及系统盘的存放位置而定。如果是NT或2000的机器,则应使用WINNT.

    /*测试程序1:

      示例如何使用进程的launch程序,通过在命令行中加载相应的命令文件,去按照命令文件中指定的程序路径打开相应的程序去执行*/

    #include <windows.h>
    #include <stdio.h>
    #include <string.h>
    #define MAX_LINE_LEN 80

    int main(int argc,char* argv[])
    {
    //local variables
    FILE* fid;
    char cmdLine[MAX_LINE_LEN];
    //CreateProcess parameters
    LPSECURITY_ATTRIBUTES processA=NULL;//Default
    LPSECURITY_ATTRIBUTES threadA=NULL;//Default
    BOOL shareRights=TRUE;//Default
    DWORD creationMask=CREATE_NEW_CONSOLE;//Window per process.
    LPVOID enviroment=NULL;//Default
    LPSTR curDir=NULL;//Default
    STARTUPINFO startInfo;//Result
    PROCESS_INFORMATION procInfo;//Result

    //1.Read the command line parameters

    if(argc!=2)
    {
    fprintf(stderr,"Usage:lanch<launch_set_filename>\n");
    exit(0);
    }

    //2.Open a file that coutain a set of commands

    fid=fopen(argv[1],"r");

    //3.For every command in the launch file

    while(fgets(cmdLine,MAX_LINE_LEN,fid)!=NULL)
    {
     //Read a command from the file
     if(cmdLine[strlen(cmdLine)-1]=='\n')
      cmdLine[strlen(cmdLine)-1]='\0';//Remove NEWLINE
     //Create a new process to execute the command
     ZeroMemory(&startInfo,sizeof(startInfo));
     startInfo.cb=sizeof(startInfo);
     if(!CreateProcess(
       NULL,//File name of executable
       cmdLine,//command line
       processA,//Process inherited security
       threadA, //Thread inherited security
       shareRights,//Rights propagation
       creationMask,//various creation flags
       enviroment,//Enviroment variable
       curDir, //Child's current directory
       &startInfo,
       &procInfo
      )
     )
    {
     fprintf(stderr,"CreatProcess failed on error %d\n",GetLastError());
     ExitProcess(0);
    }
    }
    //Terminate after all commands have finished.
    return 0;
    }

      通过上面这段极其简洁的代码,完成了看似有些难度的任务,让我们充分感受到采用一些高级的编程手段所带来的便捷与高效.


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/12 12:53:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    2、Windows线程的创建及实验:

      2.1 使用CreateThread在Windows下创建线程:

      在Windows中创建线程可以调用两个函数_beginthreadex和CreateThread两个函数,这里只介绍后者。

      CreateThread函数原型:

    HANDLE CreateThread
     (LPSECURITY_ATTRIBUTES lpThreadAttributes,//pointer to thread security attributes
      DWORD dwStackSize,//initial thread stack size, in bytes
      LPSECURITY_START_ROUTINE lpStartAddress,//pointer to thread function
      LPVOID lpParameter,//argument for new thread
      DWORD dwCreationFlags,//creation flags
      LPDWORD lpThreadId //pointer to returned thread identifier
    )

      其中,在本实验阶段比较重要的参数是第三和第四个:

      a)第三个参数是一个指向函数的指针,所以应传入的参数应为函数的地址,如&Func的形式.而这个传入的参数,则必须被声明成为:

    DWORD WINAPI threadFunc(LPVOID threadArgu);

      的形式.这个函数也就是要执行线程任务的那个函数体实体.这里应注意,传入应使用Func而非&Func。

      如:CreateThread(NULL,0,Func,…)

      具体原因:我目前认为是系函数前部使用WINAPI所致。

      b)第四个参数应是执行线程任务的函数体实体所需要的参数,即上面所举例的函数threadFunc的参数threadArgu,这在WINDOWS中被定义成一个LPVOID的类型,目前我认为,可以把它在功能上看成和void* 类似。

      参考:LPVOID的原型:

    typedef void far *LPVOID;

      所以,当你有自己需要的类型的参数传入时,可以用

    typedef struct
    {
    int firstArgu,
    long secArgu,

    }myType,* pMyType;

      将你想要传入的参数装入一个结构体中。

      在传入点,使用类似:

    pMyType pMyTpeyArgu;

    CreateThread(NULL,0,threadFunc,pMyTypeArgu,…);

      在函数threadFunc内部的接收点,可以使用“强行转换”,如:

    int intValue=((pMyType)lpvoid)->firstArgu;
    long longValue=((pMyType)lpvoid)->secArgu;
    … …

      2.2 线程实验1---创建N个随机线程,所有线程的执行时间均为T秒,观察每个线程的运行状况:

      为了使线程的运行趋于随机化,应先使用:

    srand((unsigned int)time (NULL));

      在每个线程的运行中,每个线程的睡眠时间为:

    sleepTime=1000+30*(int)eRandom(50+tNo);

    Sleep(sleepTime);

      这样,可以使进程的运行趋于随机化.

    /*测试程序2:

      创建N个随机线程的随机实验.

      命令行输入参数:

    test threadNo runSecs

    */

    #include <windows.h>
    #include <math.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>

    #define N 5
    #define Type float

    static int runFlag=TRUE;

    DWORD WINAPI threadWork(LPVOID threadNo);
    int parseArgToInt(char* inNumChar);
    Type eRandom(int upLimit);

    typedef struct
    {
     int data;
    }INTEGER;

    void main(int argc,char* argv[])
    {
     unsigned int runTime;
     int i;
     int threadNum;
     //int N;

     SYSTEMTIME now;
     WORD stopTimeMinute,stopTimeSecond;
     DWORD targetThreadID;

     //Get command line argument,N

     if(argc!=3)
     {
      printf("please enter:NThread <ThreadNum> <runTime>\n");
      return;
     }

     threadNum=parseArgToInt(argv[1]);
     runTime=parseArgToInt(argv[2]);
     //Get the time the threads should run,runtime
     //Calculate time to halt
     // runTime=60;/*in seconds.*/

     GetSystemTime(&now);
     printf("mthread:Suite starting aty system time%d,%d,%d\n",now.wHour,now.wMinute,now.wSecond);
     stopTimeSecond=(now.wSecond+(WORD)runTime)%60;

     stopTimeMinute=now.wMinute+(now.wSecond+(WORD)runTime)/60;

     //FOR 1 TO N

     INTEGER* tmpInt;

     for(i=0;i<threadNum;i++)
     {
      //CREATE A NEW THREAD TO EXECUTE SIMULATED WORK;
      //threadWork(i);

      tmpInt=(INTEGER*)malloc(sizeof(INTEGER));
      tmpInt->data=i;
      CreateThread(NULL,0,threadWork,tmpInt,0,&targetThreadID);
      Sleep(100);//Let newly created thread run
     }

     //Cycle while children work...

     while(runFlag)
     {
      GetSystemTime(&now);
      if((now.wMinute>=stopTimeMinute)&&(now.wSecond>=stopTimeSecond))
       runFlag=FALSE;
      Sleep(1000);
     }
     Sleep(5000);
     printf("Program ends successfully\n");
    }

    DWORD WINAPI threadWork(LPVOID threadNo)
    {
     //local variables
     double y;
     const double x=3.14159;
     const double e=2.7183;
     int i;
     const int napTime=1000;//in milliseconds
     const int busyTime=400;
     int tNo=((INTEGER*)threadNo)->data;
     int sleepTime;
     DWORD result=0;

     /*randomasize the random num seeds.*/

     srand((unsigned int)time (NULL));

     while(runFlag)
     {
      //Parameterized processor burst phase
      for(i=0;i<busyTime;i++)
      y=pow(x,e);

      //Parameterized sleep phase

      sleepTime=1000+30*(int)eRandom(50+tNo);
      Sleep(sleepTime);
      printf("I am thread No.%d,sleep sec:%ds\n",tNo,sleepTime);
     }

     //Terminate
     return result;
    }

    int parseArgToInt(char* inNumChar)
    {
     int equipData=0,i=0;
     while(inNumChar[i]>='0'&&inNumChar[i]<='9')
     {
      equipData=10*equipData+(inNumChar[i]-48);
      i++;
     }
     return equipData;
    }

    Type eRandom(int upLimit)
    {
     Type tmpData;
     do
     {
      tmpData=((Type)rand()/(Type)32767)*(Type)100.0*(Type)upLimit;
     }
     while(tmpData>upLimit);
     return tmpData;
    }

      2.3 线程实验2---Windows下可创建的线程的数目的测试:

      这里使用的是让测试线程睡眠100秒,如果用的是让测试进程进入死循环的方法,则会很快让系统僵掉。

    /*测试程序3:

    测试在Windows下最多可创建线程的数目.

    */

    DWORD WINAPI threadWork(LPVOID threadNo)
    {
     DWORD result=0;
     while(runFlag)
     {
      Sleep(100000);
     }
     //Terminate
     return result;
    }

    void main(int argc,char* argv[])
    {
     int count=0;
     DWORD targetThreadID;
     
     while(runFlag)
     {
      if(CreateThread(NULL,0,threadWork,NULL,0,&targetThreadID)==NULL)
      {
       runFlag=false;
       break;
      }
      else
       count++;
      printf("%d ",count);
     }
     Sleep(5000);
     printf("max threads num:%d\n",count);
     printf("Program ends successfully\n");
    }

      在Windows XP下(赛扬800MHZ,256M[URL=http://diy.yesky.com/memoery/]内存[/URL]),在上述方式下,测得可创建的最多的线程数目为2030个.

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/12 12:54:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客3
    发贴心情 
    2.4 线程实验3---最简单的一个临界资源的读者,写者程序.

      这个程序要实现的是最简单的读者,写者程序,读者将1~10十个数字依次填入临界资源区gData,当且仅当gData被读者消费后,写者才可以写入下一个数.

    /*测试程序4:

    最简单的一个临界资源的读者,写者程序.

    */

    DWORD WINAPI threadReader(LPVOID lpvoid)
    {
     int reader_Data;
     int busyTime=10000;
     float tmp;
     int i;

     while(gRunFlag)
     {
      while(stateFlag==WRITER_FLAG)
       for(i=0;i<busyTime;i++)
        tmp=pow(2,10);
        reader_Data=gData;
        printf("reader gets data:%d\n",gData);
        stateFlag=WRITER_FLAG;
     }
     printf("reader ends\n");
     return NULL;
    }

    DWORD WINAPI threadWriter(LPVOID lpvoid)
    {
     int upTime=((INTEGER*)lpvoid)->mInt;
     int busyTime=10000;
     float tmp;
     int i;

     while(gRunFlag)
     {
      while(stateFlag==READER_FLAG)
      for(i=0;i<busyTime;i++)
       tmp=pow(2,10);
      gData++;
      printf("writer gets data:%d\n",gData);

      if(gData==upTime)
       gRunFlag=false;
      stateFlag=READER_FLAG;
     }
     printf("writer ends\n");
     return NULL;
    }

    /*Main 中的调用*/

    ……
    CreateThread(NULL,0,threadReader,tmpInt,0,&targetThreadID);
    CreateThread(NULL,0,threadWriter,tmpInt,0,&targetThreadID);
    ……

      测试结果: (如图2)


    按此在新窗口浏览图片
    图2

      2.5 几点说明:

      2.5.1 主调用程序在结束时使用Sleep(5000)的意图在于:使得由它所产生的子线程可以在主进程结束之前,完成如资源释放一类的工作。

      2.5.2 在随机线程产生测试程序中:

      之所以采用在每个线程内部用

    srand((unsigned int)time (NULL));

      去初始化每个线程,是为了使得所有的线程拥有自己的随机数种子,否则,如果是在主调用程序中去初始化随机数种子数,则所有的线程得到的随机数序列都是一样的,将无法产生随机效果。

      3、应用及推广:

      采用这种在Windows下利用控制台调用Windows API进行操作系统实验的方法,具有代码简洁,观察效果好与实现目标接近等优点。

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/9/12 12:55:00
     
     GoogleAdSense射手座1984-11-30
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/23 4:29:35

    本主题贴数3,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    79.102ms