Linux进程创建及生产者消费者问题实现

  因为之前考试痛苦的复习,所以就没时间把做出来的整理出博客,现在考完了,稍微看了以下之前做的就把这篇博客整理出来了

进程的创建在了linux系统下使用fork函数实现的,一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间,然后把原来的进程的所有值都复制到新的进程中。

那么直接上代码了。

#include
#include
#include
#include
void main()
{
	pid_t p1,p2;
	if((p1=fork())==0)
	{
		printf("This is child_1 process\n");
	}
	else
	{
		if((p2=fork())==0)
		{
			printf("This is child_2 process\n");
		}
		else
		{
			printf("This is parent process\n");
		}
	}
}

 

然后是再调用exec( )用新的程序替换该子进程的内容 ,并利用wait( )来控制进程执行顺序。调用Exit()使子进程结束。

 

因为找到一篇博客学习了一下发现那篇博客写的很好,有很详尽的介绍和讲解,感兴趣的可以去看看。

https://blog.csdn.net/zjwson/article/details/53337212

#ifdef HAVE_CONFIG_H
#include 
#endif

#include 
#include 
#include 
#include 
#include 
 
 int main(int argc, char *argv[])
 {
   //以NULL结尾的字符串数组的指针,适合包含v的exec函数参数
   char *arg[] = {"ls", "-a", NULL};
   
   /**
    * 创建子进程并调用函数execl
    * execl 中希望接收以逗号分隔的参数列表,并以NULL指针为结束标志
    */
   if( fork() == 0 )
   {
     // in clild 
     printf( "1------------execl------------\n" );
     if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )
     {
       perror( "execl error " );
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用函数execv
    *execv中希望接收一个以NULL结尾的字符串数组的指针
    */
   if( fork() == 0 )
   {
     // in child 
     printf("2------------execv------------\n");
     if( execv( "/bin/ls",arg) < 0)
     {
       perror("execv error ");
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用 execlp
    *execlp中
    *l希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
    *p是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
    */
   if( fork() == 0 )
   {
     // in clhild 
     printf("3------------execlp------------\n");
     if( execlp( "ls", "ls", "-a", NULL ) < 0 )
     {
       perror( "execlp error " );
       exit(1);
     }
   }
   
   /**
    *创建子里程并调用execvp
    *v 望接收到一个以NULL结尾的字符串数组的指针
    *p 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件
    */
   if( fork() == 0 )
   {
     printf("4------------execvp------------\n");
     if( execvp( "ls", arg ) < 0 )
     {
       perror( "execvp error " );
       exit( 1 );
     }
   }
   
   /**
    *创建子进程并调用execle
    *l 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志
    *e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
    */
   if( fork() == 0 )
   {
     printf("5------------execle------------\n");
     if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )
     {
       perror("execle error ");
       exit(1);
     }
   }
   
   /**
    *创建子进程并调用execve
    * v 希望接收到一个以NULL结尾的字符串数组的指针
    * e 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境
    */
   if( fork() == 0 )
   {
     printf("6------------execve-----------\n");
     if( execve( "/bin/ls", arg, NULL ) == 0)
     {
       perror("execve error ");
       exit(1);
     }
   }
   return EXIT_SUCCESS;
}

接下来是重点生产者消费者问题

生产者:负责生产消息,在缓冲区满后休眠;

消费者:负责消费消息,在缓冲区空后休眠;

生产者休眠,是因为缓冲区满,所以只要消费者进行了消费,那么缓冲区就会有新的空间,生产者就可以继续生产,故每次消费者消费以后都要试图唤醒生产者,无论生产者是否休眠。

消费者休眠,是因为缓冲区空,所以只要生产者进行了生成,那么缓冲区就会有新的消息,消费者就可以继续生产,故每次生产者生成以后都要试图唤醒消费者,无论消费者是否休眠。

Linux中信号量机制的实现是semget,semop,semctl三个函数实现的,当时也是看的别人的博客,在这里也推荐给大家。

https://blog.csdn.net/guoping16/article/details/6584043

 

第一个函数是得到信号量对象,第二个是进行p,v操作,第三个是得到一个信号集对象

整体思路就是生产前p缓冲区,生产后v,消费也是一样

代码也做了很多注释

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define NEED_P 2//生产者数量
#define NEED_C 3//消费者数量
#define WORKS_P 6//生产次数
#define WORKS_C 4//消费次数

#define BUF_LENGTH (sizeof(struct mybuffer))
#define LETTER_NUM 3
#define SHM_MODE 0600

#define SEM_ALL_KEY 1234
#define SEM_EMPTY 0
#define SEM_FULL 1

//缓冲区结构(循环队列)
struct mybuffer
{
    char letter[LETTER_NUM];//存放字符数组
    int head;//头指针
    int tail;//尾指针
    int is_empty;//空标志
};

//得到5以内的一个随机数
int get_random()
{
    int t;
    srand((unsigned)(getpid() + time(NULL)));
    t = rand() % 5;
    return t;
}

//得到A~Z的一个随机字母
char get_letter()
{
    char a;
    srand((unsigned)(getpid() + time(NULL)));
    a = (char)((char)(rand() % 26) + 'A');
    return a;
}

//P操作
void p(int sem_id, int sem_num)
{
    struct sembuf xx;
    xx.sem_num = sem_num;//操作信号在信号集中的编号,第一个信号的编号是0
    xx.sem_op = -1;//获取资源的使用权
    xx.sem_flg = 0;
    semop(sem_id, &xx, 1);
}

//V操作
void v(int sem_id, int sem_num)
{
    struct sembuf xx;
    xx.sem_num = sem_num;//操作信号在信号集中的编号,第一个信号的编号是0
    xx.sem_op = 1;//获取资源的使用权
    xx.sem_flg = 0;
    semop(sem_id, &xx, 1);
}

//主函数
int main(int argc, char * argv[])
{
    int i, j;
    int shm_id, sem_id;
    int num_p = 0, num_c = 0;
    //定义一个缓冲区指针shmptr
    struct mybuffer * shmptr;
    char lt;
    time_t now;
    pid_t pid_p, pid_c;
    //创建两个新信号量
    sem_id = semget(SEM_ALL_KEY, 2, IPC_CREAT | 0660);
    if (sem_id >= 0)
    {
        printf("Main process starts. Semaphore created.\n");
    }
    semctl(sem_id, SEM_EMPTY, SETVAL, LETTER_NUM);
    semctl(sem_id, SEM_FULL, SETVAL, 0);
    //创建新的共享内存
    if ((shm_id = shmget(IPC_PRIVATE, BUF_LENGTH, SHM_MODE)) < 0)
    {
        printf("Error on shmget.\n");
        exit(1);
    }
    //把共享内存区对象映射到调用进程的地址空间
    if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
    {
        printf("Error on shmat.\n");
        exit(1);
    }
    shmptr->head = 0;
    shmptr->tail = 0;
    shmptr->is_empty = 1;
    
    while ((num_p++) < NEED_P)
    {
        if ((pid_p = fork()) < 0)
        {
            printf("Error on fork.\n");
            exit(1);
        }
        //如果是子进程,开始创建生产者
        if (pid_p == 0)
        {
        //将共享内存区对象映射到调用进程的地址空间
            if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
            {
                printf("Error on shmat.\n");
                exit(1);
            }
            for (i = 0; i < WORKS_P; i++)
            {
                p(sem_id, SEM_EMPTY);//对资源进行p操作
                sleep(get_random());//等待随机时间
                shmptr->letter[shmptr->tail] = lt = get_letter();//随机生成一个字母放入缓冲区
                shmptr->tail = (shmptr->tail + 1) % LETTER_NUM;//尾指针后移
                shmptr->is_empty = 0;//空标志置0
                now = time(NULL);
                printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
        //输出缓冲区字符
                for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
                {
                    printf("%c", shmptr->letter[j % LETTER_NUM]);
                }
                printf("\tProducer %d puts '%c'.\n", num_p, lt);
                fflush(stdout);
                v(sem_id, SEM_FULL);//对资源进行v操作
            }
            shmdt(shmptr);//断开共享内存连接
            exit(0);
        }
    }

    while (num_c++ < NEED_C)
    {
        if ((pid_c = fork()) < 0)
        {
            printf("Error on fork.\n");
            exit(1);
        }
        //如果是子进程,开始创建消费者
        if (pid_c == 0)
        {
            if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
            {
                printf("Error on shmat.\n");
                exit(1);
            }
            for (i = 0; i < WORKS_C; i++)
            {
                p(sem_id, SEM_FULL);//对资源进行p操作
                sleep(get_random());//等待随机时间
                lt = shmptr->letter[shmptr->head];//取出缓冲区第一个字符
                shmptr->head = (shmptr->head + 1) % LETTER_NUM;//头指针后移
                shmptr->is_empty = (shmptr->head == shmptr->tail);
                now = time(NULL);
                printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
        //输出缓冲区字符
                for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
                {
                    printf("%c", shmptr->letter[j % LETTER_NUM]);
                }
                printf("\tConsumer %d gets '%c'.\n", num_c, lt);
                fflush(stdout);
                v(sem_id, SEM_EMPTY);//对资源进行v操作
            }
            shmdt(shmptr);//断开共享内存连接
            exit(0);
        }
    }
    
    //主控程序最后退出
    while(wait(0) != -1);
    shmdt(shmptr);
    shmctl(shm_id, IPC_RMID, 0);
    semctl(sem_id, IPC_RMID, 0);
    printf("Main process ends.\n");
    fflush(stdout);
    exit(0);
}

以下是程序截图:

Linux进程创建及生产者消费者问题实现_第1张图片

完结撒花~

你可能感兴趣的:(操作系统)