操作系统简明

  1. 1、程序进程线程的基本概念

程序:源代码、指令(静态的),一个程序中可以有多个进程。

进程:正在执行的程序实例(运行,动态的),不同进程之间是相互独立的(拥有不同的内存空间),进程使用完后linux内核会回收进程资源。

线程:由进程引发,线程从属于进程。这是因为创建进程与回收进程资源会消耗大量资源,故产生了线程。一个进程可以有多个线程,多个线程共享这一个线程的资源(比如线程中的变量)。

任务:具体要做的事情(由进程和线程做)。

  1. 2、查询正在运行的进程PID

函数:pstree -g 可以查询进程树

#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);     //返回正在调用的进程PID
pid_t getppid(void);    //返回正在调用的进程的父进程的PID

#include <sys/types.h>
#include <unistd.h>
#include <iostream>
using namespace std;

int main()
{
    pid_t pid;
    while(1)
    {
        cout << "pid = " << getpid() << endl;
        cout << "ppid = " << getppid () << endl;
        cout << "hello world" << endl;
        sleep(1);
    }
    return 0;
}
  1.  3、创建一个子进程

函数:通过拷贝调用进程(父进程),创建一个子进程,子进程中包含父进程中所用代码,并且会单独分配内存空间。

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);    //父进程将返回子进程的PID,子进程将返回0

同时创建多个进程 ,下面例子将产生四个进程。他们的代码均相同,都会输出cout中的内容。

#include <sys/types.h>
#include <unistd.h> 
#include <iostream>
using namespace std;

int main()
{
    pid_t pid1, pid2;
    pid1 = fork();
    pid2 = fork();
    cout << "pid1 = " << pid1 << " pid2 = " << pid2 <<  endl;
    cout << "getppid() = " << getppid() << endl;
    // cout << "Hello world" << endl;
    return 0;
}

/*
pid1 = 308770 pid2 = 308771
getppid() = 302678
pid1 = 308770 pid2 = 0
getppid() = 308769
pid1 = 0 pid2 = 308772
pid1 = 0 pid2 = 0
getppid() = 308769
getppid() = 308770


*/

一个进程中创建的子进程他们内容相同,但拥有不同的内存地址,故全局变量count各有一份,互不干扰。

#include <iostream>
#include <sys/types.h>
#include <unistd.h>

int count = 0;
int main()
{
    pid_t pid;
    pid = fork();
    if(pid > 0)                 // parent process
    {
        while (1)
        {
            // std::cout << "Hello world" << std::endl;
            std::cout << "parent process" <<  "count = " << count ++ << std::endl;
            sleep(1);
        }      
    }
    else if (pid == 0)          //child process
    {
        while (1)
        {
            std::cout << "child process count = " << count ++ << std::endl;           
            sleep(2);
        }
        
    }
    else
        std::cout << "PROCESS ERROR!" << std::endl;
    return 0;
}
/*结果
parent processcount = 0
child process count = 0
parent processcount = 1
parent processcount = 2
child process count = 1
parent processcount = 3
child process count = 2
parent processcount = 4
parent processcount = 5
child process count = 3
parent processcount = 6

*/

 父进程结束不影响子进程,下面例子即父进程输出10次结束,而子进程仍然在继续运行。

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
int count = 0;
int main()
{
    pid_t pid;
    int i = 0;
    pid = fork();
    if(pid > 0)                 // parent process
    {
        for(i = 0; i < 10; i++)
        {
            // std::cout << "Hello world" << std::endl;
            std::cout << "parent process " <<  "count = " << count ++ << std::endl;
            sleep(1);
        }
        
    }
    else if (pid == 0)          //child process
    {
        while (1)
        {
            std::cout << "child process count = " << count ++ << std::endl;          
            sleep(1);
        }
        
    }
    else
        std::cout << "PROCESS ERROR!" << std::endl;
    return 0;
}

为什么要用到多个进程?
(1)执行不同的任务。

父进程与子进程宏观上为并行运行(cout看不出来谁先运行),默认是父进程先调用,几乎可以忽略不计

(2)父进程与子进程具有不同的内存空间,同名变量(不论是否为全局变量),并不是同一个地址,故改变不会相互影响。

