IO进程线程

本文介绍了Linux系统中的文件操作,包括文件IO和标准IO的使用,如fopen、fclose等函数。还讨论了进程的概念,如进程的创建、状态和通信方式,以及线程的创建和同步机制。此外,提到了进程间通信的几种方式,如管道、信号和共享内存。最后,简述了信号在进程间的应用以及如何创建和管理共享内存和消息队列。
摘要由CSDN通过智能技术生成

##文件IO和标准IO

对一个文件有两种操作方式

  1. 文件IO---(linux系统提供)

系统调用:当我们的应用程序要使用一些底层功能时,不应自行访问底层,而应该向操作系统发出请求

特点:1.不带缓冲区 2.操作系统直接提供的函数接口 3.调用系统调用是很耗费资源的

  1. 标准IO----(C库提供)

C库函数:在系统调用接口之上封装的接口,一个C库函数可以封装多个系统调用函数

特点:1.增强了代码的可移植性,复用性 2.提高了效率 (标准IO增加了一个缓冲机制)

一.标准IO

函数:fopen/fclose/fgetc/fputc/fgets/fputs/fread/fwrite

fopen:用于打开文件, 其调用格式为

FILE *fp = fopen(路径名,操作模式)
例如:FILE *fp = fopen("./1/txt","r");

FILE:系统自动为使用的文件在内存中开辟一片空间,存储文件的详细信息,这个控件类型为FILE结构体类型,该结构体由系统设计。

FILE *:流指针,在标准IO中,每次成功打开一个文件,都会返回一个流指针,这个流指针就描述了一个文件,所有的标准IO都围绕流指针来进行。


fclose(fp);

关闭文件的目的:1.防止其他进程操作这个文件 2.释放结构体占用的资源

每一个终端都是一个文件:ptx/xxx 这个就是终端对应的文件,这个文件名字以数字命名且存储在:/dev/pts/xxx 这些文件由linux系统自动创建。当打开一个终端时,会重建一个新的文件与之对应stdin、stdout、stderr都是指向同一个文件(终端文件)


freopen:以指定模式重新指定到另一个文件,模式用于指定新文件的访问方式。(例如可以把在终端上打印的东西传到另一个路径文件中)

FILE *fp = freopen(路径名,操作模式,流指针)
功能:改变流指针的指向
例如:FILE *fp = freopen("./1.txt","r",FILE *stream);

fgetc:从输入流中获取下一个字符,并将文件指针加1

fgetc(FILE *filename)
功能:读取一个字节的数据,并将文件指针加1,且有了缓冲区
例如:fgetc(fp);

fputc:写一个字节到文件流中

fputc(输入的字符,FILE *stream)
功能:写入一个字节数据
例如:fputc('c',fp)

注意:使用fputc就有了缓冲区

fgets:从 指针流中读取 sizeof个字符存储到字符指针变量所指向的内存空间。

返回值:它的返回值是一个指针,指向字符串中第一个字符的地址。

fgets(内存空间首地址,sizeof个大小的指针变量字符,标准输入流 stdin,也可以是文件流)
例如:char *p = fgets(buf, sizeof(buf), fp);

fputs:把字符串或数组写入指定的stream(即指向文件的指针)中,但不包括空字符。

返回值:函数返回一个非负值。

fputs(数组,FILE *stream)
例如:fputs(str,sp);

fseek:重定位文件指针

fseek(文件指针,偏移量,移到的位置)
例如:fseek(fp,0,SEEK_SET);//移动到文件最开头
fseek(fp,-50,SEEK_END);//从文件末尾向前偏移50字节处
fseek(fp,50,SEEK_CUR);//从当前指针位置向后偏移50字节处

perror:将上一个函数错误的原因输出,

fwrite:向指定文件中写入数据

size_t fwrite(const void *str,size_t size,size_t nmemb,FILE *stream)
str:内存地址
size:字节数
nmemb:块数
stream:流指针
成功返回块数,失败返回-1
例如:fwrite(&a,sizeof(a),1,fp)意思是向取a中的地址向fp所指向的文件中写入一块sizeof(a)大小的数据

