精华内容
下载资源
问答
  • 创建进程的API封装成一个进程类,用该类生成一个对象,则创建了一个进程;posix_spawn()来创建一个进程;

    将创建进程的API封装成一个进程类,用该类生成一个对象,则创建了一个进程;

    主要API函数为:

     #include <spawn.h>
    int posix_spawn(pid_t *restrict pid, const char *restrict path,
                  const posix_spawn_file_actions_t *file_actions,
                  const posix_spawnattr_t *restrict attrp,
                  char *const argv[restrict], char *const envp[restrict]);
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);

           #include <spawn.h>
    
           int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *
                  file_actions);
           int posix_spawn_file_actions_init(posix_spawn_file_actions_t *
                  file_actions);

           #include <spawn.h>
    
           int posix_spawnattr_destroy(posix_spawnattr_t *attr);
           int posix_spawnattr_init(posix_spawnattr_t *attr);

    MyProcess.h

    /*************************************************************************
    	> File Name: MyProcess.h
    	> Author: 
    	> Mail: 
    	> Created Time: 2015年12月14日 星期一 20时13分03秒
     ************************************************************************/
    
    #ifndef _MYPROCESS_H
    #define _MYPROCESS_H
    #include <iostream>
    #include <spawn.h>
    #include <string>
    #include <sys/wait.h>
    
    using std::string;
    
    class MyProcess{
        public:
            MyProcess(string path, char **av, char **ep);
            MyProcess(string path, char **av, char **ep, posix_spawn_file_actions_t psfa, posix_spawnattr_t attr);
            void run();
            void pwait(int *x);
        private:
            pid_t pid;
            posix_spawn_file_actions_t file_actions;
            posix_spawnattr_t attrp;
            char **argv;
            char **envp;
            string ProgramPath;
    };
    #endif
    

    MyProcess.cpp

    /*************************************************************************
    	> File Name: MyProcess.cpp
    	> Author: 
    	> Mail: 
    	> Created Time: 2015年12月14日 星期一 20时19分58秒
     ************************************************************************/
    
    #include "MyProcess.h"
    using namespace std;
    
    MyProcess::MyProcess(string path, char **av, char **ep)
    {
        ProgramPath = path;
        argv = av;
        envp = ep;
        posix_spawn_file_actions_init(&file_actions);
        posix_spawnattr_init(&attrp);
    }
    
    MyProcess::MyProcess(string path, char **av, char **ep, posix_spawn_file_actions_t psfa, posix_spawnattr_t attr)
    {
        ProgramPath = path;
        argv = av;
        envp = ep;
        file_actions = psfa;
        attrp = attr;
    
        posix_spawn_file_actions_init(&file_actions);
        posix_spawnattr_init(&attrp);
    }
    
    void MyProcess::run()
    {
        posix_spawn(&pid, ProgramPath.c_str(), &file_actions, &attrp, argv, envp);
    }
    
    void MyProcess::pwait(int *x)
    {
        wait(x);
    }
    

    main.cpp

    /*************************************************************************
    	> File Name: main.cpp
    	> Author: 
    	> Mail: 
    	> Created Time: 2015年12月14日 星期一 20时04分46秒
     ************************************************************************/
    
    #include "MyProcess.h"
    #include <cstdlib>
    using namespace std;
    
    #define PROCESSNUM 2
    
    int main(int argc, char *argv[])
    {
        int wait_val[2];
    
        if (argc < 2){
            cout << "the argc is less than 3" << endl;
            exit(-1);
        }
    
        MyProcess child1("ls", argv, NULL);
        MyProcess child2("/bin/ps", argv, NULL);
        
        child1.run();
        child2.run();
        
        child1.pwait(&wait_val[0]);
        child1.pwait(&wait_val[1]);
       
        cout << "child1.pwait is " << wait_val[0] << endl;
        cout << "child2.pwait is " << wait_val[1] << endl;
    
        exit(0);
    }
    


    下图才是程序的运行结果:(将下面的运行结果与上图比较,证明程序正确)


    展开全文
  • 通过分析实验现象,深入理解进程进程在调度执行和内存空间等方面的特点,掌握在POSIX规范中fork和kill系统调用的功能和使用。 实验前准备 科普:POSIX是Portable Operating System Interface...

    今天第一次真正意义上进行操作系统的实验,比起以前C语言,Java的实验,操作系统实验显得更有乐趣,也更有挑战性,因而整理为一篇博客,进一步的巩固知识。

    目的

    通过分析实验现象,深入理解进程及进程在调度执行和内存空间等方面的特点,掌握在POSIX规范中fork和kill系统调用的功能和使用。

    实验前准备

    科普:POSIX是Portable Operating System Interface for UNIX的首字母缩写词,是一套IEEE和ISO标准。这个标准定义了应用程序和操作系统之间的一个接口。只要保证他们的程序设计的符合POSIX标准,开发人员就能确信他们的程序可以和支持POSIX的操作系统互联。这样的操作系统包括大部分版本的UNIX。

    1、fork()函数:pid_t fork(void);

    返回值:fork仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值
                (1)在父进程中,fork返回新创建子进程的进程ID;
                (2)在子进程中,fork返回0;
                (3)如果出现错误,fork返回一个负值;
                在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

     2、kill()函数:int kill(pid_t pid, int sig);

    函数参数:①pid:指定进程的进程ID,注意用户的权限,比如普通用户不可以杀死1号进程(init)。

                    pid>0:发送信号给指定进程

                    pid=0:发送信号给与调用kill函数进程属于同一进程组的所有进程

                    pid<0:发送信号给pid绝对值对应的进程组

                    pid=-1:发送给进程有权限发送的系统中的所有进程

                     ②信号量:本实验用SIGTERAM。程序结束(terminate)信号,和SIGKILL不同的是该信号可以被阻塞和处理,通常用来要求程序自己退出。如果终止不了,我们才会尝试SIGKILL。

    实验程序:(已在Ubuntu 14.04下完美运行)

    #include <stdio.h> 
    #include <sys/types.h>
    #include <unistd.h> 
    #include <signal.h>
    #include <ctype.h> 
    
    /* 允许建立的子进程个数最大值 */
    #define MAX_CHILD_NUMBER 10 
    /* 子进程睡眠时间 */
    #define SLEEP_INTERVAL 2 
    int proc_number = 0; /* 子进程的自编号id,从0开始 */
    
    void do_something(); 
    
    void do_something() { 
    	for(;;) { 
    		/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
    		* 比如号码是1,就打印" 1" */
    		printf("This is process No.%*d\n", proc_number+3, proc_number); 
    		sleep(SLEEP_INTERVAL); /* 主动阻塞两秒钟 */
    	} 
    }
    
    int main(int argc, char* argv[]) { 
    	int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
    	int i, ch; 
    	pid_t child_pid; 
    	pid_t pid[10]={0}; /* 存放每个子进程的id */ 
    
    	if (argc > 1) { 
    		/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */ 
    		child_proc_number = atoi(argv[1]); 
    		child_proc_number = (child_proc_number > 10) ? 10 : child_proc_number;
    	} 
    	for (i = 0; i < child_proc_number; i++) { 
    		/* 在这里填写代码,建立child_proc_number个子进程
    		* 子进程要执行
    		* proc_number = i; 
    		* do_something();
    		* 父进程把子进程的id保存到pid[i] !!!!!!!!!!!!!*/
    		child_pid = fork();	
    		if (child_pid > 0) {
    			pid[i] = child_pid;
    		} else if (child_pid == 0) {
    			proc_number = i;
    			do_something();
    		} else {
    			printf("Fail to fork!\n");
    		}
    	} 
    
    	/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
    	* 输入q退出 */
    	while ((ch = getchar()) != 'q') { 
    		if (isdigit(ch))  { 
    		/* 在这里填写代码,向pid[ch-'0']发信号SIGTERM, 
    		* 杀死该子进程 */ 
    			kill(pid[ch-'0'], SIGTERM);
    		}
    	} 
    
    	/* 在这里填写代码,杀死本组的所有进程 */ 
    	kill(0, SIGTERM);
    
    	return 0;
    	} 
    
    /*
    函数原型:fork()函数:pid_t fork(void);
    返回值:fork仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值
    	(1)在父进程中,fork返回新创建子进程的进程ID;
    	(2)在子进程中,fork返回0;
    	(3)如果出现错误,fork返回一个负值;
    在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。
    在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的
    进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
    
    函数原型:int kill(pid_t pid, int sig);
    	函数参数
            ①pid:指定进程的进程ID,注意用户的权限,比如普通用户不可以杀死1号进程(init)。
    
                    pid>0:发送信号给指定进程
    
                    pid=0:发送信号给与调用kill函数进程属于同一进程组的所有进程
    
                    pid<0:发送信号给pid绝对值对应的进程组
    
                    pid=-1:发送给进程有权限发送的系统中的所有进程
    */

    实验过程:

    先猜想一下这个程序的运行结果。假如运行“./process 20”,输出会是什么样?然后按照注释里的要求把代码补充完整,运行程序。可以多运行一会儿,并在此期间启动、关闭一些其它进程,看process 的输出结果有什么特点,记录下这个结果。
    开另一个终端窗口,运行“ps aux|grep process”命令,看看process 究竟启动了多少个进程。回到程序执行窗口,按“数字键+回车”尝试杀掉一两个进程,再到另一个窗口看进程状况。按q 退出程序再看进程情况。

    十个进程在运行:

    杀死id为2的子进程,用“ps aux|grep process”命令查看:

    经过自己动手实验,真的发现很有意思,但里面也存在很多很多的坑,还需自己慢慢摸索。 要是觉得输出结果还是不清晰,可以在多输出getpid()。来查看进程的识别码。

    实验问题:

    1、你最初认为运行结果会怎么样?

    会创建10个进程,进程号为0~9。若输入数字、回车会杀死指定进程。若输入q、回车,会杀死所有进程。

    2、实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。

    程序最多会产生十个进程,否则根据命令行第二个参数来看。每隔SLEEP_INTERVAL秒刷新一次输出在命令行界面。直到输入数字+回车,将会杀死指定编号的进程,或直到输入q、回车,会杀死本组所有进程。

    特点:每次都输出一定数目的进程,但是,发现每次输出的进程的次序不一样,随机输出。进程的pid识别码是递增的。每次调用fork(),都会生成一个父进程,一个子进程。把生成父进程返回的子进程pid保存到pid[i]中;把i直接赋给子进程的自编号proc_number,然后调用死循环函数do_something()进行输出。进程是循环创建的,所以进程自编号proc_number是随着i由小变大的,pid也是依次递增的

    3、proc_number这个全局变量在各个子进程里的值相同吗?为什么?

    不相同,因为全局变量是共享资源,它记录的是子进程的序号

    4、kill命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?

    两次。kill(pid[ch=’0’], SIGTERM);kill(0,SIGTERM);第一个是杀死进程号pid[ch-‘0’],执行后输出的结果中不会再有该进程号。第二次是杀死本组所有进程。即主进程以及它所创建的所有子进程,执行后程序退出结束。

    5、使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?

    进程在主函数中return,或调用exit()都可以主动退出。使用kill()则是强制性的异常退出。 主动退出更好,若在子进程退出前使用kill()杀死其父进程,则系统会让init进程(一号进程)接管子进程,该子进程就会成为孤儿进程。当使用kill()杀死子进程使得子进程先于父进程退出时,父进程又没有调用wait()函数等待子进程结束,子进程处于僵死状态,即僵尸进程。且一直会保持下去,直到系统重启。子进程处于僵死状态时内核只保存该进程的必要信息以备父进程所需,此时子进程始终占着资源,这也就减少了系统可以创建的最大进程数。

    在运行 kill 命令发送信号后,目标进程会异常退出。这也是系统管理员终结某个进程的最常用方法,类似于在 Windows 平台通过任务管理器杀死某个进程。

    展开全文
  • 3 POSIX 多任务及同步机制-实验1: fork创建进程 一.实验目的 ·掌握fork函数及其特点。 ·通过实验深入理解操作系统的进程概念,Linux的进程概念。 二.实验背景 ·Fork创建子进程中的问题 ·进程的地址空间:...

    3 POSIX 多任务及同步机制-实验1: fork创建进程

    一.实验目的

    ·掌握fork函数及其特点。
    ·通过实验深入理解操作系统的进程概念,Linux的进程概念。

    二.实验背景

    ·Fork创建子进程中的问题
    ·进程的地址空间:教材P.187-188 newproc_posix.c代码
    ·父子进程的并发执行:教材P.188 newproc_posix.c代码执行结果以及两个问题
    ·Fork/exec流程:新进程覆盖子进程 (如果exec 函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp() 后面的代码也就不会执行了)
    ·孤儿进程和僵尸进程的形成原因

    三.关键代码及分析

    #include <stdio.h>
    #include <stdlib.h> //for malloc
    #include <string.h> //for mesmset,strcpy
    #include <unistd.h>
    #include <sys/types.h>
    
    int main()
    {
            pid_t pid;
            pid_t pid2;
            int var=88;
            char  *str = (char*)malloc(sizeof(char)*10);
            memset(str, 0x00, 10);
    
            /* fork a child process */
            pid = fork();
    
            if (pid < 0) { /* error occurred */
                    fprintf(stderr, "Fork Failed\n");
                    return 1;
            }
            else if (pid == 0) { /* child process */
                    printf("I am the child %d\n",pid); //行A
                    pid2 = getpid();
                    printf("I am the child %d\n",pid2);
                    strcpy(str, "child");
                    var++;
                    //sleep(50);
                    }
    
            else { /* parent process */
                    pid2 = getpid();
                    printf("I am the parent %d and creae the child %d\n",pid2,pid); //行B
                    strcpy(str, "parent");
                    //sleep(50); //为了cat /proc/{pid}/maps
                    wait(NULL);/* parent will wait for the child to complete */
                    printf("Child Complete\n");
            }
        printf("str=%s, strAdd=%p, var=%d, varAdd=%p\n", str, str, var, &var);//行C
        return 0;
    }
    

    fork函数的返回值是一个pid_t 类型的数值。pid_t 是一个<sys/types.h>定义的宏,其实就是int。
    在这里插入图片描述
    当前进程调用fork函数会使操作系统创建一个新的进程(子进程),并且在任务表中为其建立一个新的task_struct 表项。新进程和原有进程的可执行程序是同一个程序。新进程的上下文和数据绝大部分是原进程(父进程)的副本。但是二者的进程标识符PID不同,它们是两个相互独立的进程!此时程序计数器PC在父、子进程的上下文中是相同的,都指向下一条要执行的语句。
    fork函数的返回值在父子进程中是不同的。如果fork创建子进程成功,fork函数返回的值在父进程中是子进程的PID,而在子进程中返回的则是0:如果fok创建子进程失败,则返回负值。fork 失败的原因可能是系统中的进程数达到上限,或若系统中内存空间不够。所以fork函数具有一个特点:次调用 两次返回。一次调用是在父进程中fork函数被调用,返回则是分别在父、子进程中各自返回一次。

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main()
    {
    pid_t pid;
    pid_t pid2;
    char buf[128];
    
            /* fork a child process */
            pid = fork();
    
            if (pid < 0) { /* error occurred */
                    fprintf(stderr, "Fork Failed\n");
                    return 1;
            }
            else if (pid == 0) { /* child process */
                    printf("I am the child %d\n",pid);
                    pid2 = getpid();
                    printf("I am the child %d\n",pid2);
                    sprintf(buf, "/proc/%d/maps",pid2);
                    //execlp("/bin/cat","cat", buf,NULL); //行A
                    printf("I am Child %d\n",getpid());     //行B
            }
            else { /* parent process */
                    /* parent will wait for the child to complete */
                    printf("I am the parent %d and creae the child %d\n",getpid(),pid);
                    sleep(40);  // 为了运行cat /proc/父进程pid/maps
                    wait(NULL);
                    printf("Child Complete\n");
            }
    
        return 0;
    }
    
    int execlp(const char * file, const char * arg, ..., (char *)0);
    

    该函数会从系统环境变量PATH所指的目录中查找符合参数file的文件名,找到后就行执行该程序,然后将第二个以后的参数作为该程序的argv[0]、argv[1]、…参数,最后一个参数必须用空指针(NULL)表示结束。如果用常数0来表示一个空指针,则必须将它强制转能为一个字符指针。如果exec 函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp() 后面的代码也就不会执行了。

    · pid_t wait(int * status);
    调用wait函数会暂时停止当前进程的执行,直到有信号到来或子进程结束。如果在调用wait()时子进程已经结束,则wait函数会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,面子进程的进程识别码也会一起返回。 如果不在意结束状态值,则参数status可以设成NULL.

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    main()
    {
            int a;
            int pid;
            printf("AAAAAAAA");//print 1;
            pid=fork();
            if(pid==0){
                    printf("\n child\n");
            }
            else if(pid>0){
                    printf("\n parent\n");
            }
            printf("BBBBBBB\n");//print 2; 父子进程都会打印;
    }
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    int main()
    {
            pid_t pid;
            if((pid=fork())==-1)
                    perror("fork");
            else if(pid==0)
            {
                    printf("child : pid=%d,ppid=%d\n",getpid(),getppid());//输出子进程pid以及其父进程pid,此时父进程未退出
                    sleep(2);//休眠两秒等待父进程先退出
                    printf("child : pid=%d,ppid=%d\n",getpid(),getppid());//请注意此次输出子进程的ppid,与上一次的差异,此时父进程已退出
            }
            else
            {
                    printf("father : pid=%d,ppid=%d\n",getpid(),getppid());
                    sleep(1);
                    exit(0);//休眠一秒后直接退出,未使用wait调用。此时子进程相当于被“无情的抛弃了”
            }
    }
    

    当一个子进程结束运行时,子进程的退出状态(返回值)会报告给操作系统,系统则以特中的信号将子进程结束的事件通知父进程,此时子进程的进程控制块task_struct 仍驻留在内存中。一般来说,收到子进程结束的通知后,父进程会使用wait系统调用获取子进程的提出状态,然后内核就可以从任务表中删除已结束的子进程的task_struct结构体;如果父并程没有这么做的话子进程的task_struct就会一 直庄留在系统中,成为僵尸进程。 孤儿进程则是指父进程结束后仍在运行的进程,在Linux系统中,孤儿进程一般会被init进程“收养”,成为init的子进程。

    四.实验结果与分析

    gcc -o newproc-posix newproc-posix.c //编译源代码生成可执行文件

    在这里插入图片描述

    ./newproc-posix //运行可执行程序

    在这里插入图片描述

    fork函数创建子进程成功后,父进程与子进程的执行顺序是无法确定的。假设父进程先运行,根据程序计数器父进程将运行到if语句。在父进程中fork 返回的是子进程的PID,也就是说,此时程序中的pid变量大于0,因此pid<0和pid == 0的两个if分支都不会运行,父进程只运行pid>0对应的f分支语句。子进程在之后的某个时候得到调度,它的task_ struct 信息被读出,进程执行上下文被换人,根据程序计数器子进程同样运行到if语句。在子进程中fork调用返回值为0,也就是说,此时程序中的pid变量等于0,所以子进程只运行pid==0的话分支语句。此时运行的不是父进程了,虽然是同一个程序,但是这是同一个程序的另外次执行。 在操作系统中这次执行由另外一个进程使用另一个task strucet 表示的,从执行的角度说子进程和父进程是相互独立的。
    ·去掉sleep(50)的注释
    在这里插入图片描述
    gcc -o newproc-posix2 newproc-posix2.c // 编译源代码生成可执行文件
    在这里插入图片描述

    ./newproc-posix2 //执行可执行文件

    在这里插入图片描述

    ·去掉execlp()前面的注释

    在这里插入图片描述

    当执行到到execlp("/bin/cat",“cat”, buf,NULL)时,该命令指定运行的程序是cat,参数为/proc/{pid}/maps。结果显示当调用了execlp()函数后子进程的执行程序不再是newproc-posix2,而是/bin/cat。说明如果execlp() 函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp() 后面的代码也就不会执行了。

    gcc -o fork_io fork_io.c //编译源代码生成可执行文件

    在这里插入图片描述

    ./fork_io // 执行可执行文件

    在这里插入图片描述

    理论由于A 在fork函数之前,B 在fork函数之后。在执行fork函数时,行A已经被执行了,父、子进程的程序计数器都应该指向了fork 语句后的下一句。故此A输出一次,B在父、子进程各输出一次。但实际都输出了两次,原因在于用户空间和内核空间的数据传输机制上。当用户程序执行到行A时,一方面printf语句并不是自己完成打印的,而需要调用Linux提供的系统调用完成输出,在这个过程中数据会写人到进程在内核的缓冲区中,另一方面 printf没有要求立即完成输出.所以系统等待合适的时机再把内核缓冲区中的数据输出。因此当fork函数被调用时,系统会复制父进程的代码段、数据段等给子进程,所以相关数据也被一起复制,这样就导致字符串 AAAAAAA被输出了两次,如果让行A只执行一次,可以改写该句为:
    printf(“AAAAAAAA\n”);

    系统看到 \n则会立即刷新缓冲区,完成输出工作。在这种情况下,执行fork 函数时,父进程内核缓冲区中就不存在该数据了,子进程也就不会再打印一次了。
    在这里插入图片描述
    在这里插入图片描述

    #gcc -o orphan_process orphan_process.c //编译源代码生成可执行文件
    在这里插入图片描述

    ./orphan_process //运行可执行文件

    在这里插入图片描述

    fork函数调用成功后,父进程先执行,pid大于0,执行printf(“father : pid=%d,ppid=%d\n”,getpid(),getppid()); , 由于后面父进程进入休眠,那么父进程进入等待状态,一秒后进入就绪状态,在这个过程中子进程执行,子进程中pid=0,执行 printf(“child : pid=%d,ppid=%d\n”,getpid(),getppid()),可以看出此时的父进程还没退出,子进程的ppid=11295为父进程的pid号;子进程执行sleep(2)休眠2s进入等待状态,2s后进入就绪状态,在等待的时候父进程执行exit(0),父进程退出,由于子进程还没退出,父进程退出了,那么子进程被init进程“收养”,成为init的子进程,当子进程再次执行是,答打印的ppid=1,为init进程的pid号,此时的子进程为孤儿进程。

    展开全文
  • POSIX 下进程控制经典案例

    千次阅读 2015-11-03 19:55:38
    在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。操作系统所具有的四大特征也都是基于进程而形成的,并可从进程的观点来研究操作系统。显然,在操作系统中,进程是一个极其重要...

    在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。操作系统所具有的四大特征也都是基于进程而形成的,并可从进程的观点来研究操作系统。显然,在操作系统中,进程是一个极其重要的概念。下面我们通过一个经典案例来谈一下进程。

    #include <stdio.h> 
    #include <sys/types.h>
    #include <unistd.h> 
    #include <signal.h>
    #include <ctype.h> 
    
    /* 允许建立的子进程个数最大值 */
    #define MAX_CHILD_NUMBER 10 
    
    /* 子进程睡眠时间 */
    #define SLEEP_INTERVAL 2 
    
    /* 子进程的自编号,从0开始 */
    int proc_number=0; 
    
    void do_something(); 
    
    main(int argc, char* argv[])
    {   
    	/* 子进程个数 */
    	int child_proc_number = MAX_CHILD_NUMBER; 
    	int i, ch; 
        pid_t  child_pid; 
    	pid_t pid[10]={0}; /* 存放每个子进程的id */
    
    	if (argc > 1) 		/* 命令行参数第一个参数表示子进程个数*/ 
    	{
    		child_proc_number = atoi(argv[1]); 
    		child_proc_number= (child_proc_number > 10) ? 10 :	 child_proc_number;		
    	}
    
    	for (i=0; i<child_proc_number; i++) 
    	{ 
    		/* 填写代码,建立child_proc_number个子进程要执行
    		 * proc_number = i; 
    		 * do_something(); 
    		 * 父进程把子进程的id保存到pid[i] 
    		 */ 
    		child_pid = fork();
    
    		if (child_pid == -1)	//如果出现错误,fork返回一个负值
    		{
    			perror("create error!\n");
    			return 1;
    		}
    		else if (child_pid == 0)
    		{
    			proc_number=i;		//在子进程中,fork返回0
    			do_something();
    		}
    		else
    		{
    			pid[i] = child_pid;	//在父进程中,fork返回新创建子进程的进程ID	
    		}
    	}
    
    	/* 让用户选择杀死进程,数字表示杀死该进程,q退出 */
    	while ((ch = getchar()) != 'q')      
    	{ 
    		if (isdigit(ch))  
    		{ 
    	       	/* 向pid[ch-'0']发信号SIGTERM, 
                 * 杀死该子进程 
    			 */ 
    			kill(pid[ch-'0'], SIGTERM);
    			
    	    }
    	} 
    	/* 杀死本组的所有进程 */ 
    	kill(0, SIGKILL);
    	
    	return;
    } 
    
    void do_something() 
    {
    	int k;
    	for(;;)  
    	{  
    		printf("This is process No.%d and its pid is %d, address锛?p\n", proc_number,  getpid(), &proc_number);
    	    sleep(SLEEP_INTERVAL); /* 主动阻塞两秒钟 */
    	 } 
    } 
    
    1、你最初认为运行结果会怎么样?

    会创建10个进程,进程号为0~9。若输入数字、回车会杀死指定进程。若输入q、回车,会杀死所有进程

    2、实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。

    运行程序,命令行第二个参数是20,但程序中的限制还是会创建10个进程,每隔SLEEP_INTERVAL秒刷新一次输出在命令行界面。直到输入数字、回车,将会杀死指定数字的进程,或直到输入q、回车,会杀死本组所有进程。
    特点:每次输出一定数目的进程,但每次输出的进程的次序基本都不一样,循环随机输出。进程的pid也是随着进程号的序号而按序增长的,进程的地址都相同。分析:每创建一个子进程时,将其pid存储在pid[i]中,i存储在proc_number,然后调用死循环函数do_something(),输出该进程的编号proc_number,pid和地址。创建进程是利用循环创建的,其进程号也是按照循环中的i值由小到大的,依照创建的顺序pid也依次递增。进程地址相同是由于输出的是&proc_number,其指针指向的是同一存储单元,打印出的是逻辑地址,但是实际上它们的物理地址是不同的。

    3、proc_number这个全局变量在各个子进程里的值相同吗?为什么?

    相同,因为子进程相互独立,资源互不影响

    4、kill命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?

    2次。kill(pid[ch=’0’], SIGTERM);kill(0,SIGTERM);第一个是杀死进程号pid[ch-‘0’],执行后输出的结果中不会再有该进程号。第二次是杀死本组所有进程。即主进程以及它所创建的所有子进程,执行后程序退出结束。

    5、使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?

    进程在main()中return,或调用exit()都可以主动退出。使用kill()则是强制性的异常退出。 主动退出更好,若在子进程退出前使用kill()杀死其父进程,则系统会让init进程接管子进程,该子进程就会成为孤儿进程。当使用kill()杀死子进程使得子进程先于父进程退出时,父进程又没有调用wait()函数等待子进程结束,子进程处于僵死状态,即僵尸进程。且一直会保持下去,直到系统重启。子进程处于僵死状态时内核只保存该进程的必要信息以备父进程所需,此时子进程始终占着资源,这也就减少了系统可以创建的最大进程数

    6、将do_something()中的死循环改为有穷资源后创建多少个进程?

    创建很多很多个进程,但有一定的数目限制。是由于父进程刚开始创建的子进程中的第一个进程又会创建它自己的子进程,这样的规律下来就会创建很多很多个进程,但是进程数又是一定的

    7、输出proc_number的地址,观察并解释原因。

    输出的所有的地址相同。是因为在C语言中“&proc_number”是一个指针,指针指向同一存储单元,打印出的是逻辑地址,是相同的,但物理地址不同

    展开全文
  • 为什么要使用mmap进行IPC呢,个人总结有...为什么要用posix semaphore进程同步呢,个人总结有三点: 接口简单好用效率高,在Linux3.2.0开销只有0.41个Clock(而XSI semaphore有7.33)方便在非亲属进程间同步时使用
  • POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,Daniel Robbins 向您精确地展示在编程中如何...其中还涉及大量幕后细节,读完本系列文章,您完全可以运用 POSIX 线程创建多线程程序。
  • posix_spawnThe posix_spawn() and posix_spawnp() functions create a new child process from the specified process image constructed from a regular executable file. It can be used to replace the relative...
  • Linux进程通信之POSIX共享内存

    万次阅读 2013-08-13 01:09:19
    前面已经讨论过Linux个各种进程间的通信方式:管道,FIFO,消息队列,他们的共同特点就是通过内核来进行通信(假设POSIX消息队列也是在内核中实现的,因为POSIX标准并没有限定它的实现方式)。向管道,FIFO,消息...
  • Linux进程同步之POSIX信号量

    万次阅读 多人点赞 2013-07-01 22:14:15
    POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分。在SUS(Single UNIX Specification)...在《UNIX网络编程 卷2:进程间通信》的前言第二页与第1版的区别中作者提到“POSIX IPC函数时大势所趋,因为他们比
  •  1.Posix 有名信号量:使用Posix IPC 名字标识,可用于进程或线程间的同步。  2.Posix 基于内存的信号量:存放在共享内存区中,可用于进程或线程间的同步。  我们暂时只考虑不同进程间的同步。首先考虑二值信号...
  • linux创建进程消耗内存

    千次阅读 2016-05-25 20:11:49
    使用python脚本测试linux的最大进程数量
  • Linux进程间通信之POSIX共享内存

    千次阅读 2017-08-02 21:15:34
    共享内存是最高效的IPC机制,因为它不涉及进程之间的任何数据传输。这种高效率带来的问题是,我们必须用其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。...要使用POSIX共享内存对象需要完成
  • 信号量用于进程或线程间同步,Posix信号量是一个非负整型,只有两种操作,加一(sem_post)和减一(sem_wait),如果信号量值为0,...不同进程可以通过同一个名字来操作有名信号量,sem_open用于创建或者获取已存在的信号量
  • Linux进程通信之POSIX消息队列

    万次阅读 多人点赞 2013-08-07 08:53:42
    l 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必需已经打开来读,否则写进程就会阻塞(默认情况)。 l IPC的持续性...
  • Linux进程间通信之POSIX信号量

    千次阅读 2017-08-01 10:33:37
    POSIX信号量不仅可以用于进程之间的同步,也可以用于线程之间的同步。概述SUSv3规定了两种类型的POSIX信号量。 命名信号量:这种信号量拥有一个名字。通过使用相同的名字调用sem_open(),不相关的进程能够访问同一个...
  • POSIX简介

    2020-06-01 16:41:55
    POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准...完成同一功能,不同内核提供的系统调用(也就是一个函数)是不同的,例如创建进程,linux是fork函数,..
  • POSIX介绍

    2021-02-20 16:15:44
    简单地说:完成同一功能,不同内核提供的系统调用(也就是一个函数)是不同的,例如创建进程,linux是fork函数,windows是creatprocess函数。好,我现在在linux写一个程序,用到fork函数,那么这个程序该怎么...
  • Linux进程间通信之POSIX消息队列

    千次阅读 2017-07-30 15:49:23
    消息队列可认为是一个消息链表,它允许进程之间以消息的形式交换数据。有足够写权限的进程或线程可往队列中放置消息,有足够读权限的进程或线程可从队列中取走消息。每个消息都是一个记录,它由发送者赋予一个优先级...
  • linux进程通信———Posix消息队列简介及基础库函数 引言:消息队列可认为是一个消息链表,有足够写权限的线程可向队列中放置消息,有足够读权限的线程可从队列中取走消息。本篇笔记将简要介绍Posix消息队列特性、...
  • 进程间通信(8) - 共享内存(posix)

    千次阅读 2015-06-19 23:59:01
    进程间通信(8) - 共享内存(posix)
  • 进程间通信(6) - 消息队列(posix)

    千次阅读 2015-06-17 22:30:58
    进程间通信(6) - 消息队列(posix)
  • 1.概述 Posix IPC的三种类型
  • Posix IPC

    千次阅读 2016-12-13 05:20:51
    Posix消息队列,Posix信号量和Posix共享内存区合称为Posix IPC px_ipc_name() 函数可以为定位Posix IPC名字而加上正确的目录 #### 创建与打开IPC通道: 说明 mq_open sem_open shm_...
  • 当我们在linux系统中进行进程间通信时,会发现例如共享内存,信号量,消息队列等方式时,会发现有System v以及POXIS两种类型。因此,我们探究一下System v和POXIS到底代表着什么意义。 POSIX POSIX(Portable ...
  • POSIX thread

    千次阅读 2012-11-20 19:37:05
    POSIX 线程详解 1 POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,Daniel Robbins ...其中还涉及大量幕后细节,读完本系列文章,您完全可以运用 POSIX 线程创建多线程程序。
  • POSIX信号量进程是3种 IPC(Inter-Process Communication) 机制之一,3种 IPC 机制源于 POSIX.1 的实时扩展。Single UNIX Specification 将3种机制(消息队列,信号量和共享存储)置于可选部分中。在 SUSv4 之...
  • POSIX 消息队列简介

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,636
精华内容 21,854
关键字:

posix下如何创建进程