(3)父进程结束,子进程不一定结束,互不干扰。

  1. 4、检测子进程

wait函数将一直等待子进程结束,若没有监测到进程结束,则将一直阻塞。

#include <sys/types.h>
#include <sys/wait.h>

/*
  监测子进程是否结束,返回值均为结束的PID,可定义一个整形变量储存
   进程结束后返回的状态。
*/
pid_t wait(int *wstatus);
/*
  监测指定子进程是否结束,返回值均为结束的PID,可定义一个整形变量储存
   进程结束后返回的状态。
*/
pid_t waitpid(pid_t pid, int *wstatus, int options);
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>

using namespace std;
//run model: ./a.out  10 5
int main(int argc, char * argv[])
{
    pid_t child_pid;
    int numDead;
    int i;
    for(i = 1; i < argc; i ++)
    {
        switch (fork())
        {
            case -1:
                perror("fork()");
                exit(0);
            case 0:
                cout << "Child " << i << "started ID = " << getpid() << "sleeping " << argv[i] << " sec." <<endl;
                sleep(atoi(argv[i]));
                exit(0);
            default:
                break;
        } 
    }
    numDead = 0;
    while (1)
    {
        child_pid = wait(nullptr);
        numDead ++;
        cout << "wait() returned child PID : " << child_pid << " numDead = " <<  numDead << endl;
        if(child_pid == -1)
        {
            cout << "No more children, Byebye!" << endl;
            exit(0);
        }
    }
    return 0;
}

  1.  5、线程

单个线程的创建:

由进程创建,并且共享进程资源。同一个进程创建的多个进程,也将共享同一片内存单元的资源,多个线程几乎并发运行。

创建线程函数如下:

/*
    第一个参数表示线程ID号,输出的。第二个为,一个结构体指针,存放初始化线程属性。
    第三个参数,函数指针,函数指针名为void * (*start_routine)(void *) 返回值为void *
    传入参数为void * 可以为任意数据;start_roution()函数为新线程所要执行的内容。第四个为传入start_roution()中的参数
    // int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                        //   void *(*start_routine) (void *), void *arg); 


     int pthread_join(pthread_t thread, void **retval);
     param: pthread_t thread 线程的ID
            void **retval    保存目标返回的状态
*/

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

//Compile and link with -pthread.

线程例子如下:

创建两个线程变量,pthread。使用pthread_create()创建线程,并创建线程的执行函数。进程自动调用进程函数thread_function(),通过传入参数count,执行五次循环,打印输出Hello world。最后调用pthread_join()结束进程。

void * thread_function(void *);
int main()
{
    pthread_t pthread;
    int ret;
    int count = 5;

    ret = pthread_create(&pthread, nullptr, thread_function,&count);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(1);
    }
    pthread_join(pthread,nullptr);
    std::cout << "The thread is over, process over too " << std::endl;
    return 0;
}


void * thread_function(void *arg)
{
    int i;
    std::cout <<  "Thread begins running" << std::endl;
    for(i = 0; i < * static_cast<int *>(arg); i ++)
    {
        std::cout << "Hello world" << std::endl;
        sleep(1);
    }
    return nullptr;  
}

多个进程的创建:

创建pthread1, 与pthread2线程变量,调用pthread_create创建进程,创建成功则返回零。并且创建全局变量,分别在两个线程函数中将变量后++,观察输出发现,两个线程函数共享进程资源,两个线程均使用同一个count变量。        

void * thread1_function(void *arg);
void * thread2_function(void *arg);
int count = 0;
int main()
{
    pthread_t pthread1, pthread2;
    int ret;
   // int count = 5;

    ret = pthread_create(&pthread1, nullptr, thread1_function,nullptr);
    if(ret != 0)        
    {
        perror("pthread_create");
        exit(1);
    }

    ret = pthread_create(&pthread2, nullptr, thread2_function, nullptr);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(1);
    }

    pthread_join(pthread1,nullptr);
    pthread_join(pthread2,nullptr);

    std::cout << "The thread is over, process over too " << std::endl;



    return 0;
}

