文件IO知识总结

1、一切皆文件

在linux系统中,内核把一切内容都以文件的形式来表示,“一切皆文件”。所有的设备,所有的内容使用linux创建的虚拟文件系统vfs进行管理,将所有的设备用文件的概念封装起来

在应用中使用统一的接口,实现对底层不同文件类的调用,通过vfs中的file_ opeartion结构体, 提供了一个统一的函数集合,对不同类型文件的支持(某一种文件或设备能够实现的操作都是这个结构体的子集)

内核实现不同类型文件的区别细节,通过内核提供统一接口操作

系统调用:

由内核提供的操作系统的接口函数功能,与内核相关,不同系统可能系统调用不同

库函数调用:

由标准委员会进行设计,只要使用对应的语言就可以使用的函数调用

两种文件操作方式:

既可以使用由操作系统直接提供的系统调用函数接口,也可以使用标准C库提供的库函数调用

在系统调用和C库中,都有一组函数专门用于文件操作的:比如打开文件、关闭文件、读写文件。如果是系统调用----文件IO(系统IO);如果是库函数调用---标准IO

2、标准IO

标准IO是C库的一部分,实际上是对文件IO的封装,提供了更加丰富的操作方式

1、文件打开(fopen)

功能

打开文件,获取文件指针(来操作表示一个打开的文件)

头文件

#include

函数原型

FILE *fopen(const char *pathname, const char *mode);

参数

const char *pathname:指针,字符串的首地址,即将打开的文件名(路径)的文件,字符串表示文件

const char *mode:指针,字符串首地址,字符串表示打开的方式

"r":只读打开文本文件,在文件开始位置进行操作

"r+":读写打开文本文件,在文件开始位置进行操作

"w":只写方式打开文本文件,如果打开文件存在则清空文件,文件不存在则创建文件,在文件开始位置进行操作

"w+":读写方式打开文本文件,如果打开文件存在则清空文件,文件不存在则创建文件,在文件开始位置进行操作

"a":追加写打开本文文件,如果文件不存在就创建,在文件末尾进行操作(定位到文件末尾)

"a+":读追加打开文本文件,如果文件不存在就创建,写在文件末尾进行操作

返回值

FILE *    成功:返回文件指针,打开的文件

失败:NULL

返回的文件指针,是一种FILE结构体的地址(指向FILE类型的指针),FILE结构体就描述打开文件的各种信息

#include <stdio.h>


int main()
{

    //1、打开文件
    FILE * fp = fopen("1.txt","w+");

    //2、读取文件
//    char buf[21];

//    int num = fread(buf,1,20,fp);

//    buf[num] = '\0';

//    printf("%d\n",num);
//    printf("%s",buf);
    char buf[20] = "hello world";
    int num = fwrite(buf,1,20,fp);
    printf("%d\n",num);

    return 0;
}

2、关闭打开的文件(fclose)

功能

关闭打开的文件

头文件

#include

函数原型

int fclose(FILE *stream);

参数

FILE *stream:文件指针,即将关闭的文件

返回值

成功:返回0

失败:返回EOF(-1)

#include <stdio.h>



int main()
{
    FILE * fp;

//    char buf[] = "15.c";
//    fp = fopen(buf,"r");//返回值表示打开的文件信息地址
    fp = fopen("/home/ubuntu/code/io/1.c","rb");//返回值表示打开的文件信息地址

    if(fp == NULL)
    {
        printf("打开失败\n");
        return -1;
    }

    int a;

    a = fclose(fp);
    printf("%d\n",a);

    a = fclose(fp);
    printf("%d\n",a);
    return 0;
}

在程序执行时,默认会为当前程序打开三个文件:

设备

文件指针

文件描述符

标准输入(键盘)

FILE * stdin

0

标准输出(终端显示)

FILE * stdout

1

标准出错输出(终端显示)

FILE * stderr

2

标准IO函数,读写接口非常多:

第一组:每次一个字符的读写标准IO函数接口

fgetc

功能

获取指定文件中的一个字符

头文件

#include

函数原型

int fgetc(FILE *stream);

int getc(FILE *stream);

int getchar(void); == fgetc(stdin)

参数

FILE *stream:文件指针,指定读取字符的文件

返回值

成功:读取到的字符(以int类型返回)

失败:EOF

备注

EOF表示读取到文件末尾或者是读取错误失败

#include <stdio.h>


int main()
{
    //只要执行程序,默认会打开标准输入----stdin,标准输出-----stdout,标准出错----stderr
    //
    //
    char a;
    //a = fgetc(stdin);//获取一个字符,参数为 文件
    a = getchar();
    printf("%c\n",a);

    FILE * fp1 = fopen("1.txt","r");
    if(fp1 == NULL)
    {
        printf("打开失败\n");
        return -1;
    }
//    FILE * fp2 = fopen("1.txt","w");

    //读取
    
    int c;

    while(1)
    {
        c = fgetc(fp1);
        if(c == EOF)
        {
            printf("已经到文件末尾\n");
            break;
        }
        printf("%c",c);
    }

    printf("\n");
    return 0;
}

fputc

功能

将一个字符写入指定的文件

头文件

#include

函数原型

int fputc(int c, FILE *stream);

int putc(int c, FILE *stream);

int putchar(int c);==fputc(c,stdout)

参数

参数1:字符

          int c:要写入的字符

参数2:文件指针

          FILE *stream:要写入的文件

返回值

成功:返回写入的字符的值

失败:返回EOF

#include <stdio.h>


int main()
{

    //1、打开要写入的文件
    FILE * fp = fopen("1.txt","a+");

    char *p = "hello";
    while(*p)
    {
        putchar(*p);
        p++;
    }


    char buf[20] = {0};
    scanf("%s",buf);

    int i = 0;
    while(buf[i])
    {
        if( fputc(*(buf+i),fp) == EOF )
        {
            printf("写入失败\n");
            return -1;
        }
        i++;
    }



    fclose(fp);
    return 0;
}

第二组:每次一行的读写标准IO函数

fgets,gets

功能

从指定文件中读取最多一行数据

函数原型

char *fgets(char *s, int size, FILE *stream);

char *gets(char *s);:从stdin读取,存储到s首地址中

参数

参数1:

        char * s:自定义读取数据的存储空间首地址(从文件中读取的数据存储的地址)

参数2:

        int size:自定义空间的存储空间大小

参数3:

       FILE *stream:即将读取数据的文件指针(从哪个文件中读取)

返回值

成功:返回存储空间首地址s

失败:返回NULL

备注

fgets:由于s首地址存在一段空间,读取存储时不能超过这个空间大小,最多读取size-1

size-1 < 一行====>size-1

size-1 > 一行====>一行

会在读取完的下一个位置添加'\0'

失败NULL:读取错误失败或 当前读取已经在文件末尾读取不到数据

#include <stdio.h>


