①Linux简明系统编程(嵌入式公众号的课)---总课时12h

10.09
注意:这个是Linux高级编程的简明教程,是Linux应用程序的开发,而不是底层程序的开发。

内容是关于操作系统网络编程的吗?

Linux简明系统编程

  • 〇、课程思维导图
  • 〇、会用到的头文件
  • 〇、视频课+参考笔记
  • 一、任务、程序、进程、线程概念和区别
    • 第1节课:程序进程线程概念、进程ID号
      • 1.程序、进程、线程的概念
      • 2.进程号pid
      • 3.查看进程号的两个函数:getpid() 和 getppid()
      • 4.显示进程树:pstree -p
        • systemd(1):进程号为1,它是所有进程的父进程
  • 二、进程及多进程编程
    • 第2、3节课:进程创建函数fork()
      • fork函数之后父子进程谁先执行?
      • 子进程和父进程之间是相互独立的,互不干扰
    • 第4节课:监控子进程函数wait()
  • 三、线程及多线程编程
    • 第5节课:创建线程函数pthread_create()
    • 第6节课:多线程及线程间数据共享
  • 四、任务间通信与同步(7种方式)
    • ☆☆☆①任务间的通信 之 管道pipe
    • 第7、8、9、10节课:无名管道、测试无名管道大小、练习、两条管道双向传输
      • 7.无名管道
      • 8.如何测试无名管道大小?
      • 9.练习:子进程通过键盘输入内容并写入管道,父进程读取管道里的内容并打印
      • 10.两条管道双向传输
    • 第11节课:有名管道(命名管道)---mkfifo函数
    • ☆☆☆②任务间的通信 之 共享内存 shared memory
    • 第12节课:共享内存 shared memory
    • 第13节课:非亲缘关系进程通过共享内存通信
    • ☆☆☆③任务间的通信 之 消息队列message queue
    • 第14节课:消息队列message queue
    • 第15节课:非亲缘关系进程通过消息队列通信
      • 补充:创建键值key的函数:ftok()函数
    • ☆☆☆④任务间的同步 之 信号量semaphore
    • 第16节课:无名信号量
    • 第17节课:命名信号量
    • 第18节课:线程间信号量同步
    • ☆☆☆⑤任务间的同步 之 互斥锁mutex
    • 第19节课:互斥锁(常用在线程间的同步)
    • ☆☆☆⑥内核和应用进程间/进程和进程间 传递的控制命令 之 信号signal
    • 第20节课:信号signal
    • ☆☆☆⑦socket套接字
  • 五、Linux网络编程

〇、课程思维导图

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第1张图片

〇、会用到的头文件

#include//fork函数、wait函数、mkfifo函数(有名管道)、msgget函数(消息队列)、kill函数(发送信号signal)
#include//fork函数、pipe函数
#include//mkfifo函数(有名管道)、sem_open函数
#include//wait函数、waitpid函数
#include//pthread_create函数(创建线程)、几个互斥锁函数pthread_mutex_init函数、pthread_mutex_lock函数、pthread_mutex_unlock函数
#include//read、open、write、close文件、sem_open函数
#include//shmget函数
#include//shmget函数
#include//msgget函数
#include//msgget函数
#include//sem_init函数、sem_wait函数、sem_post函数、sem_open函数、sem_close函数
#include//mmap函数
#include//kill函数(发送信号signal)、signal函数()
#include
using namespace std;

int main(){
   
	...
    return 0; 
}

〇、视频课+参考笔记

视频课就来自于嵌入式技术公开课公众号的老师讲的一个课程,名称就叫Linux简明系统编程
这个CSDN专栏写的很好:linux系统编程。

一、任务、程序、进程、线程概念和区别

第1节课:程序进程线程概念、进程ID号

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

程序:

  • 源代码,指令,程序是静态的概念,比如一个安装包,就存放在电脑磁盘里,不进行任何操作