void * thread1_function(void *arg)
{
    std::cout <<  "Thread begins running" << std::endl;
    while(1)
    {
        // std::cout << "Hello world" << std::endl;
        std::cout << "pthread1 count = " << count ++ << std::endl;

        sleep(1);
    }
    return nullptr;
    
}

void * thread2_function(void *arg)
{
    std::cout << "Thread2  begins running" << std::endl;
    while(1)
    {
        // std::cout << "Good morning" << std::endl;
        std::cout << "pthread2 count = " << count ++ << std::endl;

        sleep(1);
    }
    return nullptr;
}

  1. 6、无名管道

实现任务间的通信,同步(只适用于亲缘关系的进程)

进程间通信:管道(pipe):分为无名管道和有名管道,无名管道只能单向传输信息,管道两端都有读写两端,使用时,一端打开读端,一端打开写端,进行单向传输。设计管道参考思路,父进程,可以打开写端,对于子进程打开读端,实现由父进程向子进程传输消息的单向通信。如果需要实现父子进程的双向通信,则需要两条管道。而有名管道可以实现没有亲缘关系的进程之间的通信。即两个不同cpp,编译成不同的可执行文件,两个不同的可执行文件(两个进程)通信。

创建管道函数:

/*
    param : 创建一个管道,pipefd 将返回创建的管道文件描述符,pipefd[0] 
            指向管道的读端,pipefd[1]指向管道的写端。管道的数据储存在内核中,若没有读取
            数据将阻塞在内核中。
    return: 创建成功返回0, 失败返回-1, 并设置appropriately
*/
    int pipe2(int pipefd[2], int flags);
    int pipe(int pipefd[2]);
#include <iostream>
#include <unistd.h>
#include <sys/types.h>

int main()
{

    int fd[2];
    pid_t pid;
    if(pipe(fd) == -1)
    {
        perror("pipe");
    }

    pid = fork();

    if(pid > 0)     //parent process
    {
        close(fd[0]);
        sleep(5);
        write(fd[1], "ab", 2);
        while (1);
        
    }
    else if(pid == 0)
    {
        char ch[2];
        std::cout << "Child process is waiting for data: " << std::endl;
        close(fd[1]);
        read(fd[0], ch, 2);         //没有收到消息,将会一直等待阻塞
        std::cout << "Read from pipe: " << ch << std::endl;

    }

    return 0;
}

 一个管道的容量:

子进程一直向管道写内容,每次一个字节,并用count计数,记录写的次数,而父进程监控子进程结束。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int fd[2];
    if(pipe(fd) != 0)
        perror("pipe");
    pid = fork();
    if(pid == 0)
    {
        int count = 0;
        char ch = '*';
        close(fd[0]);
        while(1)
        {
        write(fd[1], &ch, 1);
        std::cout << "count = " << ++count << std::endl;            
        }
    }
    else if(pid > 0)
    {
        waitpid(pid, nullptr, 0);
    }
}

父子进程之间的管道通信。子进程关闭管道的读端,键盘输入内容向管道中写。父进程关闭写端,向管道中读取内容。若没有读取到内容,则将一直堵塞。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int fd[2];
    if(pipe(fd) != 0)
        perror("pipe");
    pid = fork();
    if(pid == 0)
    {
        char temp[100];

        close(fd[0]);
        while(1)
        {
            std::cin >> temp;
            write(fd[1], temp, sizeof(temp));       
        }
    }
    else if(pid > 0)
    {
        close(fd[1]);
        char temp[100];
        while(1)
        {
        read(fd[0], temp, sizeof(temp));
        std::cout << "recevie data = " << temp << std::endl;
        }
    }
}

 由于管道是单向通信,若要通过管道实现父子进程间的双向通信,则需要两条管道。

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <string.h>