int main()
{

    char a[15];
//    gets(a);//从stdin读取

    fgets(a,15,stdin);

    printf("%s",a);
/*
    //1、打开文件
    FILE *fp = fopen("1.txt","r+");
    //if(fp == NULL)

    //2、读取
    
    char buf[20];//char * buf = malloc(20);

    char * s = fgets(buf,20,fp);
    if( s == NULL)
    {
        printf("判断当前是否结束\n");
        return -1;
    }

//    printf("%s",buf);
    s = fgets(buf,20,fp);
//    printf("%s",buf);
    s = fgets(buf,20,fp);
    printf("%s",buf);
*/
    return 0;
}

fputs,puts

功能

将数据写入指定文件

头文件

#include

函数原型

int fputs(const char *s, FILE *stream);

int puts(const char *s); == fputs(s,stdout);

参数

参数1:

       const char * s:自定义写入数据,把s首地址的数据写入文件

参数2:

        FILE * stream:文件指针,要写入的文件

返回值

成功:返回非负整数

失败:EOF

备注

s是首地址,把s首地址开始的每一个字符写入文件,s字符串的字符直到为'\0'

#include <stdio.h>



int main()
{

    //fputs("hello",stdout);
    puts("hello");
    FILE *fp = fopen("2.txt","w");

    
    char buf[] = "hello\nnihao\0ok";
    //scanf("%s",buf);

    int err = fputs(buf,fp);
//    if(err == EOF)

    fclose(fp);

    return 0;
}

第三组:每次读写若干数据块的标准IO函数

fread,fwrite

功能

从指定文件中读取若干个数据块

头文件

#include

函数原型

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数

参数1:

        void *ptr:指针,读取数据存放的首地址(自定义的存储位置)

参数2:

        size_t size:整数,要读取每个数据块的大小

参数3:

        size_t nmemb:整数,读取的数据块的个数

参数4:

        FILE *stream:即将被读取数据的文件的文件指针

返回值

成功:读取的数据块的个数

失败:返回0

备注

失败:当前读取已经读取到文件末尾或者读取错误

功能

将若干块数据写入指定的文件

函数原型

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

参数

参数1:

        const void *ptr:指针,要写入的内容数据首地址(把ptr地址中的数据写入文件)

参数2:

        size_t size:整数,要写入每个数据块的大小

参数3:

        size_t nmemb:整数,写入的数据块的个数

参数4:

        FILE *stream:写入的文件的文件指针

返回值

成功:返回写入的数据块的个数

失败:返回0

#include <stdio.h>


int main()
{
    
    FILE * fp = fopen("1.c","r");

    char buf[20];
    int num = 0;
    
    while(1)
    {
        num = fread(buf,1,20,fp);
        if(num == 0)
        {
            if(1 == feof(fp))
            {
                printf("文件末尾\n");
            }
            
            break;
        }
        fwrite(buf,1,num,stdout);
    }

    return 0;
}

因为fread、fwrite是严格按照数据块和数据块大小来处理,而不会对数据格式做处理,数据中的特殊字符'\0','\n'不会受到影响,可以用于二进制文件操作

判断出错或文件末尾

feof

功能

判断是否到达文件末尾

函数原型

int feof(FILE *stream);

参数

参数1:

       FILE * stream:判断的文件

返回值

如果已经到达文件末尾,返回1(真),否则返回0(假)

功能

判断文件操作是否出错

函数原型

int ferror(FILE *stream);

参数

参数1:

       FILE * stream:判断的文件

返回值

如果文件遇到错误,返回1(真),否则返回0(假)

标准IO的缓冲机制:

使用标准IO读写操作,在打开文件时会创建缓冲区,用于读写文件

读:从内核中读取一段数据到缓冲区,应用程序读操作就从缓冲区中读取

写:写入文件时,先把数据写入缓冲区,当缓冲区满足一定条件,才会写入文件

三种缓冲机制:

无缓冲-----stderr

行缓冲:只要在缓冲区遇到 '\n',把缓冲区数据刷新到文件-----stdin、stdout

全缓冲:只要缓冲区满,把缓冲区数据刷新到文件----其他用标准io打开的文件

fflush(文件指针):主动刷新文件缓冲区

3、文件IO

要对文件进行操作,首先要打开文件,然后进行读写操作

系统IO函数:(与操作系统相关)

1、打开文件

打开一个文件意味着获取到这个文件的访问句柄(文件描述符fd,在内核管理的打开文件顺序表的下标)

open

功能

打开一个指定的文件并获得文件描述符,创建一个新文件

头文件

#include

#include

#include

函数原型

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

参数

参数1:

        const char *pathname:指针,字符串的地址,字符串是即将打开的文件

参数2:

        int flags:打开方式

        O_RDONLY:读打开

        O_WRONLY:写打开

        O_RDWR:读写打开----三个是互斥

        O_APPEND:以追加方式打开(必须有写)

        O_CREAT:如果文件不存在,则会创建文件

                            函数需要第三个参数

                            mode参数

                            不使用,可以没有第三个参数

         O_EXCL:与O_CREAT一起使用,如果文件存在,则返回错误信息,用于只创建新文件

          O_NOCTTY:如果文件是终端文件,终端就不能作为当前打开文件的程序的控制终端

          O_TRUNC:如果文件存在,则清空文件

参数3:

         mode_t mode:文件创建的权限

         权限 == mode & ~umask

返回值

成功:返回打开文件的文件描述符(int)

失败:返回-1

备注

flags:打开方式,可以多个合用,如:有读写,追加,创建--->O_RDWR | O_APPEND | O_CREAT

mode:创建权限,以八进制表示,如:0777,0664

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{

    //int fd = open("2.txt",O_RDWR | O_APPEND);
    int fd = open("2.txt",O_RDWR |O_TRUNC | O_CREAT , 0666);


    if(fd < 0)
    {
        printf("打开失败\n");
    }


    printf("%d\n",fd);

    close(fd);

    return 0;
}

0

 

2、关闭文件

close

功能

通过文件描述符,关闭打开的文件

头文件

#include

函数原型

int close(int fd);

参数

参数1:

        int fd:文件描述符,即将要关闭的文件

返回值

成功:返回0

失败:返回-1

3、读写文件

read,write

功能

从指定文件中读取数据

头文件

#include

函数原型

ssize_t read(int fd, void *buf, size_t count);

参数

参数1:

        int fd:文件描述符,指定读取的文件,从fd文件中读取

参数2:

        void * buf:读取的数据存储的地址,自定义存储位置

参数3:

         size_t count:想从文件中读取对应的字节数

返回值

成功:返回实际读取的字节数,0表示当前已经读取到末尾

失败:返回-1

功能

往指定文件中写入数据

头文件

#include

函数原型

ssize_t write(int fd, const void *buf, size_t count);

参数

参数1:

        int fd:要写入的文件,往fd文件中写入

参数2:

        const void *buf:指针,要写入的数据首地址,自定义数据空间,把空间的数据写入