进程:

  • 正在执行的程序的实例,是程序的动态的概念,比如qq和微信是两个独立的进程。进程的创建和销毁会带来很大的资源消耗;每个进程都有独立的进程号(相当于一个编号,套接字Socket=(IP地址:端口号))
    进程之间是相互独立的。

线程:

  • 线程从属于进程,一个进程可以有多个线程线程之间共享进程的资源

任务:

  • 具体要做的事情。

2.进程号pid

每个进程都有一个进程号,叫pid(process id),

man getpid //查询手册

3.查看进程号的两个函数:getpid() 和 getppid()

两个函数的参数都是空,返回值为pid_t类型。
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第2张图片

4.显示进程树:pstree -p

-p表示pid,即进程号。

pstree -p
systemd(1):进程号为1,它是所有进程的父进程

systemd(1):进程号为1,它是所有进程的父进程
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第3张图片

二、进程及多进程编程

第2、3节课:进程创建函数fork()

fork()函数:返回值依然是pid_t类型。

man fork

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第4张图片
通过复制当前的进程,创建一个新进程,新的进程是当前进程的子进程。

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第5张图片

示例:

#include
#include
#include
using namespace std;

int main(){
   

    pid_t pid;
    pid = fork();
    cout << "pid = " << pid << endl;
    cout << "hello, world" << endl;
    return 0; 
}

结果:

//父进程返回的内容:
pid = 1200482
hello, world
//子进程返回的内容:
pid = 0
hello, world

解释:
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第6张图片
父进程返回的是子进程的PID,子进程返回0;
所以说上面的1200482是新创建的子进程的PID,这个信息是由父进程来返回的。

程序修改一下:

#include
#include
#include
using namespace std;

int main(){
   

    // pid_t pid;
    // pid = fork();
    // cout << "pid = " << pid << endl;
    // cout << "hello, world" << endl;

    pid_t pid1, pid2;
    pid1 = fork();
    pid2 = fork();
    cout << "pid1 = " << pid1 << "; pid2 = " << pid2 << endl;

    return 0; 
}

结果会是什么呢?

pid1 = 0; pid2 = 0 //进程D(拷贝进程B,所以和B的pid1相同)
pid1 = 0; pid2 = 1204505 //进程B
pid1 = 1204503; pid2 = 1204504 //进程A
pid1 = 1204503; pid2 = 0 //进程C(拷贝进程A,所以和A的pid1相同)

进程A的pid未知;进程B的pid为1204503;
进程C的pid为1204504;进程D的pid为1204505;
解释:
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第7张图片
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第8张图片

以下内容参考链接:https://www.csdn.net/tags/OtDakg2sMTU0OTItYmxvZwO0O0OO0O0O.html
程序1:

#include
#include
#include
using namespace std;

int main(){
   

    pid_t pid;
    pid = getpid();
    fork();
    cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
	//pid为父进程的进程ID号,而getpid()是获取当前进程的ID号。
    return 0; 
}

结果:

pid = 1336259; getpid() = 1336259 //父进程
pid = 1336259; getpid() = 1336264 //子进程

由于fork函数创建了一个新的子进程,所以fork函数后的语句会执行两次,也就是打印两次进程的pid号。因为cout语句第一次运行在父进程,所以两个pid的值相等,但cout语句第二次运行在子进程,所以两个pid就不相等了,也就是说此时的pid2是子进程的进程ID。

程序2:

#include
#include
#include
using namespace std;

int main(){
   

    pid_t pid;
    pid = getpid();
    cout << "before fork, pid = " << getpid() << endl;
    fork();
    cout << "after fork, pid = " << getpid() << endl;
    //cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
    if(getpid() == pid) cout << "这是父进程,此时的pid = " << getpid() << endl;
    else cout << "这是子进程,此时的pid = " << getpid() << endl;

	return 0;
}

结果:


before fork, pid = 1356246
after fork, pid = 1356251
这是子进程,此时的pid = 1356251
after fork, pid = 1356246
这是父进程,此时的pid = 1356246