int main()
{
    pid_t pid;
    int fd1[2],fd2[2];
    if(pipe(fd1) != 0)
        perror("pipe1");
    if(pipe(fd2) != 0)
        perror("pipe2");
    pid = fork();
    if(pid == 0)
    {
        char temp[100];
        close(fd1[1]);
        close(fd2[0]);
        while(1)
        {
            memset(temp, '\0', sizeof(temp));
            read(fd1[0],temp, sizeof(temp));
            printf("Parent send temp = %s \n", temp);
            for(int i = 0; i < sizeof(temp); i ++)
                temp[i] = toupper(temp[i]);
            write(fd2[1], temp, sizeof(temp));
        }
    }
    else if(pid > 0)
    {
        close(fd1[0]);
        close(fd2[1]);
        char temp[100];
        while(1)
        {
            memset(temp, '\0', sizeof(temp));
            std::cin.getline(temp, sizeof(temp));
            write(fd1[1], temp, sizeof(temp));
            memset(temp, '\0', sizeof(temp));
            read(fd2[0], temp, sizeof(temp));
            std::cout << "Child  send temp = " << temp << std::endl;
        }
    }
}

有名管道,可以实现两个没有亲缘关系进程之间的通信。

读端:

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
using namespace std;

/*
    param: 文件路径
            mode 设置文件模式,读写;
            创建一个先入先出的文件,被叫做有名管道。一旦创建这个文件,
            任何进程都可以打开这个管道进行读写,同操作文件的读写一样,
            但读的时候,必须等待写端被打开,否则会阻塞。
    return: 成功返回0
    int mkfifo(const char * pathname, mode_t mode)
*/

int main()
{   
    int ret, fd;
    char buf[100];
    ret = mkfifo("my_fifo", 666);//命名管道具有读写权限
    if(ret != 0)
        std::cout << "mkfifo" << std::endl;
    cout << "Prepare reading from named pipe: " << endl;
    fd = open("my_fifo", O_RDWR);
    if(fd == -1)
        std::cout << "open" << std::endl;

    while(1)
    {
        memset(buf, '\0', sizeof(buf));
        read(fd, buf, sizeof(buf));
        std::cout << "Read from named pipe: " << '\n'
                  << buf << std::endl;
        sleep(10);
    }
    return 0;
}

写端:

#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
using namespace std;

/*
    param: 文件路径
            mode 设置文件模式,读写;
            创建一个先入先出的文件,被叫做有名管道。一旦创建这个文件,
            任何进程都可以打开这个管道进行读写,同操作文件的读写一样,
            但读的时候,必须等待写端被打开,否则会阻塞。
    return: 成功返回0
    int mkfifo(const char * pathname, mode_t mode)
*/

int main(int argc, char *argv[])
{   
    int  fd;
    char buf[100];
    
    fd = open("my_fifo", O_WRONLY);     //只允许写
    if(fd == -1)
        std::cout << "open" << std::endl;
    if(argc == 1)
    {
        printf("Please send something to named pipe: \n");
        exit(EXIT_FAILURE);
    }

    strcpy(buf,argv[1]);
    write(fd, buf, sizeof(buf));
    printf("Write to the pipe: %s\n", buf);
    return 0;
}


  1. 7、共享内存

开辟一个共享的内存空间,父进程与子进程不相关的均可以看到这块内存,从而实现进程之间的通信。创建共享内存的步骤如下:

(1)创建共享内存

(2)映射共享内存到线程(父子进程均需要映射)

(3)解除映射

(4)删除共享内存

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>


/*
    开辟System V 的共享内存单元
    
    return : 返回System V共享内存单元的描述符,与Key相关联,Key是内核中使用。
              当key值为IPC_PRIVATE则创建size大小的共享内存,
             
       #include <sys/ipc.h>
       #include <sys/shm.h>
       int shmget(key_t key, size_t size, int shmflg);

       将描述的共享内存空间映射到调用的进程
       param:1、共享内存空间的描述符
              2、若为空,则自动选择当前进程映射的地址
              3、赋予进程对共享内存的读写权限,为0,则为读写都具备。
       return: 若成功,则返回映射到内存空间的首地址。
       #include <sys/types.h>
       #include <sys/shm.h>
       void *shmat(int shmid, const void *shmaddr, int shmflg);
       解除绑定,
       int shmdt(const void *shmaddr);



*/