参数3:

         size_t count:要写入的字节数

返回值

成功:返回写入的字节数

失败:返回 -1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    
    //读文件
    int fd = open("2.txt",O_RDONLY);
    
    if(fd < 0)
    {
        printf("打开失败\n");
        return -1;
    }


    //从文件中读取数据
    char buf[20];
    int num = 0;
    while(1)
    {
        num = read(fd,buf,20);
        if(num == 0)
        {
            printf("已经读完\n");
            break;
        }
        //往文件中写数据,1----终端文件
        num = write(1,buf,num);
        if(num < 0)
        {
            printf("出错\n");
            break;
        }
    }

    while(1);

    close(fd);
    close(1);

    return 0;
}

4、文件偏移位置(lseek)

在读写文件的时候,是如何保证每次都是从当前的下一个位置操作,有个偏移量的概念,这个偏移位置,可以获取,也可以人为调整

功能

调整设置文件位置偏移量

头文件

#include

#include

函数原型

off_t lseek(int fd, off_t offset, int whence);

参数

参数1:

        int fd:要调整位置偏移量的文件描述符

参数2:

        off_t offset:新的偏移位置相对基准点的偏移量

参数3:

        int whence:基准点

                             SEEK_SET:文件开始处

                             SEEK_CUR:当前位置

                             SEEK_END:文件末尾处

返回值

成功:返回新位置距离文件开始处的偏移量

失败:返回-1

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