程序3:fork函数的返回值

#include
#include
#include
using namespace std;

int main(){
   

    pid_t pid;
    //pid = getpid();
    cout << "before fork, pid = " << getpid() << endl;
    pid = fork();
    //cout << "after fork, pid = " << getpid() << endl;
    //cout << "pid = " << pid << "; getpid() = " << getpid() << endl;
    if(pid == 0) cout << "这是子进程,此时的pid = " << getpid() << endl;
    else cout << "这是父进程,此时的pid = " << getpid() << endl;

	return 0;
}

结果:

before fork, pid = 1356723
这是子进程,此时的pid = 1356728
这是父进程,此时的pid = 1356723

参考链接2:
这篇讲的也很好,可以直接看这篇博客 linux中fork函数及子进程父进程执行顺序

参考链接3:
操作系统fork()进程

    pid_t pid;
    pid = fork();
    cout << "pid = " << pid << endl;

fork是返回两个值:
一个代表父进程:代表父进程的值是一串数字,这串数字是子进程的ID(地址);
一个代表子进程:返回值为0。

多次手动运行这个程序,会发现以下两个结果:

//第一种结果:
pid = 1358110
pid = 0

//第二种结果:
pid = 0
pid = 1358828

说明fork函数之后先运行父进程还是子进程,是不确定的,父子进程在争用系统资源,看谁先执行。

参考链接4:
fork() && fork() || fork();会产生几个子进程
关键在下面的理解:
①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第9张图片

fork函数之后父子进程谁先执行?

上面的参考链接3中的程序:
注意:不能通过判断谁先打印就说谁先执行,因为打印函数本来就是个很复杂的过程,并不能说先打印出父进程的pid就说先执行的父进程

宏观上来说是同时执行;
微观上来说是交替进行的;
这就是并发;计算机操作系统笔记—并行和并发的区别

Linux2.6之后默认是父进程先调用,因为父进程一直处于活跃状态;并且父子进程谁先执行带来的影响几乎可以忽略不计,如果一定要先让谁运行,后让谁运行,这就涉及到后面要说的同步问题,同步就是让程序按照人为设定的顺序去执行

问:执行一次fork函数 先运行子进程还是先运行父进程?
答:(闫波)
没有顺序的
为了可以同步(同步就是有顺序),所以引入了信号量之类的东西;
执行同步就是让程序以一个确认的顺序运行,其实自己项目多线程用的多,包括咱们科研的,进程环境切换代价太大了,一般都是多线程
现在电脑也是,8核16线程这种,线程用的多,高并发也是线程
参考链接:Linux C++多线程同步的四种方式

子进程和父进程之间是相互独立的,互不干扰

让子进程和父进程都执行一个for循环,子进程执行20次,父进程执行10次

#include
#include
#include
using namespace std;

int main(){
   

    pid_t pid;
    int count = 0;
    pid = fork();
    if(pid == 0){
   
        for(int i = 0; i < 20; ++i){
   //while(1)
            ++count;
            cout << "子进程:执行次数" << count << endl;
            sleep(2);
        }
    }
    else if(pid > 0){
   
        for(int i = 0; i < 10; ++i){
   //while(1)
            ++count;
            cout << "父进程:执行次数" << count << endl;
            sleep(2);
        }  
    }
    else
        perror("fork");

    return 0; 
}

结果是父子进程各自值行自己的++countcout操作,所以父子进程之间是相互独立的,互不影响;
另外当父进程执行10次之后,子进程会继续执行,直到打印完20次才停止,也就是说父进程的结束并不会导致子进程也停止

注意,不要写上面的那个while(1),那样会导致子进程根本停不下来,Ctrl + C也没用。

第4节课:监控子进程函数wait()