fread:从一个文件流中读取数据读取size*nmemb个字节数据,并将它们替换到buf(缓冲区)所指定的数组中,函数的返回值就是读取的数据量

size_t fread(void *str,size_t size,size_t nmemb,FILE *stream)
成功返回块数
str是读取的数据存放的内存的指针,可以是数组,也可以是新开辟的空间,str就是一个索引。
例如:fread(buf,100,10,fp)从

sprintf:按照格式把数据放在内存中(数据->内存

int sprintf(char *str,const char *format,...)
sprintf(buf,%d-%d-%d,2023,2,15);

fprintf:按照格式把数据放在流指针指向的文件中(数据->流指针指向的文件

int fprintf(FILE *stream,const char *format,....)
stream:流指针
format:格式控制符
....:不定参数
例如:fprintf(fp,%d-%d-%d,2023,2,15);

fscanf:把文件中的数据提取到不定参数中(文件->不定参数)

int fscanf(FILE *stream,const char *format,....)
例如:fscanf(fp,%d-%d-%d,&a,&b,&c)

sscanf:把内存中的数据提取到不定参数中(内存->参数

int sscanf(const char *str,const char *format,...)
例如:sscanf(buf,%d-%d-%d,&a,&b,&c);

##查单词

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

int main()
{
    FILE *fp = fopen("./dict.txt","r");//找到单词文件
    if(NULL  == fp){
        perror("fopen");
        return -1;
        }
    char buf[32] = {0};//接受单词
    char str[1000] = {0};//定位单词
        
    while(1)
{
        printf("please input word:");
        scanf("%s",buf);//输入单词
        while(1)
        {
            char *p = fgets(str,sizeof(str),fp);//定义一个指针获取单词表
             if(NULL == p){
                   perror("fgets");
                    printf("no such word\n");
                    break;//如果没有输入的单词,则跳出
                }
                if(strncmp(p,buf,strlen(buf)) == 0 && p[strlen(buf)+1] ==' ' )//判断条件
                    {
                            printf("%s\n",p);//打印此单词
                             break;
                     }
                    memset(str,0,sizeof(str);//清空单词表,因为p指向的文件中指针不在开头也就是里面有数,所有要清空
        }
                rewind(fp);//将文件指针置为开头
}
        fclose(fp);
        return 0;

}

time时间相关函数

#include <time.h>

#include <unistd.h>

time_t t;//初始化t

time_t time(time_t *t):从1970-1-1 0:0:0开始到现在的秒数

int n = time(NULL or &t);//返回值是秒数,从1970年开始

time_t t; time(&t);


struct tm *localtime(const time_t *timep):从1970-1-1 0:0:0开始到现在的秒数转换成日历

struct tm *p = localtime(&t);
struct tm{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
}

p->tm_year + 1900; //从1900年开始
p->tm_mon + 1;//从0月开始
p->tm_hour;
p->tm_mday;
p->tm_sec;

char *q = ctime(&t);
功能:获取现在时间
printf("%s\n",q);

文件IO:系统调用函数

无缓存

文件描述符 :表示打开文件的符号,每打开一个文件 系统会自动分配一个fd

非负整数一般最大打开的文件描述符数量:0~1023

0:stdin 1: stdout 2:stderr

fdopen:将文件IO转换成标准IO,转换成对应文件指针返回。

FILE *fdopen(int fd,const char *mode)
参数:fd 文件描述符  mode 打开方式
FILE *fp = fdopen(1,"w")用写的方式以输出流方式指向文件

文件IO函数 (open/close/read/write/lseek)

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>


open

int fd = open("文件路径名",打开方式)//文件已经存在
int fd = open("文件路径名",打开方式,文件权限)//文件不存在,则创建
例如: int fd = open("./",O_RDONLY,0666);

打开方式:O_CREAT 创建 O_RDONLY只读 O_WRONLY可写

O_RDWR读写 O_TRUNC清零 O_ APPEND追加

标准IO->文件IO

r ->O_RDONLY w+ ->O_RDWR | O_CREAT | O_TRUNC

r+ ->O_RDWR a ->O_WRONLY | O_CREAT | O_APPEND

w ->O_RDWR a+ ->O_RDWR | O_CREAT | O_APPEND

实际创建的文件权限需要经过一个公式计算得到:mode & (~umask)

mode:文件权限:0666

成功返回:文件描述符fd


close

#include <unistd.h>

int close(int fd);

write

ssize_t write(int fd,const void *buf,size_t count);
功能:把内存中的数据写入到文件中

read

ssize_t read(int fd,void *buf,size_t count);
功能:从文件中读取count字节的数据到内存

lseek

off_t lseek(int fd,off_t offset,int whence);
功能:定位文件指针
offset:偏移量
whence:基点
SEEK_SET:文件开头
SEEK_SET:文件末尾
SEEK_SET:文件当前位置
例如:lseek(fd,0,SEEK_END);

目录操作函数

opendir

#include<sys/types.h>

#include<dirent.h>

opendir:打开目录

DIR *opendir(const char *name);
功能:打开目录
name:目录的路径名
成功返回:DIR*
DIR *k = opendir("");

readdir:读取目录信息

struct dirent *readdir(DIR *dirp);
功能:读取目录信息
dirp:opendir的返回值
返回成功: struct dirent * 失败:NULL(一直遍历到目录完成)
DIR *k = opendir("");
struct dirent *p = readdir(k);
struct dirent{
 d_ino; /* inode number 索引节点号 */
   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
    unsigned short d_reclen; /* length of this d_name 文件名长 */
    unsigned char d_type; /* the type of d_name 文件类型 */
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */

}

closedir:关闭目录

int closedir(DIR *dirp);
DIR *k = opendir("");
struct dirent *p = readdir(k);
closedir(k);

chdir:用户将当前的工作目录改变成以参数路径所指的目录

int chdir(const char * path);

文件信息函数

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

stat

struct stat mybuf:_stat函数用来获取指定路径的文件或者文件夹的信息。

struct stat mybuf;
int stat(const char *path,struct stat *buf);
int stat("文件路径名",结构体地址&mybuf)
struct stat{

    dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/  
    ino_t       st_ino;     /* inode number -inode节点号*/    
    mode_t      st_mode;    文件的权限、类型 
    nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/    
    uid_t       st_uid;     所属用户ID  
    gid_t       st_gid;    所属组ID    
    dev_t       st_rdev;    /* device ID (if special file) -设备号,针对设备文件*/    
    off_t       st_size;    /* total size, in bytes -文件大小,字节为单位*/    
    blksize_t   st_blksize; /* blocksize for filesystem I/O -系统块的大小*/    
    blkcnt_t    st_blocks;  /* number of blocks allocated -文件所占块数*/    
    time_t      st_atime;    最后一次访问时间
    time_t      st_mtime;   最后一次修改时间
    time_t      st_ctime;   最后一次文件属性修改时间

}

getpwuid

struct passwd *getpwuid(uid_t uid):通过用户uid查找用户的相关信息,并以passwd的结构体返回其数据

struct passwd *getpwuid(uid_t uid);
struct passwd{
 
    char *pw_name;                /* 用户登录名 */
      char *pw_passwd;              /* 密码(加密后) */
      __uid_t pw_uid;               /* 用户ID */
      __gid_t pw_gid;               /* 组ID */
      char *pw_gecos;               /* 详细用户名 */
      char *pw_dir;                 /* 用户目录 */
      char *pw_shell;               /* Shell程序名 */
    }

getgrgid

struct group *getgrgid(gid_t gid):获取用户组的相关信息

struct group *getgrgid(gid_t gid);
struct group{
  
char *gr_name;                  /* 组名 */
  char *gr_passwd;              /* 密码 */
  __gid_t gr_gid;                  /* 组ID */
  char **gr_mem;                  /* 组成员名单 */
}

文件类型:

{

常规文件:S_ISREG ‘-’

目录:S_ISDIR 'd'

字符设备:S_ISCHR 'c'

块设备:S_ISBLK 'b'

管道:S_ISFIFO 'p'

套接字:S_ISSOCK 's'

符号链接:S_ISLNK 'i'

}


##实现ls-s功能
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <time.h>
#include <dirent.h>
#include <grp.h>

int main()
{
           DIR *p_dir = opendir("./");//打开当前路径目录
           if(NULL == p_dir)
            {
                   perror("opendir");
                    return -1;
            }
            struct dirent *rid_dir = p_dir;//读取目录信息,dirent型结构体指针指向打开的目录文件
            struct group *gr_gid = NULL;//定义一个group型指针,用来接收用户组信息
            struct passwd *pw_uid = NULL;//定义一个passwd型指针,用来接收用户信息
            struct tm *dir_time = NULL; //定义一个tm型指针,用于读取最后一次时间
                while(1)
{
  //1.判断隐藏文件     
     if(strncmp(rid_dir->d_name,'.',1) == 0)//d_name是文件名,rid_dir读到的文件判断,隐藏文件则跳出本次循环
             {
                    continue;
             }    
             else if(rid_dir == NULL){
                    break;
              }
  //2.获取文件信息
               struct stat mybuf;//定义一个stat结构体型
               stat(rid_dir->d_name,&mybuf);//获取每一个文件的信息
  //3.判断文件的类型
               if(S_ISREG(mybuf.st_mode))//S_ISREG是常规文件的意思不是函数,mybuf.st_mode是在stat结构体中获取每一个文件的类型
                  {
                        printf("-");//判断普通文件
                  }

                   。。。。。。依次执行判断完文件类型

  //3.打印出文件权限
                    printf("%c",mybuf.st_mode & 0x1<<8?'r':'-');//把1左移几位判断该位置是否有权限
                    printf("%c",mybuf.st_mode & 0x1<<7?'w':'-');
                    printf("%c",mybuf.st_mode & 0x1<<6?'x':'-');
                    printf("%c",mybuf.st_mode & 0x1<<5?'r':'-');
                    printf("%c",mybuf.st_mode & 0x1<<4?'w':'-');
                    printf("%c",mybuf.st_mode & 0x1<<3?'x':'-');
                    printf("%c",mybuf.st_mode & 0x1<<2?'r':'-');
                    printf("%c",mybuf.st_mode & 0x1<<1?'w':'-');
                    printf("%c",mybuf.st_mode & 0x1<<0?'x':'-');
    //4.打印硬链接
                    printf("%d",mybuf.st_nlink);//打印文件个数即硬链接数
    //5.获取用户组和用户id
                    gr_gid = getgrgid(mybuf.st_gid);//获取用户组id
                    pw_uid = getpwuid(mybuf.st_uid);//获取用户id
    //6.求文件大小
                    printf("%ld",sizeof(mybuf.st_size));//求文件大小
    //7.获取时间
                    dir_time = localtime(&mybuf.st_ctime);//获取文件属性最后一次修改时间
                    printf("%d月",r->tm_mon +1);//获取月数
                    printf("%d日",r->tm_mday);//获取日
                    printf("%d:%d",r->tm_hour,r->tm_min);//获取时和分
                    printf("%s\n",rid_dir->d_name);//打印文件名
}

            closedir(p_dir);//关闭目录文件

            return 0;

}

##库的制作

静态库*.a

  1. 静态库的制作和使用

制作:$gcc -c xxx.c -o xxx.o

$ar -crs libxxx.a xxx.o

静态库的命名规范:

必须以lib开头,紧跟库的名字,跟扩展名.a 例如:libxxx.a

使用:

$gcc main.c -L路径 -lxxx -L指定静态库所在目录 -l指定静态库的名字 xxx部分

运行:$./a.out

优点:a.out运行后不需要库,程序在编译链接的时候把库的代码复制到可执行文件当中的,可以直接运行

缺点:每个a.out都需要包含库,体积大,耗资源,对程序更新,部署,发布带来麻烦。


动态库*.so

动态库的制作和使用

制作:

$gcc -fPIC -c xxx.c -o xxx.o

$gcc -shared -o libxxx.so xxx.o

动态库的命名规范:

必须以lib开头,紧跟库的名字,跟扩展名.so 例如:libxxx.so

使用:

$gcc main.c -L路径 -lxxx

$ldd a.out #用于查看可执行程序以来的动态库有哪些

运行:

$./a.out #会报错

动态库的搜索方式:

1.将动态库拷贝到/lib/或者/usr/lib/

$sudo cp libxxx.so /lib/

  1. export LD_LIBRARY_PATH=.或者so所在的路径(临时情况)

特点:在编译时不会链接到可执行文件中,只是在其中保存一个索引,在运行时,才真正的链接(动态),因此可执行程序体积小。

优点:a.out体积小,节省资源

缺点:a.out运行后需要库,不能直接运行


##进程

  1. 进程

进程是一个程序的依次执行的过程

每一个进程都分配一个虚拟的4G (0-3G:用户 3G-4G:内核)

  1. 进程和程序的区别

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

  1. 进程的内存管理

正文段 用户数据段 系统数据段

  1. 进程号PID

唯一的标识一个过程

  1. 进程的类型

交互进程(ctrl+z/jobs-l/bg/fg/kill-l/kill-9 PID/ps -ajx)

ctrl+z:把前台进程转成后台

jobs -l:查看作业

bg %作业号:把后台作业重新放在前台,且不能ctrl+c结束

fg %作业号:把后台作业重新放在前台,能被ctrl+c结束

kill -l:查看信号种类

kill-9 PID:杀死该进程

ps -ajx:查看进程

  1. 进程的运行状态

ps -ajx查看

运行态R:此时进程或者正在进行或准备运行

内核调度程序到CPU上执行running

等待态:此时进程需要满足一些条件,如果不满足就等待

可中断S:如果进程收到信号会醒来ctrl+c

不可中断D:如果进程收到信号不会醒来

停止态T:此时进程被中止SIGSTOP

死亡态Z:已终止的进程(僵尸进程)

但还在进程向量数组中占有一个task_struct结构

task_struct{
        pid_t pid;
        R;
}

<高优先级

N低优先级

L有些页被锁进内存

s会话组组长

+位于前台的进程组

I多线程,克隆线程


##进程相关函数

fork/【exit库函数有缓存,会刷新缓存区】(_exit系统调用无缓冲,不会刷新)

创建进程/退出进程

#include <unistd.h>
pid_t pid;//进程号
pid = fork();//分配进程
if(pid < 0){
    perror("fork");
    return -1;//创建进程失败
}
if(pid == 0){//子进程创建成功
    int n = 5;
    while(n--)
    {
        printf("son is running/n");
        sleep(1);    
    }
        exit(0);//结束进程,返回0
}
   else if(pid > 0){//创建父进程成功
     int status;//用来返回子进程结束的状态,父进程通过status可以得到一些子进程结束时的状态信息,一般为NULL
   
       wait(&status);//父进程等待子进程结束回收它的资源
       printf("%d",WEXITSTATUS(status));//获取子进程返回值
        printf("%d",WIFEXITED(status));//判断子进程是否正常结束
        
        while(1)
        {
            printf("father is running\n");
            sleep(1);
        }    
        return 0;
}

wait/waitpid

回收进程资源

pid_t pid;//进程号
pid = fork();//分配进程
if(pid < 0){
    perror("fork");
    return -1;//创建进程失败
}
if(pid == 0){//子进程创建成功
    int n = 5;
    while(n--)
    {
        printf("son is running/n");
        sleep(1);    
    }
        exit(0);//结束进程,返回0
}
   else if(pid > 0){//创建父进程成功
     int status;//用来返回子进程结束的状态,父进程通过status可以得到一些子进程结束时的状态信息,一般为NULL
         
            waitpid(-1,NULL,WNOHANG);//父进程自动回收子进程结束后的资源

        while(1)
        {
            printf("father is running\n");
            sleep(1);
        }    
        return 0;

#include<sys/types.h>

#include <sys/wait.h>

pid_t wait(int *status);

功能:父进程等待子进程结束,回收它的资源

参数:status:一般为NULL

WEXITSTATUS(status) 获取子进程返回值 WIFEXITED(status) 判断子进程是否正常结束


pid_t waitpid(pid_t pid,ing *status,int options);

功能:父进程自动回收子进程结束后的资源

参数:pid:-1 任意进程 status:NULL

options:0 阻塞(即waitpid(-1,NULL,0) == wait(NULL)) WNOHANG 非阻塞


子进程先于父进程退出--父进程为回收资源--子进程会变成僵尸进程

危害:占用进程号、内存空间、PCB进程控制块等

解决:wait/waitpid

注意:任何进程结束都会变成僵尸进程,只是时间有长有短

父进程先于子进程退出--子进程会变成孤儿进程--被init进程接管

init进程:系统启动后运行的第一个用户空间进程,pid=1,会定期扫描系统,收养孤儿进程

注:孤儿进程一般没什么危害


##exec函数族

  1. 概念:函数族提供了一种在进程中启动另一个程序执行的方法,它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,再执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是linux下任何可执行脚本文件。

#include <unistd.h>
int execl(const char *path,const char *arg,...);
功能:进程重载0-3G的用户空间,可与fork函数搭配使用
参数:int execl("绝对路径","标识符(可以理解成文件名)",不定参数(一般为NULL));
int execv(const char *path,char *const argv[]);
int execlp(const char *file,const char *arg,...);
int execvp(const char *file,char *const argv[]);
int execle(const char *path,const char *arg,...,char *const envp[]);
int execve(const char *path,char *const argv[],char *const envp[]);

##守护进程

  1. 守护进程

在linux中与用户交互的界面叫终端,从终端运行起来的程序都依附于这个终端,当终端关闭时,相应的进程都会被关闭,守护进程可以突破这个限制。

  1. 特点

在后台服务的进程

生存期很长

守护进程独立于控制终端

  1. 守护进程创建流程

创建子进程,父进程退出 fork(void);

在子进程中创建新会话setsid(void);

修改工作目录chdir("");

修改umask,增加安全性 umask();

关闭文件描述,即回收资源 close();


##线程

线程函数

#include <pthread.h>

pthread_t tid;//线程号

int pthread_create(pthpread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);
功能:创建线程
参数:
    thread: &tid  线程号的地址
    attr: NULL 线程函数默认的属性
    start_routine: 线程函数的函数名
    arg: 给线程函数传递的参数NULL
例如:pthread_t tid;//初始化线程号
     int ret = pthread_create(&tid,NULL,fun,NULL);

int pthread_join(pthread_t thread,void **retval)
功能:主线程等待子线程结束,回收资源(阻塞)
参数:
thread : tid
retval : NULL (接受子线程结束的返回值)
返回值: 成功0  失败-1
例如:pthread_join(tid,NULL);
int pthread_detach(pthread_t thread);
功能:主线程和子线程分离开来,系统自动回收资源(非阻塞)
参数:
thread : tid
返回值: 成功0   失败-1


sem_t sem;//初始化信号量

int sem_init(sem_t *sem,int pshared,unsignd value)
功能:初始化信号量
参数:
sem : &sem取地址
pshared : 0:线程 1:进程
value : 初值 0 or 1
例如:sem_init(&sem,0,0);
int sem_wait(sem_t *sem);//相当于value-1
int sem_post(sem_t *sem);//相当于value+1

pthread_mytex_mutex;//互斥锁

pthread_mutex_init(&mutex,NULL);//初始化
pthread_mutex_lock(&mutex);//上锁
pthread_mutex_unlock(&mutex);//解锁

##传统的进程间通信

  1. 无名管道

查看命令: man 2 pipe

头文件:#include <unistd.h>

函数原型: int pipe(int pipefd[2])

pipefd[2]:无名管道的两个文件描述符,int型数组,大小为2,pipefd[0]为读端,pipefd[1]为写端

无名管道的特点:

  1. 没有名字,所以无法用open()打开

  1. 只能用于亲缘进程间(如父子进程、兄弟进程、祖孙进程等)通信

  1. 半双工工作模式,读写端是分开的,pipefd[0]为读端,pipefd[1]为写端,无名管道两端每一端都可以读和写

  1. 是一种特殊的文件,只存在内存中,由内核进行管理

  1. 对于它的读写可以以使用文件IO如read、write函数

  1. 无名管道的操作属于一次性操作,如果对无名管道进行读操作,数据会全被读走

注意事项:

  1. 当管道中无数据时,执行读操作,读操作阻塞

  1. 无名管道的大小是固定的,管道一旦满,写操作就会导致进程阻塞

  1. 对无名管道的操作,类似一个队列,后写入的数据不会覆盖之前的数据,会在其后面存储,读取完的数据会从管道里面移除。

  1. 将读端关闭,向无名管道中写数据,管道破裂,进程收到信号(SIGPIPE),默认这个信号会将进程杀死

  1. 当管道中有数据,将写端关闭,读操作可以执行,之后数据读完,可以继续读取(非阻塞),直接返回0


单工:固定一种方向通信,如广播

半双工:同一时间只能由一端发送到另一端

全双工:通信方向都可以,同时可以发送也可以接受电话


2.有名管道

有名管道也叫命名管道,在文件系统目录中存在一个管道文件

管道文件仅仅是文件系统中标识,并不在磁盘上占据空间。在使用时,内存上开辟空间,作为两个进程数据交互通道

查看命令:man 3 mkfifo

头文件:#include <sus/types.h> #include <sys/stat.h>

函数原型: int mkfifo(const char*pathname,mode_t mode);

 int mkfifo(const char*pathname,mode_t mode);
功能:创建有名管道
参数:
pathname:文件路径名
mode:权限
在Shell中使用mkfifo命令:mkfifo filename创建管道

特点:

1.有名管道存在文件系统中,数据存在内存中

2.可以用于无亲缘关系的进程

3.只有读端和写端同时存在管道才能成功打开

数据传输特点:

  1. 读端不存在时,写端写入数据将会阻塞

  1. 读端意外结束,写端在写数据将会管道破裂,该进程结束

  1. 有名管道数据存储在内存中,数据交互在内核中


有名管道和无名管道的异同点:

  1. 相同点:

open打开管道文件以后,在内存中开辟了一块空间,管道内容在内存中存放,有两个指针——头指针(指向写的位置)和尾指针(指向读的位置)指向它。读写数据都是在给内存的操作,并且都是半双工通讯。

  1. 区别

有名在任意进程之间使用,无名在父子进程之间使用


##信号

简单概念:信号在软件层次上对中断机制的一种模拟

kill把信号发送给进程或进程组;raise把信号发送给进程自身。

#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);  raise(signo) == kill(getpid(),signo);//等价于

alarm();//设置闹钟

#include <unistd.h>
unsignd int alarm(unsigned int seconds);
功能:可以为当前进程定义闹钟,时间到了会发出SIGALRM信号
注:每个进程只能有一个alarm,当重新定义时,会重新计时,如果之前定义了一个闹钟,则这次定义返回的是上次闹钟剩余的时间,否则返回0

pause();//程序暂停

pause函数的作用,是让当前进程暂停运行,交出CPU给其他进程去执行,当前进程进入pause状态后,当前进程会表现为”卡住、阻塞住“,要退出pause状态,当前进程需要被信号唤醒。


#信号的三种处理方式

  1. 忽略 2.默认

#include <signal.h>

void (*signal (int signo,void (*func)(int)))(int);

3.自定义信号处理函数

#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
参数:
捕获信号,设置信号的处理方式
handler:SIG_IGN:忽略   SIG_DFL:默认
SIGINT:ctrl+c   SIGQUIT:ctrl+\   SIGTSTP:ctrl+z


##共享内存

进程间通信方式:

传统进程间通信方式: 无名管道 有名管道 信号

system V 的IPC(信号间通信方式)对象 : 共享内存 消息队列 信号灯集

ipcs

ipcs -m:查询显示当前系统的共享内存
ipcs -q:查询显示当前系统的消息队列
ipcs -s:查询显示当前系统的信号灯集
ipcrm -m shmid:删除某个共享内存
ipcrm -q msgid:删除某个消息队列
ipcrm -s semid:删除某个信号灯集

IPC步骤:ftok->shm_get/msg_get/sem_get->shmmat/shmdt/shmctrl/msgctrl/msgsend/msgrecv/semctrl/semop


##共享内存

是一种通信效率最高的进程间通信方式,进程间通信直接访问内存,不需要进行数据的拷贝

#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>

步骤:1.ftok 2.shmget 3.shmat 4.进程间通信fork 5.shmat 6.shmctl

key_t key;
key_t ftok(const char *pathname,int proj_id);
功能:得到key的值
参数:pathname:路径名 proj_id:1-255(子序号)
int shmget(key_t key,size_t size,int shmflg);
功能:创建共享内存
参数:key:key值 或者IPC_PRIVATE 
size:共享内存大小 1024bytes 
shmflg:打开方式:IPC_CREAT|0666
成功返回shmid 失败-1
void *shmat(int shmid,const void *shmaddr,int shmflg);
功能:创建映射区域
参数:shmid:shmid值
shamaddr:NULL(默认:自动映射)
shmflg:SHM_RDONLY:只读  0:读写(重要!)
成功返回内存地址  失败返回(void *)-1
int shmdt(const void *shmaddr);
功能:解除映射
参数:shmaddr:共享内存地址shmat的返回值,即内存地址
成功0 失败-1
int shmctl(int shmid,int cmd,struct_ds *buf);
功能:控制函数
参数:shmid:shmid值  
cmd:IPC_RMIDsh删除共享内存
buf:NULL
成功0 失败-1

##消息队列

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

步骤:1.ftok 2.msgget 3.进程间通信 4.msgsnd 5. msgrcv 6.msgctl

int msgget(key_t key,int msgflg);
功能:创建消息队列
参数:key:key值
msgflg:IPC_CREAT|0666
成功返回msgid值 失败-1
struct msgbuf msg{
    long mtype;//是一个正的长整型量,通过它来区分不同的消息数据类型大于0,过设定mtype 值,可以进行单个消息队列的多向通讯
    char mtext[N];//消息数据的内容
};
struct msgbuf msg;
int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg);
功能:发送数据
参数:msgid:msgid值
msgp:&msg(结构体变量的地址)
msgsz:消息正文的长度 sizeof(msg)-sizeof(long)
msgflg:IPC_NOWAIT 非阻塞    0 阻塞
ssize_t msgrcv(int msgid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
功能:接受数据
参数:msgid:msgid值
msgp:&msg(结构体变量地址)
msgsz:消息正文长度 sizeof(msg)-sizeof(long)
msgtyp:消息类型
msgflg:IPC_NOWAIT 非阻塞  0 阻塞
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
功能:控制函数
参数:msgid:msgid值
cmd:IPC_RMID(删除消息队列)
buf:NULL

##信号灯集

#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>

步骤:1.ftok 2.shmget 3.shmat 4.semget 5.semctl 6.进程间通信 7.semop 8.semctl 9.shmdt 10.shmctl(黑体部分是与共享内存增加部分)

int semget(key_t key,int nsems,int semflg);
功能:创建信号灯集
参数:key:ftok的返回值orIPC_PRIVATE
nsems:设置的信号灯数目
semflg:IPC_CREAT|0666
成功返回semid
union semun{
    int val;//设置初值
    struct semid_ds *buf;//使用缓冲区
    unsigned short *array;//使用的数组
    struct seminfo *_buf;//(linux特有的)使用缓冲区 
}
int semctl(int semid,int semnum,int cmd,...);
功能:信号灯集控制函数
参数:semid:semget的返回值
semnum:要设置的信号灯编号
cmd:IPC_RMID删除信号灯集 SETVAL:设置信号灯
...:共用体的变量or不写
int semop(int semid,struct sembuf *sops,unsigned nsops);
功能:对信号灯进行PV操作(+1 -1)
参数:semid:semget的返回值
sops:结构体变量的地址
struct sembuf{
      unsigned short sem_num;//信号灯的编号
      shrot sem_op;//信号灯操作+1 -1
      shrot sem_flg;//0阻塞  
}
nsops:信号灯个数,默认为1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值