int main()
{

        int fd = open("2.txt",O_RDWR);

        lseek(fd,0,SEEK_END);

        write(fd,"hello world",11);

        lseek(fd,10,SEEK_SET);

        write(fd,"nihao",5);

        close(fd);
        return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

int main()
{

        int fd = open("2.txt",O_RDWR);

        char buf[10];
        int num = 0;

        num = read(fd,buf,10);

            write(1,buf,num);

            num = read(fd,buf,10);
            write(1,buf,num);

        printf("\n");

        //把文件位置偏移到文件开始处
    
        num = lseek(fd,0,SEEK_SET);    
        printf("%d\n",num);

        num = read(fd,buf,10);
        write(1,buf,num);

    
            return 0;
}

5、文件属性(stat)

在操作文件时,经常需要获取文件的属性,比如类型,权限,大小,所有者等等

功能

获取文件的元属性

头文件

#include

#include

#include

函数原型

int stat(const char *pathname, struct stat *statbuf);

参数

参数1:

        const char *pathname:指针,字符串地址,字符串存储文件名

参数2:

        struct stat *statbuf:结构体指针,当调用函数时,把属性存储到这个地址中,属性结构体

返回值

成功:返回0

失败:返回-1

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

int main()
{

    char buf[20] = {0};
    
    fgets(buf,20,stdin);

    int n = strlen(buf);
    printf("%d\n",n);
    buf[n-1] = '\0';

    struct stat s;

    if( stat(buf,&s) < 0)//参数1:文件,参数2:属性
    {
        printf("失败\n");
        return -1;
    }


    printf("inode 号 %ld\n",s.st_ino);
    printf("链接数 %ld\n",s.st_nlink);
    printf("size %ld\n",s.st_size);

    printf("时间 %ld\n",s.st_atime);

    printf("%o\n",s.st_mode & 0170000);
    printf("%o\n",s.st_mode & 0777);

    return 0;
}

属性结构体

struct stat {
    dev_t     st_dev;//文件所在存储器的设备号
    ino_t     st_ino;//inode号,每个文件的唯一编号
    mode_t    st_mode;//文件类型和文件权限
        inode 7
        文件类型:12-15bit
        文件权限:0-8bit         
    nlink_t   st_nlink;//引用计数,链接数
    uid_t     st_uid;//所属用户的id
    gid_t     st_gid;//所属组的id
    dev_t     st_rdev;//特殊文件的设备号
    off_t     st_size;//文件大小
    blksize_t st_blksize;//块大小
    blkcnt_t  st_blocks;//块数

    struct timespec st_atim;//最后访问时间
    struct timespec st_mtim;//最后修改时间
    struct timespec st_ctim;//最后属性修改时间

    #define st_atime st_atim.tv_sec//tv_sec 表示从1970年到当前的秒数    
    #define st_mtime st_mtim.tv_sec
    #define st_ctime st_ctim.tv_sec
};

6、目录操作(opendir)

如何对目录进行操作

功能

打开,存在目录,获取目录指针,可以获取目录项

头文件

#include

#include

函数原型

DIR *opendir(const char *name);

参数

参数1:

         const char *name:指针,字符串地址,要打开的目录

返回值

成功:返回目录指针

失败:返回NULL指针

功能

在打开的目录,读取一个目录项

头文件

#include

函数原型

struct dirent *readdir(DIR *dirp);

参数

参数1:

        DIR *dirp:目录指针,读取目录项的目录指针

返回值

成功:返回目录项结构体(目录中一个文件的基本信息)地址

失败:返回NULL,表示读取失败或读取到目录末尾结束

struct dirent {

               ino_t          d_ino;//inode号

               off_t          d_off;

               unsigned short d_reclen;

               unsigned char  d_type;

               char           d_name[256];//文件名

           };

#include<dirent.h>
#include<stdio.h>
#include<sys/types.h>
#include<string.h>

int main()
{

        DIR *dir = opendir("/home/ubuntu/exercise/wj-IO");

        while(1)
        {
                struct dirent * file = readdir(dir);
                if(file==NULL)
                        break;
                if(strncmp(file->d_name,".",1)==0)
                        continue;
                printf("文件名是:%s\n",file->d_name);
        }
        return 0;
}

4、多文件与库的制作

gcc 编译环境:

gcc编译一个程序的流程:

1、预处理

处理 以# 开头的语句 不进行语法检查

a、展开头文件就是把头文件中的内容复制到.c 文件中

b、处理 宏替换 #define N 10

-E 选项 只进行预处理

-o 选项 输出名字重命名

预处理后的后缀名:.i   表示经过预处理

例:

gcc   xx.c   -E -o xx.i

2、编译  c编译器

将C语言程序  转换为  汇编程序  要进行语法检查

-S将预处理后的文件 进行编译

编译后的后缀名:.s  表示汇编文件

例:

gcc  xx.i  -S  -o xx.s

3、汇编汇编器

将.s 汇编代码  通过汇编器 编译为 机器指令 二进制

-c将目标文件编译为 二进制机器指令

机器指令文件后缀名:.o   表示二进制机器指令码文件

例:

gcc xx.s  -c   -o xx.o

4、链接   链接器

将二进制机器码文件(也可能和库文件) 进行合并链接 形成一个完整的程序文件

例:

gcc xx1.o  xx2.o xx3.o  -o  程序名

1、多文件

在工程程序中,通常一个完整的项目不会使用一个文件来编写所有的操作功能,而是按照分类的思想方式,把相关性的操作写在一个文件中,通过多个文件组合形成一个完整的工程

分别编写多个代码文件

vim  文件1.c文件2.c文件3.c

在进行多文件编译时,在编译步骤中,只有链接步骤才能合并在一起,之前的步骤都是 单个文件独立进行编译,需要对使用的其他文件内容进行声明

通过头文件进行声明,在编译时进行预处理就会把头文件的内容复制到源文件

在编写 源文件时,如果用到了其他文件的内容,需要对其他文件的内容进行声明

头文件格式:

#ifndef X1_H #define X1_H 声明内容 #endif

编译:

gcc  文件1.c  文件2.c  文件3.c ...

2、库的制作

库:从本质上来说是一种可执行代码的二进制格式,可以被载入执行

库分为两种:

静态库

静态库,可以直接在编译时就加入到程序中,进行使用

静态库的制作:

//把程序文件编译为二进制文件,只编译不链接

gcc  -c  xxx.c  -o  xxx.o

 //生成静态库

ar  -cr  libxxx.a   xxx.o

// libxxx.a 就是生成的静态库

使用库:

-L:库的路径

-l(小写L):链接库名

-I(大写 i ):头文件路径(库的头文件)

gcc  main.c 1.c 2.c  +  库文件

在编译我们自己的程序时,添加库

gcc  xx.c xx2.c ...  -L库路径  -lxxx库名  -I头文件

动态库(共享库)

动态库,可以直接在运行时才加入到程序中,进行使用

在编译时,只是将库中的函数的符号表和程序进行编译链接

动态库相比于静态库生成的可执行程序体积小,效率,在允许程序时需要依赖动态库

动态库的制作:

gcc  -fPIC  -shared  xxx.c -o libxxx.so

 -fPIC:忽略文件位置

-shared:共享库

动态库的使用:

编译:

gcc  main.c 1.c 2.c  +  库文件

在编译我们自己的程序时,添加库

gcc  xx.c xx2.c ...  -L库路径  -lxxx库名  -I头文件

运行:

还需要动态库才能允许

指定动态库,在运行时能够找到动态库:

方法1:

将自己的库拷贝到系统的lib'下

cp *.so  /lib

方式2:

通过指定LD_LIBRARY_PATH 临时生效

exportLD_LIBRARY_PATH = /home/xxxxx绝对路径

方式3:

通过系统的配置文件指定库的路径

配置路径:/etc/ld.so.conf.d 目录下,新加文件或在已有文件中添加库的路径

使配置生效

sudoldconfig

5、进程与线程

1、进程

1、进程的概念

一个程序文件,只是一堆待执行的代码和比分待处理的数据,程序只有加载到内存中,然后让cpu逐条执行其代码,才会根据代码内容做出相应的动作,才形成一个活的,动态的进程

进程:正在执行中的程序就叫做进程,进程就是程序的一次执行,是程序执行中动态变化的过程,程序:只是这一系列动作的指令操作文本

进程是动态的,程序是静态的、

进程有执行的概念,而程序没有

进程包含执行时的所有资源,而程序没有

 

进程执行:

既然进程是一个动态的过程,有诞生的时刻,也有死亡的时刻,每一个进程必然有一个创建它的进程(进程执行是由另一个进程创建),有父进程,

init进程系统运行的第一个进程,通过p进程去创建进程的方式来启动进程的执行

在进程中,通过pid号来标识每个执行的进程

查看进程的情况:

ps

top

进程的状态

进程是动态的执行过程,进程在执行时会有很多的运行状态:开始、休眠、等待、暂停、消亡

就绪态:进程在等待队列中排队,占用cpu时间就允许,也可以占用cpu正在运行占用cpu或等待cpu、根据内核的各种调度方式,选择就绪的进程进入cpu执行,执行时间片对应的时间

僵尸态:程序进程已经执行结束,不在执行,资源等待回收,进程结束信息都封存在状态信息中

死亡态:父进程进行回收子进程的资源,查看子进程的结束信息当父进程已经消亡,子进程被收养,由收养的进程来回收

睡眠态:处于就绪态,但是缺少某些资源不可操作,会睡眠等待,当有对应资源时就可执行,

暂停/停止态:当进程收到暂停信号时,进程的执行就被停止,此时就叫做暂停态,在该状态下进程不参与调度,但是系统资源不会释放,放在内核的等待队列,当有激活信号(SIGCONT),从等待队列移动到就绪队列重新参与调度

 

0

2、进程相关操作函数

子进程创建(fork)

功能

程序创建一个新的进程(执行函数就额外创建一个进程执行),在创建子进程时,会拷贝父进程的所有资源内容到子进程中,用于子进程执行,子进程从fork()的返回开始执行

头文件

#include

#include

函数原型

pid_t fork(void);

返回值

父进程:返回 子进程的pid

子进程:返回 0

失败:返回-1

fork函数作用创建子进程,把进程内容完全复制一份给子进程进行执行

fork()成功,分别会在父进程和子进程返回值,但是值不同,可以根据返回值设置,子进程和父进程执行特定的代码内容

if(pid > 0)

if(pid == 0)

子进程会从fork()执行的下一句

父子进程是相互平等,执行的次序是随机的,父子进程是相互独立的,子进程完全拷贝父进程的内存空间,子进程有自己的内存空间,内存是独立开,互不影响

进程的关闭

结束进程:(exit)

1、主函数return 结束进程

2、给进程发送结束信号

kill   发送信号

3、调用进程结束函数

功能

结束当前进程,退出本进程

头文件

#include

#include

函数原型

void exit(int status);

void _exit(int status);

参数

参数1:

       int status:整数 & 0xff 作为当前进程的结束状态

备注

在进程的任意位置调用都会结束当前进程

结束状态:正常结束 :0

                  异常结束:非0

exit:在结束时会刷新缓冲区

_exit:在结束时不会刷新缓冲区直接结束

status参数:在退出进程时,进程的结束状态,这个值存在于pcb,等待父进程的回收

父进程等待子进程结束回收子进程的资状态源

wait,waitpid

功能

阻塞等待任意一个子进程结束,回收子进程资源。获取子进程的的结束状态

头文件

#include

#include

函数原型

pid_t wait(int *wstatus);

参数

参数1:

        int *wstatus:指针,地址,接收子进程状态的地址(获取状态存入地址)

返回值

成功:返回等待回收到的子进程的pid

失败:返回-1

功能

等待指定的一个子进程结束,获取结束状态

头文件

#include

#include

函数原型

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数

参数1:

       pid_t pid:子进程pid

                    < -1,表示等待组id的绝对值为pid的进程的任意子进程结束

                    ==-1,表示等待任意子进程结束

                    ==0,表示当前进程组,任意子进程结束

                    >0,表示等待 pid的子进程结束

参数2:

         int *wstatus:地址,用于存储状态

参数3:

          int options:选项

                   WNOHANG:非阻塞,查看当前子进程是否结束,结束就回收,没结束不会等待

                    0:阻塞

返回值

成功:返回子进程pid

           如果是非阻塞且没有子进程结束返回0

失败:返回-1

#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>

int main()
{
    char src[20]="";
    char dest[20]="";

    printf("请输入文件名1:");
    scanf("%s",src);

    printf("请输入文件名2:");
    scanf("%s",dest);

    //获取文件1大小
    //打开文件1
    int src_fd = open(src,O_RDONLY);

    //获取文件大小
    int size = lseek(src_fd,0,SEEK_END);

    //文件2是空洞文件
    int dest_fd = open(dest,O_WRONLY|O_CREAT|O_TRUNC,0664);

    //空洞文件
    lseek(dest_fd,size-1,SEEK_SET);

    write(dest_fd,"\0",1);

    lseek(src_fd,0,SEEK_SET);
    lseek(dest_fd,0,SEEK_SET);
    char buf[20];
    int s=size/2;
    while(s>0)
    {
        size = read(src_fd,buf,20);
        //if(size==0)
        //{
        //    break;
        //}
        write(dest_fd,buf,size);
        s--;
    }

    lseek(src_fd,s,SEEK_SET);
        lseek(dest_fd,s,SEEK_SET);
    pid_t pid=fork();
    if(pid==0)
    {
        char buf[20];
        sleep(3);
        while(1)
        {
            size = read(src_fd,buf,20);
            if(size==0)
            {
                break;
            }
            write(dest_fd,buf,size);
        }

    }

    close(src_fd);
    close(dest_fd);

    return 0;
}
        

fork创建子进程,子进程执行内容与父进程完全一致,只能在程序中,根据pid值选择执行

改变进程执行内容,替换为其他程序,作为进程执行

execl

功能

在进程中,替换原进程内容,在进程改为新程序执行

头文件

#include

函数原型

int execl(const char *pathname, const char arg, .../ (char  *) NULL */);

参数

参数1:

       const char *pathname:要替换的程序的路径

参数2-n:可变参数,字符串,用于表示程序执行的参数

参数2:

        const char *arg:程序路径名

参数3:

         const char *arg:程序名的 参数

........

参数n:

         NULL:表示参数结束

返回值

失败:返回-1

#include <stdio.h>
#include <unistd.h>


int main()
{

    printf("hello world\n");
    
    //替换当前进程的内容,把其他程序的内容拿到当前进程执行
    
    //execl("/bin/ls","/bin/ls","-a","-l",NULL);

    //execl("./copy","./copy",NULL);


        pid_t pid = fork();
        if(pid == 0)
        {
            
            //int x = execl("/bin","/bin",NULL);
            int x = execl("/bin/ls","/bin/ls",NULL);

            if(x < 0)
            {
                printf("error\n");
            }
    
            exit(0);
        }

        wait(NULL);
        printf("end\n");

    return 0;
}

3、守护进程

1、交互进程:进程的执行有交互过程,如终端进程

2、批处理进程:指定顺序,依次执行多个进程内容,如shell脚本

3、守护进程(精灵进程):随着系统启动而启动执行,系统关闭结束才结束进程

周期性的执行某项任务或者等待某个事件产生的进程,它的运行不依赖任何shell终端,它的生存周期较长,从开机运行到关机结束

进程组:

linux进程以组为单位,在进程组中的第一个进程进程组组长,管理整个进程组

会话:

管理一个或多个进程组,在会话的第一个进程组,就是会话的组长,关闭整个会话时,会把会话中的所有进程都结束关闭

创建守护进程:

1、创建子进程,退出父进程

pid_t pid = fork();

if(pid > 0)

exit(0);

2、在子进程中创建新的会话

setsid();

3、改变子进程的工作目录

chdir("");

4、修改子进程的文件操作掩码

umask()

5、关闭所有的文件描述符

//获取最大的打开的文件描述符+1

int s = getdtablesize()

close(0 ~ s-1)

//以1s为单位,把时间写入文件
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>

int main()
{
    //1、创建子进程,退出父进程
    
    pid_t pid = fork();
    if(pid > 0)
    {
        exit(0);
    }

    //2、创建新会话,设置子进程为新会话组长,脱离终端
    
    setsid();

    //3、切换守护(子)进程的工作目录
    chdir("/");

    //4、在子进程中取消文件权限掩码
    umask(0);

    //5、关闭文件描述父
    //获取文件描述符最大值
    int size = getdtablesize();

    for(int i = 0;i < size;i++)
    {
        close(i);
    }


    //打开文件
    int fd = open("log.txt",O_WRONLY|O_APPEND|O_CREAT,0777);

    while(1)
    {
        sleep(1);
        //时间
        time_t t = time(NULL);
        //转换的时间字符串
        char * c = ctime(&t);

        write(fd,c,strlen(c));
    }


    return 0;
}

4、进程间通信

每个进程都有自己运行的所有资源,每个进行都是独立的相互不干扰的,每个进程都是使用自己的资源空间

1、管道

在内核的内存空间设置一个能够传输数据的通道,要进行通信的进程通过文件IO的操作方式,操作这个通道,进行数据传输

要进行通信的进程,一个去创建管道,另外的就连接到管道,进行通信

无名管道(pipe)

管道没有名字标识,无法在之后找到管道文件

无名管道只能用于父子进程间通信

(因为子进程会复制父进程的所有资源)

创建无名管道(pipe)

功能

创建打开无名管道文件,得到管道的文件描述符,操作管道文件

头文件

#include

函数原型

int pipe(int pipefd[2]);

参数

参数1:

        int pipefd[2]:数组地址,用于存储创建打开的文件描述符

#include <stdio.h>
#include <unistd.h>


int main()
{
    //创建无名管道文件

    int fds[2];
    pipe(fds);//fds[0]读,fds[1]写

    pid_t pid = fork();
    
    if(pid > 0)
    {
        while(1)
        {
            char buf[20];
            fgets(buf,20,stdin);
    
            //写入到管道中
            write(fds[1],buf,20);
        }
    }
    else if(pid == 0)
    {
        while(1)
        {
            char bufc[20];
            //从管道中读取
            read(fds[0],bufc,20);
            printf("child:");
            fputs(bufc,stdout);
        }
    }
}
有名管道

管道有名字标识,创建有名管道后,在对应的目录中存在对应名字的管道文件

进程打开管道文件后,在内存空间中使用管道文件(管道文件大小永远为0)

对有名管道的使用:就是文件的操作

操作有名管道:

1、打开有名管道文件

open();运行读或写

2、从管道中读取或往管道中写入,按照文件io操作

read()、write()

3、关闭管道文件

close();

#include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{

    //打开管道文件
    int fd = open("./fifo",O_WRONLY);


    //往管道中写内容
    while(1)
    {
        char buf[20];
        fgets(buf,20,stdin);
        int size = strlen(buf);
        buf[size-1] = '\0';//把\n 变为\0
        //写管道
        write(fd,buf,size);
        if(strcmp(buf,"quit") == 0)
        {
            break;
        }
    }
    close(fd);

    return 0;
}
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
/*
void sigcall(int sig)
{
    read();

}
*/

