目录: 1.普通的IO函数
_________________________________________其他函数___________________________
宏(包括函数式宏) 没有返回值的概念,因为它只是在源代码文件(级别)上的文本替换(即修改些源代码),没有一个切换并保护现场的过程,所以不需要也不存在一个通过返回值将其的结果传送回来的过程。还有,宏所代表的表达式的计算结果是叫一个结果值,不叫(函数)返回值。例如, #define A(a,b,c) ({a=1;b+=1;c=3;a+b+c;}) #include <stdio.h> int main() { int a; int b=1; int c; int d; d=A(a,b,c); printf("%d,%d,%d,%d\n",a,b,c,d); return 0; }
void *memset(void *s,int c,size_t n) 将已开辟内存空间 s 的首 n 个字节的值设为值 c , 可以清空结构体或数组
void bzero(void *s, int n); 会将参数s 所指的内存区域前n 个字节,全部设为零值。
他们的区别: https://zhidao.baidu.com/question/507145427.html
详细: https://baike.baidu.com/item/setsockopt/10069288?fr=aladdin
int strcmp(const char *s1, const char *s2); 两个字符串比较大小 一样返回0 (s1>s2)? >0 :<0
int strncasecmp(char* s1 ,char* s2 ,size_t n) 比较两个字符串前n个字符的大小, 一样返回0 , (s1>s2) ? >0 : <0
int memcmp(const void *str1, const void *str2, size_t n)); 比较两个内存块前n个字节的大小 一样返回0 (s1>s2)? >0 :<0
char* strerror(int error); 返回error的错误字符串
FILE* stream != int fd
int open(const char * pathname, int flags , mode_t mode)
在Linux一切皆文件open可以打开所有文件 , 可以只传前两个参数
flags: O_RDONLY 只读 ,O_WRONLY 只写 , O_RDWR可读写
O_CREAT 不存在则创建 , O_EXCL 判断文件是否存在,存在返回-1 ,一般和O_CREAT一同出现.
O_APPEND 每次读写都会在文件的末尾进行操作
O_NONBLOCK 无论读写对当前文件都是非阻塞的,默认是阻塞的
mode: 0(无权限) 1(执行权限) 2(写权限) 4(读权限) 0671
特殊权限位 文件拥有者权限位 文件拥有者所在组的权限位 其他用户的权限位
一般是0 2+4 = 6可读写 1+2+4 = 7可读可写可执行 1只有执行权限
特殊权限位: 4 临时获得文件拥有者(UID)的权限,使可执行文件在执行阶段具有UID的权限
2 临时获得文件拥有者组(GID)的权限,使可执行文件在执行阶段具有GID的权限
1 使文件只能被该文件的拥有者(UID)或者root用户删除
前提是该文件存在,且只能临时(open和close之间)的获取拥有者权限,不能更改其权限(文件权限除了创建时设置,和 只能被UID和root更改)
具体的mode描述: http://codemouse.online/archives/2020-03-29224238?tdsourcetag=s_pctim_aiomsg
ssize_t (read/write ) (int fd, void *buf, size_t count); 成功 >0 (返回字节数) =0 (可能对端fd关闭) 失败 <0
详细: https://blog.csdn.net/songchuwang1868/article/details/90665865
FILE *fopen(char *filename, char *mode); windows没有fd的概念, 具体: https://blog.csdn.net/fanyun_01/article/details/96971583
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) ptr字符串或数组 , size每个字符的大小 , nmemb写的字符个数 , stream操作的文件
详细: https://blog.csdn.net/fanyun_01/article/details/96971583
size_t fread(const void *ptr, size_t size, size_t nmemb, FILE *stream)ptr存放数据的buff , size每个字符的大小 , nmemb读的字符个数 , stream操作的文件
详细: https://baike.baidu.com/item/fread/10942353?fr=aladdin
________________________________________________________________________________
fgetc和getc的区别在于实现方法, fgetc是函数的方式实现的,getc以宏定义的方式实现的 , fputc和putc同理
gets(char* str); 从stdin获取一行(遇到EOF或换行符为一行)字符返回给str,无大小限制
puts(char* str); 把一行字符串str(遇到EOF或换行符为一行)输出到stdout里
fgetc/getc(FILE* stream(比如stdin)); 从指定的文件流中读取一个字符并作为函数返回值(以字符的asiic码值)返回给接收者
fputc/putc(int char ,FILE* stream(stdout)); 把一个字符char(以其asiic的值)输出到指定文件流(stdout)里,再以asiic值代表字符打印
fgets(char* str , int n , FILE* stream) 从文件流中获取一行字符串(遇到换行符,EOF,或达到指定大小n),返回在str , fgets会默认在str结尾加\n
fputs(char* str , FILE* stream) 把一行字符串输出到指定文件流里
_______________________________________如何获得一个文件的大小________________________
int fseek(FILE *stream, long int offset, int whence) 设置指定文件流(stream)里指针的偏移值
whence: 指针偏移的起始位置 SEEK_SET(流的开头) SEEK_CUR(流的当前位置) SEEK_END(流的末尾)
offset: 从whence指定好的偏移位置 开始 ,偏移到指定的偏移量 如,从当前位置偏移 offset = 3 个字符
成功返回0, 失败返回非0值 ,可用perror()打印error值
int ftell(FILE *stream) 返回指定文件流当前的位置距离文件流首位的偏移量
void rewind(FILE *stream) 设置文件流里已经偏移的指针,重新指向文件流的开头
作用 : 获取文件流的实际大小, 可以使用fseek指向末尾,再利用ftell获取开头到末尾的偏移量 , 再用rewind重新指向开头
int socket(int af, int type, int protocol)
socket 返回值: 成功 >0 (返回文件描述符的序号) , 失败 -1 ( 创建失败 INVALID_SOCKET也是-1 )
int bind(int sockfd ,const struct sockaddr* my_addr, socklen_t addrlen)
bind 返回值: 成功 0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)
int listen( int sockfd, int backlog);
listen 返回值: 成功0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)
SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept 返回值: 成功>0(返回文件描述符的序号一般从3开始) , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)
int connect(SOCKET s, const struct sockaddr * name, int namelen);
connect返回值: 成功 0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)
int recv(SOCKET s, charFAR*buf, intlen, intflags);
int recvfrom (int sockfd,void *buf,int buf_len,unsigned int flags,struct sockaddr *from,int *fromlen) flags一般是0
recv/recvfrom():返回值 成功 >0 (返回字符串的len) , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)
int send( SOCKET s, const char FAR *buf, int len, int flags );
int sendto ( int sockfd , const void * msg, int msg_len, unsigned int flags, const struct sockaddr * to , int tolen ) flags一般是0
send/sendto():返回值 成功 >0 (返回字符串的len) , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)
htons(uint16_t hostshort): 将主机无符号短整型(uint16_t)转换为网络字节序(大端(0x12 0x34)转小端(0x34 0x12) ),返回转换好的网络字节序
ntohs(uint16_t netshort): 与htons反之, 网络转主机 ,返回转好的主机字节序
htonl(uint32_t hostlong): 将主机无符号长整型(uint32_t)转换为网络字节序(大端(0x12 0x34)转小端(0x34 0x12) ),返回转换好的网络字节序
ntohl(uint32_t netlong): 与htonl反之, 网络转主机 ,返回转好的主机字节序
inet_addr(const char* cp): 将主机点分十进制的ip,转换成无符号长整型(u_long)的二进制的网络字节序
int atoi(const char* nptr): 将字符串转换成整型数 , 返回整型数
___________________________________设置fd是否阻塞_________________________
int fcntl(int fd, int cmd); cmd: F_GETFL , F_SETFL , F_GETFD , F_SETFD
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
https://baike.baidu.com/item/fcntl/6860021?fr=aladdin
FD_ZERO(fd_set *rfds) 初始化struct fd_set frds(fd集合)
成功 0 ,失败-1
FD_SET(fd,fd_set *rfds) 初始化后把要监听的fd加入到&frds
成功 0 ,失败-1
监听rfds里fd状态 (读或写或异常状态都会改变),指定时间内 , 以轮循的方式判断每一个fd的状态是否改变, 如果全部无变化select返回0(rfds的里的fd也不做变化) ,如果有N个fd状态改变则保留这些状态改变的fd , 其他无变化的则清除出frds ,并返回留下的数量 (因为select会改变rfds里的fd , 所以每次都要重载rfds)
返回值: 成功>0(返回就绪的fd数量) , =0超时(没有发生读写) ,-1错误
FD_ISSET(fd,fd_set *rfds) 判断指定fd是否存在frds里,如果存在说明fd状态发生改变,则可以处理当前指定的fd , 不存在说明没有变化,不做处理
存在返回>0 , 不存在返回0 , 失败 -1
小知识:在比较多fd的情况下可以把fd都加入到一个fd数组里 , 以循环(for)的方式吧数组内的fd加入到rfds里,避免一行行的FD_SET(fd)
_____________________________________epoll_______________________________
int epoll_create(int size); 设置要监听的事件最大数量
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
op: EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
event: EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //在指定时间内监听的fd是否有每个fd指定的事件发生 ,max最大监听数
int kill(pid_t pid,int sig); 成功返回0,失败返回-1 给指定进程pid发送指定信号signo
int raise(int sig); 成功返回0,失败返回-1 给当前进程发送指定信号
sig_t signal(int signum,sig_t handler); 给指定信号绑定指定执行函数 , sigkill , sigstop不能被更改
unsigned int alarm(unsigned int seconds); 指定一个时间,程序的执行时间到达指定时间后给当前进程发送 SIGALRM , alarm会覆盖前一个alarm
程序开始执行时有一个jiffies会从0累加时间直到程序结束, 而alarm的指定时间是从jiffies的0开始计算的,
useconds_t ualarm(useconds_t usecs , useconds_t interval); 程序在触发第一个 SIGALRM 后每间隔一个时间段(interval)执行一次 SIGALRM
int setitimer(int which, const struct itimerval new_value, struct itimerval *old_value) ,创建比alarm()更精确的定时器 old_value旧的定时器时间可以NULL
int getitimer(int which, struct itimerval *curr_value); ,curr_value 设置指定开始时间和间隔时间
which: ITIMER_REAL 以系统真实的时间来计算,它送出SIGALRM信号 ,三种定时器一般使用第一个就可以了
ITIMER_VIRTUAL 以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号
ITIMER_PROF 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号
____________________比signal更多功能局限也比signal大,可以对执行函数进行传参_____
int sigaction(int signum , const struct sigaction *act , struct sigaction *oldact); 绑定信号函数,和设置信号屏蔽集, 也可以保存信号之前的执行函数方便恢复
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
小知识: sigaction的第三参数可以作为 sa_sigaction的第三参数的传参,比如一个指针
int sigqueue(pid_t pid, int sig, const union sigval val); 给指定pid发送指定sig并且可以带数据sigval 的发送信号
typedef union sigval { //可以存放4字节的数据 指针只能在同一进程传递跨进程则无效
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue发送的数据可以通过信号函数的 sigaction_t* info.si_int 或 info->si_value.sival_int 获取
_________________对信号屏蔽集(sa_mask)的操作函数_______________
int sigemptyset(sigset_t set); 清空信号集合 成功返回0 失败则返回-1
int sigfillset(sigset_t set); 填满信号集合(把所有信号都放进去) 成功返回0 失败则返回-1
int sigaddset(sigset_t set, int signum); 往set集合里追加signum 成功返回0 失败则返回-1
int sigdelset(sigset_t set, int signum); 把signum从set中删除 成功返回0 失败则返回-1
int sigismember(const sigset_t *set, int signum); 测试set里是否有signum 有则返回1 无则返回0 失败则返回-1
int sigprocmask(int how, const sigset_t newset, sigset_t *oldset); 第三个参数作备份用的
how = SIG_BLOCK 将设置好的信号屏蔽集newset加入到当前进程的屏蔽集里
SIG_UNBLOCK 将设置好的信号屏蔽集newset从当前进程的屏蔽集里删除
SIG_SETMASK 将设置好的信号屏蔽集newset设置为当前进程的屏蔽集
sigsuspend(sigmask) 信号阻塞, 在收到指定信号集里的信号前 程序会阻塞,直到收到信号为止
sigpendirp(sigset) 查询有多少未决信号
struct aiocb {
int aio_fildes; /* 文件描述符 */
off_t aio_offset; /* 文件偏移 */
volatile void *aio_buf; /* 缓冲区地址 */
size_t aio_nbytes; /* 传输的数据长度 */
int aio_reqprio; /* 请求优先级 */
struct sigevent aio_sigevent; /* 通知方法 */
int aio_lio_opcode; /* 仅被 lio_listio() 函数使用 AIO_READ , AIO_WRITE*/
};
int aio_read(struct aiocb *aiocbp) 对aiocb对象发起读操作 , 不会阻塞进程,内核会继续完成操作
int aio_write(struct aiocb *aiocbp) 对aiocb对象发起写操作, 不会阻塞进程,内核会继续完成操作
int aio_error( struct aiocb *aiocbp ); 主动发起询问aio_read()和aio_write()的返回状态的请求
返回值 EINPROGRESS,说明请求尚未完成
ECANCELLED,说明请求被应用程序取消了
-1 返回错误:errno , 0 说明完成当前请求
ssize_t aio_return( struct aiocb *aiocbp ); 接收读写操作的返回状态, 要留足够时间等待操作的完成才接收,不然会因为异步(操作未完成)而接收到完成前返回状态
int aio_suspend(const struct aiocb *const cblist[], int n, const struct timespec *timeout); 阻塞函数,直到指定的aiocb异步操作完成或超时才会返回
struct timespec { timeout给NULL就可以了
time_t tv_sec; // seconds
long tv_nsec; // and nanoseconds
};
第一个参数是 把要操作的 aiocb对象加入到 cblist[] 这个集合里
第二个参数为要操作的 aiocb 对象的个数, 第三个参数为等待阻塞的超时时间,NULL为无限等待
有三种返回值 1.超时了 , 2.出错了error , 3.aio全部都返回了
int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig); 处理批量的 aiocb 对象
mode: LIO_WAIT 阻塞发起 阻塞等到所有发起的AIO全部完成后,才会返回
LIO_NOWAIT 非阻塞发起 发起后立即返回,通过绑定的信号来通知
struct sigevent { int sigev_notify; //应用线程的通知类型 一般使用默认选项 SIGEV_THREAD 创建一个线程 int sigev_signo; //应用线程的信号 union sigval sigev_value; //应用线程的值 void (*sigev_notify_function)(union sigval); //应用线程的回调函数 pthread_attr_t *sigev_notify_attributes; //应用线程的属性 一般使用默认的 NULL
};
lio_listio() LIO_WAIT 阻塞模式 , 给aiocb->aio_opcode指定类型(AIO_READ或AIO_WRITE), lio_listio()会根据指定类型做出对 aiocb->aio_filedes 指向的文件句柄的相应操作(不用手动的aio_read了只管aio_return()后打印数据了), 且完成操作函数才会返回,否则函数会一直阻塞
lio_listio() LIO_NOWAIT 非阻塞模式 , 无需给aiocb指定类型 , 但是需要用到 sigevent ,给aiocb绑定回调函数, 当主进程发起aio_read或者aio_write时,会触发回调函数,但主进程不阻塞, 要留足够的时间给 非阻塞模式完成相应的操作 比如sleep 1秒就足矣, 否则得到的数据不匹配
POSIX文件操作:
在移动开发中,难免要做一些跨平台的事情。iOS和Android各执一套,平台很难跨。但其底层Linux和Unix则早早做了一套不错的兼容标准。这套标准就是POSIX , 其实就是 open 设置文件权限 read write lseek(文件数据的偏移值) 等这些可以对所有文件都可以操作的函数
详细: https://www.jianshu.com/p/b86298645e08
目录管理:___________________________________________________________________
int stat(const char *file_name, struct stat *buf); 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
struct stat { dev_t st_dev; /* 设备号码 特殊的文件*/ ino_t st_ino; /* inode节点号 常规的文件*/ mode_t st_mode; /* 文件对应的模式 , 文件 , 目录等 */ nlink_t st_nlink; /* 文件的连接数*/ uid_t st_uid; /* 文件的所有者 */ gid_t st_gid; /* 文件所有者对应的组 */ dev_t st_rdev; /* 特殊设备号码 */ off_t st_size; /* 普通文件 , 对应文件字节数 */ blksize_t st_blksize; /* 文件内容对应的块大小 , 比如磁盘的读写单位都是按块计算的*/ blkcnt_t st_blocks; /* 文件内容对应块的数量 */ time_t st_atime; /* 文件最后被访问的时间 */ time_t st_mtime; /* 文件最后被修改的时间 */ time_t st_ctime; /* 文件状态改变的时间 */ };
01.S_IFMT 0170000 文件类型的位遮罩 02.S_IFSOCK 0140000 socket 03.S_IFLNK 0120000 符号链接(symbolic link) 04.S_IFREG 0100000 一般文件 05.S_IFBLK 0060000 区块装置(block device) 06.S_IFDIR 0040000 目录 07.S_IFCHR 0020000 字符装置(character device) 08.S_IFIFO 0010000 先进先出(fifo) 09.S_ISUID 0004000 文件的(set user-id on execution)位 10.S_ISGID 0002000 文件的(set group-id on execution)位 11.S_ISVTX 0001000 文件的sticky位 12.S_IRWXU 00700 文件所有者的遮罩值(即所有权限值) 13.S_IRUSR 00400 文件所有者具可读取权限 14.S_IWUSR 00200 文件所有者具可写入权限 15.S_IXUSR 00100 文件所有者具可执行权限 16.S_IRWXG 00070 用户组的遮罩值(即所有权限值) 17.S_IRGRP 00040 用户组具可读取权限 18.S_IWGRP 00020 用户组具可写入权限 19.S_IXGRP 00010 用户组具可执行权限 20.S_IRWXO 00007 其他用户的遮罩值(即所有权限值) 21.S_IROTH 00004 其他用户具可读取权限 22.S_IWOTH 00002 其他用户具可写入权限 23.S_IXOTH 00001 其他用户具可执行权限
在Linux一切皆文件, 而区别这些文件的类型则保存在 mode_t ,目前,st_mode使用了其低19bit. 0170000 => 1+ 3*5 = 16.其中,最低的9位(0-8)是权限,9-11是id,12-15是类型。
S_ISLNK(st_mode) :是否是一个连接. 是为1 否为0
S_ISREG(st_mode) :是否是一个常规文件.
S_ISDIR(st_mode) :是否是一个目录
S_ISCHR(st_mode) :是否是一个字符设备.
S_ISBLK(st_mode) :是否是一个块设备
S_ISFIFO(st_mode) :是否 是一个FIFO文件.
S_ISSOCK(st_mode) :是否是一个SOCKET文件
DIR * opendir(const char * name); 打开目录
struct dirent * readdir(DIR * dir); 读取目录信息
struct dirent { ino_t d_ino; ff_t d_off; signed short int d_reclen; unsigned char d_type; har d_name[256]; };
int closedir(DIR * dir); 关闭目录
详细 : https://www.iteye.com/blog/lobert-1706165
int mkdir(const char *pathname, mode_t mode); 以mode方式创建一个以参数pathname命名的目录,mode定义新创建目录的权限。和open的mode一样
什么是原始套接字: https://www.linuxidc.com/Linux/2015-04/115683.htm
int socket(AF_INET, SOCK_RAW, int protocol); SOCK_RAW原始套接字要在root下运行, 但可以临时提升到文件拥有者权限 , 比如ping
原始套接字最重要的在于对 协议的了解 并基于这些协议自行构建数据包, 比如 ICMP协议
僵死进程: 一个进程使用fork创建父子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。(子进程退出后子进程的资源得不到释放,这些称为僵死进程,由父进程用wait确认子进程状态,并释放资源)
孤儿进程: 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。(init进程: https://baike.baidu.com/item/init%E8%BF%9B%E7%A8%8B/3042859?fr=aladdin)
守护进程: fork()一个进程 父子进程有一个关联那就是在同一个会话组里, 而结束父进程后,子进程将setsid()一个新的会话组并作为该组的组长, 子进程将摆脱了和父进程的关联, 并在后台运行 这样的进程称为守护进程
进程状态: https://www.cnblogs.com/diegodu/p/9167671.html
_______________________________________________
pid_t fork(void); 创建父进程和子进程 , 父进程保存子进程的pid 子进程0 ,父子进程会复制主进程资源两份,各自一份,互不影响
int wait(int* statloc); 阻塞父进程直到其所有子进程全部退出 ,由父进程确认子进程状态,如果是退出状态则释放子进程的资源,并将状态返回给*statloc
int waitpid(pid_t pid, int* statloc, int options); 阻塞当前进程兵等待目标pid结束进程, 可以由 options决定是否阻塞
wait和waitpid的区别: https://blog.csdn.net/csdn_kou/article/details/81091191
pid_t getpid(void); 获得当前进程的 id
pid_t getppid(void); 获得父进程的 id
pid_t getsid(pid_t pid) 获取当前会话的ID
setsid() 将调用进程设置成新的会话组组长, 直接调用不需要返回值和参数
setuid() 将当前进程设置为文件拥有者权限
int socketpair(int d, int type, int protocol, int sv[2]); 建立一对匿名的已经连接的套接字,可通过fd句柄进行亲缘关系进程间的通信,是双向的
int pipe(int *pipe_fd); 创建无名管道文件, 通过fd的两端进行传输数据 单向且只能是亲缘关系进程, 主进程关闭也会消失, 简单,传输快
int mkfifo(const char* pathname, mode_t mode); 创建有名管道文件 , 可以进行非亲缘关系的进程通信, 文件不会随着进程的消失而消失,会存在于文件系统中, 可多次使用 ,仅限于两个进程一对一, 和普通文件一样的用法,open后往文件fd read/write数据(文件不具备存储性,只有流通性), 传输慢,量小 ,open fifo文件的时候进程会阻塞,直到有另外一个进程同时open同一个fifo文件 ,在共享文件夹里无法创建fifo
exec函数族: https://blog.csdn.net/zhengqijun_/article/details/52852074
execl(const char *path, const char *arg, ...) 传的参是文件路径 第二个开始的参数数量是可变的,arg0 ,arg1 ... 一般都使用execl
execv(const char *path, char * const argv[]);传的参是文件路径 ,第二个参数是一个指针数组不用同上一样一个个的手动传参
execlp(const char *file, const char *arg, ...)传的参是文件名(系统会从$PATH里找到目录) l使用路径 p使用文件名根据环境变量PATH
execve(const char *path, char * const argv[], char* const envp[](指定环境变量))是真正意义上的系统调用,其他的都是在此基础上封装的库函数
https://blog.csdn.net/David_361/article/details/86057551
int access(const char *filenpath, int mode); 或者int _access( const char *path, int mode ); 确定文件的权限,存在指定权限返回0, 否 -1
______________________XSI消息队列:_______________________________
和管道的区别, msgsnd的data会一直存在于队列里, 每次打开消息队列都可以 msgrcv 消息队列里的data , 且消息队列创建后会一直存在
struct msqid_ds { 在内核中每个消息队列都是以msqid_ds这样的结构体进行管理的, 可通过msgrcv等这样的操作函数对当前结构体进行读取或写入数据,和获取当前消息的队列的一些信息属性 struct ipc_perm msg_perm; /* Ownership and permissions */ messagequeue的权限 三个time主要用来监控消息队列什么时候有数据什么时候发送数据什么时候接收数据 time_t msg_stime; /* Time of last msgsnd(2) */ 最后一次的发送msg时间 time_t msg_rtime; /* Time of last msgrcv(2) */ 最后一次的接收msg时间 time_t msg_ctime; /* Time of last change */ 最后一次的msg改变的时间 unsigned long __msg_cbytes; /* Current number of bytes in queue的数目 具体有多少个msqid_ds结构体 queue (nonstandard) */ msgqnum_t msg_qnum; /* Current number of messages 消息的数目 代表msqid_ds里有多少个msg in queue */ msglen_t msg_qbytes; /* Maximum number of bytes 队列中允许的最大字节数 allowed in queue */ pid_t msg_lspid; /* PID of last msgsnd(2) */ 最后一个发送进程 pid_t msg_lrpid; /* PID of last msgrcv(2) */ 最后一个接收进程 struct msg * msq_first ; 消息队列里指向第一个消息 struct msg * msq_last ; 指向消息队列里最后一个消息, 可通过msgctl对消息队列进行增删改查 }; struct ipc_perm //拥有者及权限的对象 { __kernel_key_t key; __kernel_uid_t uid; //用户\创建者 __kernel_gid_t gid; //用户组 __kernel_uid_t cuid; //当前有效ID __kernel_gid_t cgid; //当前有效组ID __kernel_mode_t mode; nsigned short seq; };
key_t ftok(const char *pathname, int proj_id); 根据目录返回一个key值
int msgget(key_t key ,int megflag) 基于key值创建一个消息队列并返回一个int值作为句柄
flag : #define IPC_CREAT 00001000 /* 创建 */
#define IPC_EXCL 00002000 /* 如果key存在则失败 */
#define IPC_NOWAIT 00004000 /* return error on wait */
int msgctl(int msgqid, int cmd, struct msqid_ds *buf); 消息队列控制函数
cmd: IPC_STAT :读取消息队列的 msqid_ds 属性,保存在msqid_ds *buf里
IPC_SET : 设置消息队列属性 ,给msgqid指定 msgqidmsqid_ds *buf信息
IPC_RMID :删除消息队列
IPC_INFO :Linux特有命令,获取并保存 msginfo结构体里的全部信息,保存在buf中,
IPC_INFO (Linux-specific)
Returns information about system-wide message queue limits and parameters in the structure pointed to by buf. This structure is of type msginfo (thus, a cast is required), defined in <sys/msg.h> if the _GNU_SOURCE feature test macro is defined:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgp:用户定义的缓冲区(可以自定义一个struct{type,data};) , msgsz:消息内容长度 ,
msgflg: , 0:消息队列满时msgsnd阻塞 , IPC_NOWAIT:队列满时msgsnd不阻塞立刻返回
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgtyp: 0 接收第一条消息 , >0接收类型等于msgtyp的第一个消息 , <0接收类型等于或者小于msgtyp绝对值的第一个消息
消息队列多次调试时要确保消息队列是否存在,如果不存在再创建如果存在则继续使用已经存在的消息队列 , 因为消息队列一经创建是会一直存在下去的, 且msgsnd的消息也会一直存在于消息队列里 , 就算关闭发送端,也是可以多次msgrcv的
有些判断是需要根据返回值健全的处理方式,而不是置之不理,
___________________________XSI信号量_______________________________________
信号量的概念: https://blog.csdn.net/Code_ZX/article/details/85008036
信号量是解决进程间的同步于互斥的问题 , 信号量代表了进程可消耗的资源个数,比如信号量的初始值是0, semop+1 后其他进程可以根据值进行处理(同步这也是生产者和消费者的概念, 先有可消耗资源,再消耗资源,至于要消耗多少具体就看有多少可消耗的)
struct semid_ds { 在内核中每个信号量都是这样的结构体进行管理, 可通过semop等这些操作函数,对当前结构体的数据进行更改或获取, 具体可以在gdb的时候 p (struct semid_ds) sem_id 进行查看 struct ipc_perm sem_perm; /* Ownership and permissions */ time_t sem_otime; /* Last semop time */ time_t sem_ctime; /* Last change time */ unsigned long sem_nsems; /* No. of semaphores in set */ //信号量的个数 struct sem * sem_base //信号量链表的头指针 }; struct sem{ int semval 信号量的值 ,代表当前信号可消耗资源的个数 ,如果是1那么当前信号可以用1次 ,如果为0当前信号不能进行消耗操作 int sempid 最近一个操作的进程号 }
union semun { //在 Linux内核2.6 该共用体已被注释需要手动定义共用体 int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short* array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ };
int semget(key_t key,int n,int semflag); 创建一个sem
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效, 其他和msgget()一样
int semctl(int semid,int semnum,int cmd, /*union semun arg*/); //可以对 (struct semid_ds)semid这个对象进行操作
semnum = 0 代表第一个信号量,以此类推, arg是获取或设置信号量的值 , 可以不传参
int semop(int semid,struct sembuf *sops,size_t nsops) //修改信号量的值
sops: 设置从初始值开始要加或者减的值 , nsops: 信号操作结构的数量,恒大于或等于1。
PV原子操作(不可中断或一系列的操作): P:有可用资源 V:生产资源
_________________________________________XSI共享内存_________________________________
struct shmid_ds { 共享内存在内核的对象 struct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ __kernel_time_t shm_atime; /* last attach time */ __kernel_time_t shm_dtime; /* last detach time */ __kernel_time_t shm_ctime; /* last change time */ __kernel_ipc_pid_t shm_cpid; /* pid of creator 创建者*/ __kernel_ipc_pid_t shm_lpid; /* pid of last operator 最后一次操作的pid*/ unsigned short shm_nattch; /* no. of current attaches */ };
不属于任何一个进程,是内核创建的一块共享的内存 ,生命周期和创建的进程相同, 共享内存是内核的资源会以映射的方式让进程访问, 不会使用到进程的资源(要做好互斥), 访问操作的内存是在内核里,而非进程的
int shmget(key_t key, size_t size, int shmflg); 基于key创建共享内存
size一般就几K共享内存不会太大 shmflg: IPC_CREAT | IPC_EXCL | 0666
int shmctl(int shmid, int cmd, struct shmid_ds *buf); 操作(设置,获取,删除)共享内存
cmd: IPC_STAT 获取 shmid 的 shmid_ds ,保存在buf里
IPC_SET 把buf 设置成shmid 的 shmid_ds
IPC_RMID 删除shmid和他的shmid_ds
SHM_LOCK 给共享内存上锁 这并不代表一个进程锁定住共享内存以后其它进知程禁止访问。锁定后只是不会交换到文件系统里(虚拟内存,swap分区)道,而不是指它的访问性, 如果要限制访问性,可以使用信号量
SHM_UNLOCK 解锁
void *shmat(int shmid, const void *shmaddr, int shmflg); 开始访问共享内存
shmaddr: 指定共享内存映射到进程虚拟内存的首地址, 通常为NULL 让内核分配地址
shmflg: SHM_RDONLY 以只读的方式其他为读写方式 一般是 0
SHM_REMAP 以重映射的方式
SHM_EXEC 以可执行方式
int shmdt(const void *shmaddr); 结束使用共享内存detach