char msg[] = "Hello world";
int main()
{
    int shmid;
    pid_t pid;
    shmid = shmget(IPC_PRIVATE, 1024, 666|IPC_CREAT);
    pid = fork();
    if(pid > 0)
    {
        char * p_addr;
        p_addr = shmat(shmid, NULL, 0);
        memset(p_addr, '\0', sizeof(msg));
        memcpy(p_addr, msg, sizeof(msg));
        shmdt(p_addr);
        waitpid(pid, NULL, 0);
    }
    else if(pid == 0)
    {
        char *c_addr;
        c_addr = (shmat(shmid, NULL, 0));
        printf("Child process waits a short time: \n");
        sleep(3);
        printf("Chiled process reads from shared memory: %s\n", c_addr);
        shmdt(c_addr);

    }
    else
    {
        printf("fork() ");
    }
    return 0;
}

非亲缘关系的进程,若想使用共享内存,则需要进行需要自定义键值。IPC_PRIVATE由系统自动生成键值。

read读端

#define MY_KEY 9527         
int main()
{
    int shmid;
    shmid = shmget(MY_KEY, 1024, IPC_CREAT);
    char * c_addr;
    c_addr = shmat(shmid, NULL, 0);
    printf("Read from shared memory: %s",c_addr);
    shmdt(c_addr);
    return 0;
}

write端

#define MY_KEY 9527         

char msg[] = "Hello world";
int main()
{
    int shmid;
    shmid = shmget(MY_KEY, 1024, IPC_CREAT);
    char * p_addr;
    p_addr = (shmat(shmid, NULL, 0));
    memset(p_addr, '\0', sizeof(msg));
    memcpy(p_addr, msg, sizeof(msg));
    shmdt(p_addr);

    return 0;
}
  1. 8、消息队列

将消息放入一个容器中,实际上是一个结构体。

const int MY_TYPE = 9527;
char msg[] = "Hello world";
int main()
{
    pid_t pid;
    pid = fork();
    int msgid;

    struct msgbuf
    {
        long mtype;
        char mtext[100];
        int number;   
    };
    struct msgbuf buff;

    msgid = msgget(IPC_PRIVATE, IPC_CREAT);
    if(pid > 0)
    {
        sleep(0);
        buff.mtype = MY_TYPE;
        std::cout << "Please enter a string you want to send: " << std::endl;
        std::cin >> buff.mtext;
        std::cout << "Please enter a number you want to send: " << std::endl;
        std::cin >> buff.number;
        msgsnd(msgid, &buff, sizeof(buff) - sizeof(buff.mtype), 0);
        waitpid(pid, NULL, 0);
    }
    else if(pid == 0)
    {
        sleep(10);
        std::cout << "Child process is waiting for msg: " << std::endl;
        msgrcv(msgid, &buff, sizeof(buff) - sizeof(buff.mtype), MY_TYPE, 0);    //由于创建的父子进程均有buff,且不是同一个,故可以这样操作
        std::cout << "Child process read msg: " << buff.mtext << "\t"<< buff.number << std::endl;
        msgctl(msgid, IPC_RMID,nullptr);
    }
    else
    {
        std::cout << "fork()" << std::endl;
    }
    return 0;
}

非亲缘关系的进程之间使用消息队列通信,需要使用相同的消息类型以及键值。

wirte端:

#define MY_TYPE 9527
#define MY_KEY 1314
int main()
{

    int msgid;

    struct msgbuf
    {
        long mtype;
        char mtext[100];
        int number;
    };

    struct msgbuf buff;
    msgid = msgget(MY_KEY,IPC_CREAT);
    buff.mtype = MY_TYPE;
    printf("Please enter a string you want to send: \n");
    gets(buff.mtext);
    printf("Please enter a number you want to send: \n");
    scanf("%d", &buff.number);
    msgsnd(msgid, &buff, sizeof(buff) - sizeof(buff.mtype), 0);

    return 0;
}

read端:

#define MY_TYPE 9527
#define MY_KEY 1314
int main()
{

    int msgid;

    struct msgbuf
    {
        long mtype;
        char mtext[100];
        int number;
    };

    struct msgbuf buff;
    msgid = msgget(MY_KEY,IPC_CREAT);
    buff.mtype = MY_TYPE;
    while (1)
    {
        printf("Process is waiting for msg: \n");
        msgrcv(msgid, &buff, sizeof(buff) - sizeof(buff.mtype), MY_TYPE, 0);
        printf("Process read from msg: %s, %d \n", buff.mtext, buff.number);
    }
    msgctl(msgid, IPC_RMID, NULL);
    

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值