man 2 wait 

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第10张图片
所有这些系统调用都用于等待调用进程的子进程的状态更改并获取状态已更改的子进程的信息
wait()系统调用暂停调用线程的执行,直到它的一个子线程终止。
waitpid()系统调用暂停调用线程的执行,直到pid参数指定的子线程改变状态。默认情况下,waitpid()只等待终止的子进程。
返回值:
wait():如果成功,返回被终止子进程的pid;发生错误时,返回-1
waitpid():如果成功,返回状态改变的子进程的pid;如果指定了WNOHANG,并且pid指定的一个或多个子进程(ren)已经存在,但是还没有改变状态,则返回0。发生错误时,返回-1。

示例:
创建三个子进程,分别在5秒、10秒、15秒之后结束,父进程一直等待,直到所有子进程都运行结束,父进程才停止运行,在这个过程中,父进程一直监控几个子进程的运行状态。

代码:

#include
#include
#include
#include
using namespace std;

int main(){
   
    int arr[4] = {
   0, 10, 5, 15};
    pid_t child_pid;

    for(int i = 1; i <= 3; ++i){
   
        switch (fork())
        {
   
        case -1:
            perror("fork");
            exit(0);//break;
        case 0:
            cout << "子进程 " << i << " 已创建,pid = " << getpid() << ",ppid = " << getppid() << ",sleeping 时长为 " << arr[i] << "秒。" << endl;
            sleep(arr[i]);
            exit(0);//break;
        default:
            break;
        }
    }
    int numDead = 0;
    while(true){
   
        child_pid = wait(nullptr);//等待子进程结束
        if(child_pid < 0){
   
            cout << "子进程都结束了,再见!" << endl;
            break;
        }
        ++numDead;
        cout << "wait()函数返回了 pid = " << child_pid << "的子进程,它是第 " << numDead << " 个结束的子进程;" << endl;
    }

    return 0;
}

结果:

子进程 1 已创建,pid = 4168680,ppid = 4168679,sleeping 时长为 10秒。
子进程 2 已创建,pid = 4168681,ppid = 4168679,sleeping 时长为 5秒。
子进程 3 已创建,pid = 4168682,ppid = 4168679,sleeping 时长为 15秒。
wait()函数返回了 pid = 4168681的子进程,它是第 1 个结束的子进程;
wait()函数返回了 pid = 4168680的子进程,它是第 2 个结束的子进程;
wait()函数返回了 pid = 4168682的子进程,它是第 3 个结束的子进程;
子进程都结束了,再见!

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第11张图片

三、线程及多线程编程

第5节课:创建线程函数pthread_create()

程序:源代码,指令,程序是静态的概念,比如一个安装包,就存放在电脑磁盘里,不进行任何操作

进程:正在执行的程序的实例,是程序的动态的概念,比如qq和微信是两个独立的进程。
1.进程的创建和销毁会带来很大的资源消耗
2.每个进程都有独立的进程号PID
3.注意:进程之间是相互独立的
4.注意:套接字中的端口号进程号不是一回事;
套接字Socket = (IP地址:端口号)
端口号和进程号的关系:

线程:线程从属于进程,一个进程可以有多个线程,线程之间共享进程的资源

max pthread_create

①Linux简明系统编程(嵌入式公众号的课)---总课时12h_第12张图片

头文件:

#include

四个参数:

pthread_t *threak
//pthread_t* 类型,表示线程ID号,这里写某个pthread_t类型变量的地址
const pthread_attr_t *attr,
//线程结构体指针类型,一般写null
void *(*start routine)(void *)
//函数指针类型,函数是指某个线程函数,函数的参数是void*,返回值也是void*,这里写函数的地址,即函数名
void *arg
//传递给线程函数的参数,一般写null

返回值:int类型,如果成功创建了一个线程,就返回0;否则返回一个错误的数字。

示例:

#include
#include
#include
#include
using namespace std;

//线程函数:
void* thread_func(void* arg){
   
    return nullptr;
}
int main(){
   

    pthread_t pthread;//线程ID号
    int res;

    res = pthread_create(&pthread, nullptr, thread_func, 

你可能感兴趣的:(linux,c++,运维)