int main()
{

    //打开管道文件
    int fd = open("./fifo",O_RDONLY);

//    signal(10,sigcall);

    while(1)
    {
        //从管道文件中读取
        char buf[20];
        int num = read(fd,buf,20);
        if(strcmp(buf,"quit") == 0)
        {
            break;
        }
        printf("size is %d,data is %s\n",num,buf);
    }

    //关闭管道
    close(fd);

    return 0;
}
创建管道文件:(mkfifo)

功能

创建一个有名管道文件

函数原型

int mkfifo(const char *pathname, mode_t mode);

参数:

参数1:

       const char *pathname:创建管道文件的路径名

参数2:

        mode_t mode:创建管道文件的读写权限

返回值

成功:返回0

失败:返回-1

mkfifo xxxx

2、信号

信号是一种通知,而不是携带数据进行传输,给指定的进程产生一个通知信号

对于linux内核,支持的信号

 

内核中的信号,都有一个设定好的默认操作,当进程接收到信号后可以执行默认操作

17:忽略

18:恢复运行

19:暂停信号。系统的暂停信号

20:暂停信号。由控制终端发起的暂停信号

21:暂停

22:暂停

23:忽略

28:忽略

对应进程而言,当进程接收到信号后,可以支持三种操作:

1、执行该信号的缺省动作(内核规定的信号操作)

2、进程对该信号设置为忽略,丢弃该信号(接收到信号什么都不做):SIGSTOP和SIGKILL是特殊信号不能忽略

3、进程设置捕获信号,进程中接收到该信号后,按照进程的设定操作进行

当向进程发送信号时,进程就会接收到对应的信号,进行处理

信号操作:

1、发送信号(kill)

功能

给指定进程发送信号

头文件

#include

#include

函数原型

int kill(pid_t pid, int sig);

参数

参数1:

        pid_t pid:发送给哪个pid进程

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

                   ==0:发送给当前进程同一个进程组的所有进程

                   -1:发送给能够发送的所有进程

                   < -1:发送信号给组id == |pid|的进程组

参数2:

        int sig:发送的信号

返回值

成功:返回 0

失败:返回-1

#include <stdio.h>

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



int main()
{
    
    int pid;
    int sig;

    while(1)
    {
    printf("请输入要发送信号的进程,以及信号:");
    scanf("%d,%d",&pid,&sig);

    if( kill(pid,sig) < 0 )
    {
        printf("发送失败\n");
    }
    else
        printf("发送成功\n");
    
    }

    return 0;
}
2、进程接收信号的处理signal

功能

设置进程捕获信号之后的处理方式

头文件

#include

函数原型

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

参数

参数1:信号

        int signum:设定哪个信号的处理方式

参数2:信号的处理方式

        sighandler_t handler:函数指针,函数地址,当之后捕获到对应的信号后,调用哪个函数来处理

                   SIG_IGN:对信号进行忽略

                   SIG_DFL:对信号进行默认操作

                   handler:对信号调用哪个函数处理

返回值

成功:返回参数二的值

失败:返回SIG_ERR

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

void signal_callback(int sig)//sig 就是当前捕获的信号20
{
    if(sig == 10)
        printf("hello world\n");
    else if(sig == 20)
        printf("nihao\n");
}

int main()
{

    printf("pid is %d\n",getpid());
    printf("parent pid is %d\n",getppid());
        
    if( signal(10,signal_callback) == SIG_ERR )
    {
        printf("出错\n");
    }

    signal(20,signal_callback);

    while(1);

    return 0;
}
3、给当前进程发送信号(raise)

功能

给当前调用的进程发送指定的信号

头文件

#include

函数原型

int raise(int sig);

参数

参数1:

        int  sig:发送的信号

返回值

成功:返回0

失败:返回非0

#include <signal.h>
#include <stdio.h>


int main()
{
    printf("hello world\n");

    raise(19);

    printf("hello world\n");
    sleep(1);

    return 0;
}
4、挂起进程等待信号(pause)

功能

阻塞挂起睡眠等待,直到接收到信号

头文件

#include

函数原型

int pause(void);

返回值

失败:返回-1

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void sig_call(int sig)
{
    printf("hello\n");
}

int main()
{

    printf("start\n");
    signal(20,sig_call);
    printf("wait\n");

    pause();
    printf("end\n");
    return 0;
}
3、共享内存---IPC对象(sytemV)

在内核空间中,申请一段多个进程都可以访问的内存空间,多个进程找到这个空间之后,都可以取访问

操作功能:

1、创建/打开共享内存(shmget)

int shmget(key_t key, size_t size, int shmflg);

功能

创建或打开指定key 的共享内存

头文件

#include

#include

函数原型

int shmget(key_t key, size_t size, int shmflg);

参数

参数1:

       key_t key:要创建或打开的共享内存的键值

参数2:

       size_t size:共享内存的大小

参数3:

        int shmflag:共享内存选项

                             IPC_CREAT:如果key对应的共享内存不存在,就创建

                             mode:共享内存的访问权限(如:0664)

返回值

成功:返回共享内存的id,在进程中表示打开的共享内存

失败:返回-1

2、映射共享内存到进程中(shmat)

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能

映射共享内存的地址到进程

头文件

#include

#include

函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数

参数1:

        int  shmid:要映射的共享内存id

参数2:

        const void * shmaddr:指针,地址

                   不为NULL(地址):表示把共享内存映射到这个地址(根据shmaddr来选择映射区域)

                   NULL:系统会自动选择一个地址区域进行映射

参数3:

          int shmflg:映射选项

                          SHM_RDONLY:以只读方式映射共享内存

                           0:读写

返回值

成功:返回 映射的地址

失败:返回(void *)-1

3、进行通信(往共享内存写,从共享内存读)
4、解除共享内存映射(shmdt)

int shmdt(const void *shmaddr)

功能

解除已经映射的共享内存

头文件

#include

#include

函数原型

int shmdt(const void *shmaddr)

参数

参数1:

       const void * shmaddr:要解除的映射的共享内存地址

返回值

成功:返回0

失败:返回-1

5、删除共享内存(shmctl)

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能

控制管理共享内存

头文件

#include

#include

函数原型

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

参数1:

        int shmid:要操作的共享内存id

参数2:

         int cmd:操作方式

                     IPC_SET:设置共享内存属性

                     IPC_STAT:获取共享内存属性

                     IPC_RMID:删除共享内存

参数3:

         struct shmid_ds *buf:属性地址

          设置时,就是把这个地址的属性写入共享内存

          获取时,就是把共享内存的属性添加到这个地址中

//往共享内存写
#include <sys/ipc.h>
       #include <sys/shm.h>
 #include <sys/types.h>
#include <stdio.h>

int main()
{
/*
       #include <sys/types.h>
       #include <sys/ipc.h>
    计算key 值
       key_t ftok(const char *pathname, int proj_id);
    

*/

    key_t key = ftok("/home",10);//通过路径与整数,来计算一个key

    //1、创建打开共享内存
    
    int shmid = shmget(key,1024,IPC_CREAT|0664);
    //if(shmid < 0){}

    //2、映射映射内存地址到进程(之后操作这个地址就是操作共享内存)
    
    int * addr = shmat(shmid,NULL,0);

    //3、进行通信,往共享内存写数据
    
    while(1)
    {
        //*addr = 10;
        //*(addr+1) = 20;
        scanf("%d",addr);
        if(*addr == 0)
        {
            break;
        }
    }

    //4、解除映射的共享内存
    shmdt(addr);

    //5、关闭共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}
//从共享内存读
#include <sys/ipc.h>
       #include <sys/shm.h>
 #include <sys/types.h>
#include <stdio.h>

int main()
{
/*
       #include <sys/types.h>
       #include <sys/ipc.h>
    计算key 值
       key_t ftok(const char *pathname, int proj_id);
*/

    key_t key = ftok("/home",10);//通过路径与整数,来计算一个key

    //1、创建打开共享内存
    
    int shmid = shmget(key,1024,IPC_CREAT|0664);
    //if(shmid < 0){}

    //2、映射映射内存地址到进程(之后操作这个地址就是操作共享内存)
    
    int * addr = shmat(shmid,NULL,0);

    //3、进行通信,往共享内存写数据
    
    sleep(5);

    while(1)
    {
        //*addr = 10;
        //*(addr+1) = 20;
        printf("%d\n",*addr);
        if(*addr == 0)
        {
            break;
        }
        sleep(1);
    }

    //4、解除映射的共享内存
    shmdt(addr);

    //5、关闭共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}
4、消息队列---IPC对象

消息队列是在内核中的一种特殊通信对象,多个进程都可以访问队列,进行通信,消息队列中的传输的消息数据携带了类型

类型用于标识当前消息可以被谁读取

消息队列的操作:

1、创建/打开消息队列msgget()

功能

创建打开消息队列,获取消息队列id

头文件

#include

#include

#include

函数原型

int msgget(key_t key, int msgflg);

参数

参数1:

        key_t key:创建打开消息队列的键值

参数2:

        int msgflg:选项

                      IPC_CREAT:不存在则创建

                      mode:打开权限,0664

返回值

成功:返回消息队列id

失败:返回-1

2、发送、接收消息msgsnd()

功能

往消息队列中添加消息

头文件

#include

#include

#include

函数原型

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数

参数1:

        int msgid:要发送消息的消息队列

参数2:

        const void * msgp:要发送消息的地址,把这个地址中的消息发送(消息队列)

                消息结构体:

                struct    msgdata

                 {

                         long  type;//必须包含,消息蕾西

                         data;//消息正文

                  };

参数3:

         size_t  msgsz:消息正文长度

                   sizoef(struct msgdata) - sizeof(long)

参数4:

        int  msgflg:选项

                   0:阻塞

                   IPC_NOWAIT:非阻塞

返回值

成功:返回0

失败:返回-1

msgrcv()

功能

从消息队列读取接收消息

头文件

#include

#include

#include

函数原型

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

参数

参数1:

        int msgid:要读取的消息队列id

参数2:

        void * msgp:读取消息后存储的地址,包含类型与正文的首地址

参数3:

        size_t size:正文的大小

参数4:

        long  msgtype:指定获取哪种类型的消息

                  >0:获取指定类型消息

                 ==0:获取消息队列中第一个消息不管类型

参数5:

        int msgflg:选项

                  0:阻塞读取

                  IPC_NOWAIT:非阻塞

返回值

成功:返回读取正文大小

失败:返回-1

3、关闭消息队列msgctl()

功能

操作消息队列

头文件

#include

#include

#include

函数原型

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数

参数1:

        int msgid:要操作哪个消息队列

参数2:

        int cmd:功能选择

                   IPC_STAT:拷贝消息队列属性到,参数3的地址中

                   IPC_SET:把参数3地址中属性,设置到消息队列

                   IPC_RMID:删除消息队列,参数3:NULL

#include <stdio.h>
#include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
#include <string.h>

struct msg
{
    long type;
    char buf[20];
};

int main()
{

    //1、创建消息队列
    
    int msgid = msgget(ftok("/home/ubuntu",1110),IPC_CREAT|0664);


    //2、发送消息
    while(1)
    {
        struct msg m = {0};

        scanf("%ld",&m.type);
        scanf("%s",m.buf);
        msgsnd(msgid,&m,sizeof(m)-sizeof(m.type),0);
    
        if(strcmp(m.buf,"quit") == 0)
            break;
    }

    //3、删除消息队列
    msgctl(msgid,IPC_RMID,NULL);

    return 0;
}
#include <stdio.h>
#include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
#include <string.h>

struct msg
{
    long type;
    char buf[20];
};

int main()
{

    //1、创建消息队列
    
    int msgid = msgget(ftok("/home/ubuntu",1110),IPC_CREAT|0664);


    //2、接收消息
    while(1)
    {
        long type;
        struct msg m = {0};
        scanf("%ld",&type);

        msgrcv(msgid,&m,sizeof(m)-sizeof(m.type),type,0);
        printf("type is %ld,data is %s\n",m.type,m.buf);

        if(strcmp(m.buf,"quit") == 0)
            break;
    }

    //3、删除消息队列
    msgctl(msgid,IPC_RMID,NULL);

    return 0;
}
5、信号量---IPC对象

信号灯/信号量,不是用于传输数据的,而是用于标识一些特性信号的,用来协调各个进程进行工作

通过信号量,控制进程之间执行的顺序

p操作:申请资源

先-1,然后执行

while(1)

{

if(xxx >0)

{xxx - 1;break;}

}

v操作:释放资源

先做,然后+1

......

xxx+1

信号量通信操作:

1、创建/打开信号量semget()

功能

创建/打开信号量

头文件

#include

#include

#include

函数原型

int semget(key_t key, int nsems, int semflg);

参数

参数1:

        key_t key:创建打开信号量的键值

参数2:

         int nsems:在信号量中,元素个数(信号量的个数)

参数3:

         int semflg:信号量选项

                        IPC_CREAT:没有key信号量,就创建

                        mode:打开权限 ,0664

返回值

成功:返回信号量id

失败:返回-1

2、信号量操作 p、v操作 semop()

功能

对信号量进行P/V操作

头文件

#include

#include

#include

函数原型

int semop(int semid, struct sembuf *sops, size_t nsops);

参数

参数1:

        int semid:操作的信号量

参数2:

        struct sembuf * sops:操作信号量结构体地址,在结构体中包含操作方式,可以操作多个元素

                      sem_num:操作第几个信号灯元素

                      sem_op:做什么操作

                               -1:减一

                                1:加一

                                0:一直等待信号量变为0

                      sem_flg:是否阻塞

                                0:阻塞等待

                                IPC_NOWAIT:非阻塞

参数3:

        size_t nsops:操作信号量中的几个元素

返回值

成功:返回0

失败:返回-1

3、关闭删除信号量 semctl()

功能

控制信号量,管理信号量

头文件

#include

#include

#include

函数原型

int semctl(int semid, int semnum, int cmd, ...);

参数

参数1:

        int semid:要管理操作哪个信号量

参数2:

        int semnum:要操作管理信号量哪个元素

参数3:

        int  cmd:操作命令

                    IPC_STAT:获取信号量的属性

                                 参数4:属性结构体的地址,把信号量中获取的存储到地址中

                    IPC_SET:设置信号量的属性

                                  参数4:属性结构的地址,把结构体的属性内容设置到信号中

                    IPC_RMID:删除信号量,不需要第4个参数

                    SETVAL:设置信号量中指定元素的值

                                  参数4:整数,指

//往共享内存写
#include <sys/ipc.h>
       #include <sys/shm.h>
 #include <sys/types.h>
#include <stdio.h>
#include <sys/sem.h>

int main()
{
/*
       #include <sys/types.h>
       #include <sys/ipc.h>
    计算key 值
       key_t ftok(const char *pathname, int proj_id);
    

*/

    key_t key = ftok("/home",10);//通过路径与整数,来计算一个key

    //1、创建打开共享内存
    
    int shmid = shmget(key,1024,IPC_CREAT|0664);
    //if(shmid < 0){}

    //2、映射映射内存地址到进程(之后操作这个地址就是操作共享内存)
    
    int * addr = shmat(shmid,NULL,0);

    //3、进行通信,往共享内存写数据

    //信号量操作
    //1、创建信号量
    int semid = semget(key,1,IPC_CREAT|0664);
    //初始化信号量的元素值
    semctl(semid,0,SETVAL,0);

    while(1)
    {
        //*addr = 10;
        //*(addr+1) = 20;
        scanf("%d",addr);
        //2、进行释放资源操作---信号量的v操作
        struct sembuf buf;
        buf.sem_num = 0;//第0个元素
        buf.sem_op = 1;//+1,v
        buf.sem_flg = 0;//阻塞
        //执行信号量p、v操作
        semop(semid,&buf,1);
        
        if(*addr == 0)
        {
            break;
        }
    }

    //3、关闭删除信号量
    semctl(semid,0,IPC_RMID);


    //4、解除映射的共享内存
    shmdt(addr);

    //5、关闭共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}
//往共享内存读
#include <sys/ipc.h>
       #include <sys/shm.h>
 #include <sys/types.h>
#include <stdio.h>
#include <sys/sem.h>

int main()
{
/*
       #include <sys/types.h>
       #include <sys/ipc.h>
    计算key 值
       key_t ftok(const char *pathname, int proj_id);
    

*/

    key_t key = ftok("/home",10);//通过路径与整数,来计算一个key

    //1、创建打开共享内存
    
    int shmid = shmget(key,1024,IPC_CREAT|0664);
    //if(shmid < 0){}

    //2、映射映射内存地址到进程(之后操作这个地址就是操作共享内存)
    
    int * addr = shmat(shmid,NULL,0);

    //3、进行通信,往共享内存写数据

    //信号量操作
    //1、创建或打开信号量
    int semid = semget(key,1,IPC_CREAT|0664);

    while(1)
    {
        
        //等待信号量
        //完成p操作,申请资源 , -1
        struct sembuf buf;
        buf.sem_num = 0;
        buf.sem_op = -1;
        buf.sem_flg = 0;
        semop(semid,&buf,1);
        
        printf("%d\n",*addr);
        if(*addr == 0)
            break;
    }

    //3、关闭删除信号量
    semctl(semid,0,IPC_RMID);


    //4、解除映射的共享内存
    shmdt(addr);

    //5、关闭共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}

2、线程

由于进程的资源都是独立的,每个进程都有自己的一份资源,相互没有影响

线程:在一个进程中的多个任务,这多个任务可以共享进程中的共享资源,在进程中彼此之间共享一套资源的任务叫做线程

线程和进程一样也是独立参与系统调度

线程也叫做轻量级进程

创建线程:pthread_create();

功能

在进程中创建一个新线程(任务)

头文件

#include

函数原型

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

void *(*start_routine) (void *), void *arg);

参数

参数1:

       pthread_t * thread:线程id地址,当创建新线程后,把线程id存储到这个地址

参数2:

      const pthread_attr_t * attr:创建线程时,设置线程的属性

              NULL:设置为默认属性

参数3:

       void * (* start_routine)(void *):函数指针,线程执行的任务函数

参数4:

       void * arg:线程函数的参数

返回值

成功:返回0

失败:返回错误码

线程结束:

1、线程函数结束,则线程结束

2、调用pthread_exit(结束状态)

功能

结束线程,同时返回线程的结束状态

头文件

#include

函数原型

void pthread_exit(void *retval);

参数

参数1:

        void * retval:结束状态,把地址作为结束状态

等待线程结束 pthread_join()

功能

等待指定线程结束,回收线程资源,获取结束状态

头文件

#include

函数原型

int pthread_join(pthread_t thread, void **retval);

参数

参数1:

       pthread_t  thread:指定线程id,等待哪个线程结束

参数2:

       void ** retval:线程结束状态存储的地址,把结束状态一级指针,存储到哪个地址

返回值

成功:返回0

失败:返回错误码

取消线程:

pthread_cancel()

#include <stdio.h>
#include <pthread.h>

void * print(void * arg)
{
    while(1)
    {
        printf("i = %d\n",*(int *)arg);
        sleep(1);
    }

    return NULL;
}


int main()
{

    int i = 1;
    
    pthread_t tid;
    pthread_create(&tid,NULL,print,&i);
    
    while(1)
    {
        sleep(1);
        i++;
    
        if(i == 50)
        {
            pthread_cancel(tid);
            break;
        }
    }

    return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

void * pthread_start(void * arg)
{//arg ==NULL

    int i = 0;

    char * p = "hello world\n";
    while(1)
    {
        printf("hello\n");
        sleep(1);
        if(i++ == 10)
            pthread_exit(p);
        //return NULL;
    }

    return NULL;//结束状态
}

int main()
{

    //创建一个线程
    pthread_t tid;
    pthread_create(&tid,NULL,pthread_start,NULL);

    //主线程
    //
    //等待子线程结束,才开始打印
    void * reval;
    pthread_join(tid,&reval);//pthread_join(NULL);
    printf("%s\n",(char *)reval);

    while(1)
    {
        printf("nihao\n");
        sleep(1);
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

(*~.@天蓝@.~*)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值